Browse Source

[master]Merge branch 'master' of ssh://git.bind10.isc.org//var/bind10/git/bind10

Jeremy C. Reed 13 years ago
parent
commit
a7dbb75a88
53 changed files with 1259 additions and 406 deletions
  1. 5 0
      ChangeLog
  2. 0 1
      configure.ac
  3. 5 19
      doc/guide/bind10-guide.xml
  4. 0 16
      src/bin/bind10/bind10.xml
  5. 0 4
      src/bin/bind10/bob.spec
  6. 6 0
      src/bin/xfrin/xfrin.py.in
  7. 3 2
      src/bin/xfrin/xfrin_messages.mes
  8. 7 0
      src/bin/xfrout/xfrout.py.in
  9. 4 0
      src/bin/xfrout/xfrout_messages.mes
  10. 6 0
      src/bin/zonemgr/zonemgr.py.in
  11. 4 0
      src/bin/zonemgr/zonemgr_messages.mes
  12. 1 1
      src/lib/cc/cc_messages.mes
  13. 1 1
      src/lib/cc/session.cc
  14. 137 9
      src/lib/datasrc/database.cc
  15. 7 2
      src/lib/datasrc/database.h
  16. 29 0
      src/lib/datasrc/datasrc_messages.mes
  17. 1 0
      src/lib/datasrc/tests/Makefile.am
  18. 67 3
      src/lib/datasrc/tests/database_unittest.cc
  19. 196 0
      src/lib/datasrc/tests/faked_nsec3.cc
  20. 86 0
      src/lib/datasrc/tests/faked_nsec3.h
  21. 4 177
      src/lib/datasrc/tests/memory_datasrc_unittest.cc
  22. 1 0
      tests/lettuce/configurations/.gitignore
  23. 34 0
      tests/lettuce/configurations/bindctl_commands.config.orig
  24. 16 0
      tests/lettuce/configurations/default.config
  25. 7 1
      tests/lettuce/configurations/example.org.config.orig
  26. 8 1
      tests/lettuce/configurations/example.org.inmem.config
  27. 7 1
      tests/lettuce/configurations/example2.org.config
  28. 11 1
      tests/lettuce/configurations/ixfr-out/testset1-config.db
  29. 1 1
      tests/lettuce/configurations/multi_instance/multi_auth.config.orig
  30. 14 0
      tests/lettuce/configurations/no_db_file.config
  31. 1 1
      tests/lettuce/configurations/nsec3/nsec3_auth.config
  32. 1 1
      tests/lettuce/configurations/resolver/resolver_basic.config.orig
  33. 9 1
      tests/lettuce/configurations/xfrin/retransfer_master.conf
  34. 9 1
      tests/lettuce/configurations/xfrin/retransfer_slave.conf
  35. 1 0
      tests/lettuce/data/.gitignore
  36. 0 0
      tests/lettuce/data/ixfr-out/zones.sqlite3
  37. 30 3
      tests/lettuce/features/bindctl_commands.feature
  38. 22 0
      tests/lettuce/features/default.feature
  39. 54 6
      tests/lettuce/features/example.feature
  40. 16 2
      tests/lettuce/features/ixfr_out_bind10.feature
  41. 18 1
      tests/lettuce/features/multi_instance.feature
  42. 264 96
      tests/lettuce/features/nsec3_auth.feature
  43. 42 18
      tests/lettuce/features/queries.feature
  44. 11 1
      tests/lettuce/features/resolver_basic.feature
  45. 20 9
      tests/lettuce/features/terrain/bind10_control.py
  46. 12 6
      tests/lettuce/features/terrain/steps.py
  47. 26 12
      tests/lettuce/features/terrain/terrain.py
  48. 12 0
      tests/lettuce/features/xfrin_bind10.feature
  49. 1 1
      tests/lettuce/setup_intree_bind10.sh.in
  50. 17 5
      tests/system/bindctl/tests.sh
  51. 10 0
      tests/system/glue/nsx1/b10-config.db.in
  52. 10 0
      tests/system/ixfr/b10-config.db.in
  53. 5 2
      tests/system/start.pl

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+425.    [func]*          muks
+	Don't autostart b10-auth, b10-xfrin, b10-xfrout and b10-zonemgr in
+	the default configuration.
+	(Trac #1818, git 31de885ba0409f54d9a1615eff5a4b03ed420393)
+
 424.	[bug]		jelte
 	Fixed a bug in bindctl where in some cases, configuration settings
 	in a named set could disappear, if a child element is modified.

+ 0 - 1
configure.ac

@@ -1201,7 +1201,6 @@ AC_OUTPUT([doc/version.ent
            tests/system/ixfr/in-3/setup.sh
            tests/system/ixfr/in-4/setup.sh
           ], [
-           chmod +x compatcheck/sqlite3-difftbl-check.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

+ 5 - 19
doc/guide/bind10-guide.xml

@@ -771,12 +771,8 @@ as a dependency earlier -->
       master process will also start up
       <command>b10-cmdctl</command> for administration tools to
       communicate with the system,
-      <command>b10-auth</command> for authoritative DNS service,
-      <command>b10-stats</command> for statistics collection,
-      <command>b10-stats-httpd</command> for statistics reporting,
-      <command>b10-xfrin</command> for inbound DNS zone transfers,
-      <command>b10-xfrout</command> for outbound DNS zone transfers,
-      and <command>b10-zonemgr</command> for secondary service.
+      <command>b10-stats</command> for statistics collection, and
+      <command>b10-stats-httpd</command> for statistics reporting.
     </para>
 
     <section id="start">
@@ -810,12 +806,7 @@ as a dependency earlier -->
         The configuration is in the Boss/components section. Each element
         represents one component, which is an abstraction of a process
         (currently there's also one component which doesn't represent
-        a process). If you didn't want to transfer out at all (your server
-        is a slave only), you would just remove the corresponding component
-        from the set, like this and the process would be stopped immediately
-        (and not started on the next startup):
-      <screen>&gt; <userinput>config remove Boss/components b10-xfrout</userinput>
-&gt; <userinput>config commit</userinput></screen>
+        a process).
       </para>
 
       <para>
@@ -1861,15 +1852,10 @@ what is XfroutClient xfr_client??
     <para>
       The main <command>bind10</command> process can be configured
       to select to run either the authoritative or resolver or both.
-      By default, it starts the authoritative service.
-<!-- TODO: later both -->
-
-      You may change this using <command>bindctl</command>, for example:
+      By default, it doesn't start either one. You may change this using
+      <command>bindctl</command>, for example:
 
       <screen>
-&gt; <userinput>config remove Boss/components b10-xfrout</userinput>
-&gt; <userinput>config remove Boss/components b10-xfrin</userinput>
-&gt; <userinput>config remove Boss/components b10-auth</userinput>
 &gt; <userinput>config add Boss/components b10-resolver</userinput>
 &gt; <userinput>config set Boss/components/b10-resolver/special resolver</userinput>
 &gt; <userinput>config set Boss/components/b10-resolver/kind needed</userinput>

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

@@ -244,10 +244,6 @@ TODO: configuration section
     <itemizedlist>
 
       <listitem>
-        <para> <varname>/Boss/components/b10-auth</varname> </para>
-      </listitem>
-
-      <listitem>
         <para> <varname>/Boss/components/b10-cmdctl</varname> </para>
       </listitem>
 
@@ -259,18 +255,6 @@ TODO: configuration section
         <para> <varname>/Boss/components/b10-stats-httpd</varname> </para>
       </listitem>
 
-      <listitem>
-        <para> <varname>/Boss/components/b10-xfrin</varname> </para>
-      </listitem>
-
-      <listitem>
-        <para> <varname>/Boss/components/b10-xfrout</varname> </para>
-      </listitem>
-
-      <listitem>
-        <para> <varname>/Boss/components/b10-zonemgr</varname> </para>
-      </listitem>
-
     </itemizedlist>
 
     <para>

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

@@ -8,10 +8,6 @@
         "item_type": "named_set",
         "item_optional": false,
         "item_default": {
-          "b10-auth": { "special": "auth", "kind": "needed" },
-          "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
-          "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
-          "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
           "b10-stats": { "address": "Stats", "kind": "dispensable" },
           "b10-stats-httpd": {
             "address": "StatsHttpd",

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

@@ -38,6 +38,11 @@ from isc.log_messages.xfrin_messages import *
 isc.log.init("b10-xfrin")
 logger = isc.log.Logger("xfrin")
 
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
+DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
+
 try:
     from pydnspp import *
 except ImportError as e:
@@ -1576,6 +1581,7 @@ class Xfrin:
                 logger.error(XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER, ZONE_MANAGER_MODULE_NAME)
 
     def startup(self):
+        logger.debug(DBG_PROCESS, XFRIN_STARTED)
         while not self._shutdown_event.is_set():
             self._cc_check_command()
 

+ 3 - 2
src/bin/xfrin/xfrin_messages.mes

@@ -129,8 +129,9 @@ zone is not known to the system. This may indicate that the configuration
 for xfrin is incomplete, or there was a typographical error in the
 zone name in the configuration.
 
-% XFRIN_STARTING starting resolver with command line '%1'
-An informational message, this is output when the resolver starts up.
+% XFRIN_STARTED xfrin started
+This informational message is output by xfrin when all initialization
+has been completed and it is entering its main loop.
 
 % XFRIN_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the xfrin daemon. The

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

@@ -40,6 +40,12 @@ from isc.log_messages.xfrout_messages import *
 
 isc.log.init("b10-xfrout")
 logger = isc.log.Logger("xfrout")
+
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
+DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
+
 DBG_XFROUT_TRACE = logger.DBGLVL_TRACE_BASIC
 
 try:
@@ -1002,6 +1008,7 @@ class XfroutServer:
 
     def run(self):
         '''Get and process all commands sent from cfgmgr or other modules. '''
+        logger.debug(DBG_PROCESS, XFROUT_STARTED)
         while not self._shutdown_event.is_set():
             self._cc.check_command(False)
 

+ 4 - 0
src/bin/xfrout/xfrout_messages.mes

@@ -133,6 +133,10 @@ be a result of rare local error such as memory allocation failure and
 shouldn't happen under normal conditions. The error is included in the
 log message.
 
+% XFROUT_STARTED xfrout started
+This informational message is output by xfrout when all initialization
+has been completed and it is entering its main loop.
+
 % XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the xfrout daemon. The
 daemon will now shut down.

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

@@ -44,6 +44,11 @@ from isc.log_messages.zonemgr_messages import *
 isc.log.init("b10-zonemgr")
 logger = isc.log.Logger("zonemgr")
 
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
+DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
+
 # Constants for debug levels.
 DBG_START_SHUT = logger.DBGLVL_START_SHUT
 DBG_ZONEMGR_COMMAND = logger.DBGLVL_COMMAND
@@ -657,6 +662,7 @@ class Zonemgr:
         return answer
 
     def run(self):
+        logger.debug(DBG_PROCESS, ZONEMGR_STARTED)
         self.running = True
         try:
             while not self._shutdown_event.is_set():

+ 4 - 0
src/bin/zonemgr/zonemgr_messages.mes

@@ -67,6 +67,10 @@ zone manager to record the master server for the zone and start a timer;
 when the timer expires, the master will be polled to see if it contains
 new data.
 
+% ZONEMGR_STARTED zonemgr started
+This informational message is output by zonemgr when all initialization
+has been completed and it is entering its main loop.
+
 % ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command
 This is a debug message indicating that the zone manager has received
 a SHUTDOWN command over the command channel from the Boss process.

+ 1 - 1
src/lib/cc/cc_messages.mes

@@ -14,7 +14,7 @@
 
 $NAMESPACE isc::cc
 
-% CC_ASYNC_READ_FAILED asynchronous read failed
+% CC_ASYNC_READ_FAILED asynchronous read failed (error code = %1)
 This marks a low level error, we tried to read data from the message queue
 daemon asynchronously, but the ASIO library returned an error.
 

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

@@ -249,7 +249,7 @@ SessionImpl::internalRead(const asio::error_code& error,
         }
         user_handler_();
     } else {
-        LOG_ERROR(logger, CC_ASYNC_READ_FAILED);
+        LOG_ERROR(logger, CC_ASYNC_READ_FAILED).arg(error.value());
         isc_throw(SessionError, "asynchronous read failed");
     }
 }

+ 137 - 9
src/lib/datasrc/database.cc

@@ -27,15 +27,18 @@
 #include <dns/rrset.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/nsec3hash.h>
 
 #include <datasrc/data_source.h>
 #include <datasrc/logger.h>
 
 #include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
 
 using namespace isc::dns;
 using namespace std;
 using namespace isc::dns::rdata;
+using namespace boost;
 
 namespace isc {
 namespace datasrc {
@@ -177,15 +180,17 @@ private:
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
                                   bool check_ns, const string* construct_name,
-                                  bool any)
+                                  bool any,
+                                  DatabaseAccessor::IteratorContextPtr context)
 {
     RRsigStore sig_store;
     bool records_found = false;
     std::map<RRType, RRsetPtr> result;
 
-    // Request the context
-    DatabaseAccessor::IteratorContextPtr
-        context(accessor_->getRecords(name, zone_id_));
+    // Request the context in case we didn't get one
+    if (!context) {
+        context = accessor_->getRecords(name, zone_id_);
+    }
     // It must not return NULL, that's a bug of the implementation
     if (!context) {
         isc_throw(isc::Unexpected, "Iterator context null at " + name);
@@ -316,12 +321,12 @@ namespace {
 typedef std::set<RRType> WantedTypes;
 
 const WantedTypes&
-NSEC_TYPES() {
+NSEC3_TYPES() {
     static bool initialized(false);
     static WantedTypes result;
 
     if (!initialized) {
-        result.insert(RRType::NSEC());
+        result.insert(RRType::NSEC3());
         initialized = true;
     }
     return (result);
@@ -331,6 +336,7 @@ const WantedTypes&
 NSEC3PARAM_TYPES() {
     static bool initialized(false);
     static WantedTypes result;
+
     if (!initialized) {
         result.insert(RRType::NSEC3PARAM());
         initialized = true;
@@ -339,6 +345,18 @@ NSEC3PARAM_TYPES() {
 }
 
 const WantedTypes&
+NSEC_TYPES() {
+    static bool initialized(false);
+    static WantedTypes result;
+
+    if (!initialized) {
+        result.insert(RRType::NSEC());
+        initialized = true;
+    }
+    return (result);
+}
+
+const WantedTypes&
 DELEGATION_TYPES() {
     static bool initialized(false);
     static WantedTypes result;
@@ -968,10 +986,120 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     }
 }
 
+// The behaviour is inspired by the one in the in-memory implementation.
 ZoneFinder::FindNSEC3Result
-DatabaseClient::Finder::findNSEC3(const Name&, bool) {
-    isc_throw(NotImplemented, "findNSEC3 is not yet implemented for database "
-              "data source");
+DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3).arg(name).
+        arg(recursive ? "recursive" : "non-recursive");
+
+    // First, validate the input
+    const NameComparisonResult cmp_result(name.compare(getOrigin()));
+    if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+        cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+        isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: " <<
+                  name << ", zone: " << getOrigin() << "/" << getClass());
+    }
+
+    // Now, we need to get the NSEC3 params from the apex and create the hash
+    // creator for it.
+    const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
+                                 NSEC3PARAM_TYPES(), false));
+    const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
+    if (!nsec3param.first || param == nsec3param.second.end()) {
+        // No NSEC3 params? :-(
+        isc_throw(DataSourceError, "findNSEC3 attempt for non NSEC3 signed " <<
+                  "zone: " << getOrigin() << "/" << getClass());
+    }
+    // This takes the RRset received from the find method, takes the first RR
+    // in it, casts it to NSEC3PARAM (as it should be that one) and then creates
+    // the hash calculator class from it.
+    const scoped_ptr<NSEC3Hash> calculator(NSEC3Hash::create(
+        dynamic_cast<const generic::NSEC3PARAM&>(
+            param->second->getRdataIterator()->getCurrent())));
+
+    // Few shortcut variables
+    const unsigned olabels(getOrigin().getLabelCount());
+    const unsigned qlabels(name.getLabelCount());
+    const string otext(getOrigin().toText());
+
+    // This will be set to the one covering the query name
+    ConstRRsetPtr covering_proof;
+
+    // We keep stripping the leftmost label until we find something.
+    // In case it is recursive, we'll exit the loop at the first iteration.
+    for (unsigned labels(qlabels); labels >= olabels; -- labels) {
+        const string hash(calculator->calculate(labels == qlabels ? name :
+                                                name.split(qlabels - labels,
+                                                           labels)));
+        // Get the exact match for the name.
+        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3_TRYHASH).
+            arg(name).arg(labels).arg(hash);
+
+        DatabaseAccessor::IteratorContextPtr
+            context(accessor_->getNSEC3Records(hash, zone_id_));
+
+        if (!context) {
+            isc_throw(Unexpected, "Iterator context null for hash " + hash);
+        }
+
+        const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
+                                          false, NULL, false, context));
+
+        if (nsec3.first) {
+            // We found an exact match against the current label.
+            const FoundIterator it(nsec3.second.find(RRType::NSEC3()));
+            if (it == nsec3.second.end()) {
+                isc_throw(DataSourceError, "Hash " + hash +
+                          "exists, but no NSEC3 there");
+            }
+
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_DATABASE_FINDNSEC3_MATCH).arg(name).arg(labels).
+                arg(*it->second);
+            // Yes, we win
+            return (FindNSEC3Result(true, labels, it->second, covering_proof));
+        } else {
+            // There's no exact match. We try a previous one. We must find it
+            // (if the zone is properly signed).
+            const string prevHash(accessor_->findPreviousNSEC3Hash(zone_id_,
+                                                                   hash));
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV).arg(name).
+                arg(labels).arg(prevHash);
+            context = accessor_->getNSEC3Records(prevHash, zone_id_);
+            const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
+                                                   NSEC3_TYPES(), false, NULL,
+                                                   false, context));
+
+            if (!prev_nsec3.first) {
+                isc_throw(DataSourceError, "Hash " + prevHash + " returned "
+                          "from findPreviousNSEC3Hash, but it is empty");
+            }
+            const FoundIterator
+                prev_it(prev_nsec3.second.find(RRType::NSEC3()));
+            if (prev_it == prev_nsec3.second.end()) {
+                isc_throw(DataSourceError, "The previous hash " + prevHash +
+                          "exists, but does not contain the NSEC3");
+            }
+
+            covering_proof = prev_it->second;
+            // In case it is recursive, we try to get an exact match a level
+            // up. If it is not recursive, the caller is ok with a covering
+            // one, so we just return it.
+            if (!recursive) {
+                LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                          DATASRC_DATABASE_FINDNSEC3_COVER).arg(name).
+                    arg(labels).arg(*covering_proof);
+                return (FindNSEC3Result(false, labels, covering_proof,
+                                        ConstRRsetPtr()));
+            }
+        }
+    }
+
+    // The zone must contain at least the apex and that one should match
+    // exactly. If that doesn't happen, we have a problem.
+    isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely a "
+              "broken NSEC3 zone: " << otext << "/" << getClass());
 }
 
 Name

+ 7 - 2
src/lib/datasrc/database.h

@@ -684,7 +684,7 @@ public:
     /// This is used to find previous NSEC3 hashes, to find covering NSEC3 in
     /// case none match exactly.
     ///
-    /// In case a hash before before the lowest or the lowest is provided,
+    /// In case a hash before the lowest or the lowest is provided,
     /// this should return the largest one in the zone (NSEC3 needs a
     /// wrap-around semantics).
     ///
@@ -889,6 +889,9 @@ public:
         ///     ones requested by types. It also puts a NULL pointer under the
         ///     ANY type into the result, if it finds any RRs at all, to easy the
         ///     identification of success.
+        /// \param srcContext This can be set to non-NULL value to override the
+        ///     iterator context used for obtaining the data. This can be used,
+        ///     for example, to get data from the NSEC3 namespace.
         /// \return A pair, where the first element indicates if the domain
         ///     contains any RRs at all (not only the requested, it may happen
         ///     this is set to true, but the second part is empty). The second
@@ -900,7 +903,9 @@ public:
         FoundRRsets getRRsets(const std::string& name,
                               const WantedTypes& types, bool check_ns,
                               const std::string* construct_name = NULL,
-                              bool any = false);
+                              bool any = false,
+                              DatabaseAccessor::IteratorContextPtr srcContext =
+                              DatabaseAccessor::IteratorContextPtr());
 
         /// \brief DNSSEC related context for ZoneFinder::findInternal.
         ///

+ 29 - 0
src/lib/datasrc/datasrc_messages.mes

@@ -75,6 +75,35 @@ The datasource tried to provide an NSEC proof that the named domain does not
 exist, but the database backend doesn't support DNSSEC. No proof is included
 in the answer as a result.
 
+% DATASRC_DATABASE_FINDNSEC3 Looking for NSEC3 for %1 in %2 mode
+Debug information. A search in an database data source for NSEC3 that
+matches or covers the given name is being started.
+
+% DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1: %2
+Debug information. An NSEC3 that covers the given name is found and
+being returned.  The found NSEC3 RRset is also displayed.
+
+% DATASRC_DATABASE_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned.  When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_DATABSE_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+
+% DATASRC_DATABASE_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space.  When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried, as "." is 1 label long).
+
+% DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV looking for previous NSEC3 for %1 at label count %2 (hash %3)
+Debug information. An exact match on hash (see
+DATASRC_DATABASE_FINDNSEC3_TRYHASH) was unsuccessful. We get the previous hash
+to that one instead.
+
 % DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3/%4
 Debug information. The database data source is looking up records with the given
 name and type in the database.

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

@@ -59,6 +59,7 @@ run_unittests_SOURCES += sqlite3_accessor_unittest.cc
 run_unittests_SOURCES += memory_datasrc_unittest.cc
 run_unittests_SOURCES += rbnode_rrset_unittest.cc
 run_unittests_SOURCES += zone_finder_context_unittest.cc
+run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
 
 # We need the actual module implementation in the tests (they are not part
 # of libdatasrc)

+ 67 - 3
src/lib/datasrc/tests/database_unittest.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "faked_nsec3.h"
+
 #include <stdlib.h>
 
 #include <boost/shared_ptr.hpp>
@@ -24,6 +26,7 @@
 #include <dns/name.h>
 #include <dns/rrttl.h>
 #include <dns/rrset.h>
+#include <dns/nsec3hash.h>
 #include <exceptions/exceptions.h>
 
 #include <datasrc/database.h>
@@ -44,6 +47,7 @@ using namespace std;
 using boost::dynamic_pointer_cast;
 using boost::lexical_cast;
 using namespace isc::dns;
+using namespace isc::datasrc::test;
 
 namespace {
 
@@ -210,8 +214,14 @@ const char* const TEST_RECORDS[][5] = {
 
 // FIXME: Taken from a different test. Fill with proper data when creating a test.
 const char* TEST_NSEC3_RECORDS[][5] = {
-    {"1BB7SO0452U1QHL98UISNDD9218GELR5", "NSEC3", "3600", "", "1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J"},
-    {"1BB7SO0452U1QHL98UISNDD9218GELR5", "RRSIG", "3600", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {apex_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {apex_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {ns1_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {ns1_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {w_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {w_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {zzz_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {zzz_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
     {NULL, NULL, NULL, NULL, NULL}
 };
 
@@ -955,7 +965,7 @@ private:
              i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
             i->push_back(hash);
         }
-        (*readonly_records_)[hash] = cur_name_;
+        nsec3_namespace_[hash] = cur_name_;
         cur_name_.clear();
     }
 
@@ -993,6 +1003,31 @@ private:
         }
         addCurHash(prev_name);
     }
+
+public:
+    // This adds the NSEC3PARAM into the apex, so we can perform some NSEC3
+    // tests. Note that the NSEC3 namespace is available in other tests, but
+    // it should not be accessed at that time.
+    void enableNSEC3() {
+        // We place the signature first, so it's in the block with the other
+        // signatures
+        vector<string> signature;
+        signature.push_back("RRSIG");
+        signature.push_back("3600");
+        signature.push_back("");
+        signature.push_back("NSEC3PARAM 5 3 3600 20000101000000 20000201000000 "
+                            "12345 example.org. FAKEFAKEFAKE");
+        signature.push_back("exmaple.org.");
+        (*readonly_records_)["example.org."].push_back(signature);
+        // Now the NSEC3 param itself
+        vector<string> param;
+        param.push_back("NSEC3PARAM");
+        param.push_back("3600");
+        param.push_back("");
+        param.push_back("1 0 12 aabbccdd");
+        param.push_back("example.org.");
+        (*readonly_records_)["example.org."].push_back(param);
+    }
 };
 
 // This tests the default getRecords behaviour, throwing NotImplemented
@@ -1046,6 +1081,11 @@ public:
                                                "FAKEFAKEFAKE"));
     }
 
+    ~ DatabaseClientTest() {
+        // Make sure we return the default creator no matter if we set it or not
+        setNSEC3HashCreator(NULL);
+    }
+
     /*
      * We initialize the client from a function, so we can call it multiple
      * times per test.
@@ -1206,6 +1246,9 @@ public:
     const std::vector<std::string> empty_rdatas_; // for NXRRSET/NXDOMAIN
     std::vector<std::string> expected_rdatas_;
     std::vector<std::string> expected_sig_rdatas_;
+
+    // A creator for use in several NSEC3 related tests.
+    TestNSEC3HashCreator test_nsec3_hash_creator_;
 };
 
 class TestSQLite3Accessor : public SQLite3Accessor {
@@ -3742,4 +3785,25 @@ TEST_F(MockDatabaseClientTest, journalWithBadData) {
                  second->getNextDiff(), DataSourceError);
 }
 
+/// Let us test a little bit of NSEC3.
+TEST_F(MockDatabaseClientTest, findNSEC3) {
+    // Set up the faked hash calculator.
+    setNSEC3HashCreator(&test_nsec3_hash_creator_);
+
+    DataSourceClient::FindResult
+        zone(this->client_->findZone(Name("example.org")));
+    ASSERT_EQ(result::SUCCESS, zone.code);
+    boost::shared_ptr<DatabaseClient::Finder> finder(
+        dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
+
+    // It'll complain if there is no NSEC3PARAM yet
+    EXPECT_THROW(finder->findNSEC3(Name("example.org"), false),
+                 DataSourceError);
+    // And enable NSEC3 in the zone.
+    this->current_accessor_->enableNSEC3();
+
+    // The rest is in the function, it is shared with in-memory tests
+    performNSEC3Test(*finder);
+}
+
 }

+ 196 - 0
src/lib/datasrc/tests/faked_nsec3.cc

@@ -0,0 +1,196 @@
+// 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 "faked_nsec3.h"
+
+#include <dns/name.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <map>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::testutils;
+
+namespace isc {
+namespace datasrc {
+namespace test {
+
+class TestNSEC3HashCreator::TestNSEC3Hash : public NSEC3Hash {
+private:
+    typedef map<Name, string> NSEC3HashMap;
+    typedef NSEC3HashMap::value_type NSEC3HashPair;
+    NSEC3HashMap map_;
+public:
+    TestNSEC3Hash() {
+        // Build pre-defined hash
+        map_[Name("example.org")] = apex_hash;
+        map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+        map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+        map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+        map_[Name("x.y.w.example.org")] =
+            "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
+        map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+        map_[Name("w.example.org")] = w_hash;
+        map_[Name("zzz.example.org")] = zzz_hash;
+        map_[Name("smallest.example.org")] =
+            "00000000000000000000000000000000";
+        map_[Name("largest.example.org")] =
+            "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
+    }
+    virtual string calculate(const Name& name) const {
+        const NSEC3HashMap::const_iterator found = map_.find(name);
+        if (found != map_.end()) {
+            return (found->second);
+        }
+        isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
+                  << name);
+    }
+    virtual bool match(const rdata::generic::NSEC3PARAM&) const {
+        return (true);
+    }
+    virtual bool match(const rdata::generic::NSEC3&) const {
+        return (true);
+    }
+};
+
+NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3PARAM&)
+    const
+{
+    return (new TestNSEC3Hash);
+}
+
+NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3&) const {
+    return (new TestNSEC3Hash);
+}
+
+void
+findNSEC3Check(bool expected_matched, uint8_t expected_labels,
+               const string& expected_closest,
+               const string& expected_next,
+               const ZoneFinder::FindNSEC3Result& result,
+               bool expected_sig)
+{
+    EXPECT_EQ(expected_matched, result.matched);
+    // Convert to int so the error messages would be more readable:
+    EXPECT_EQ(static_cast<int>(expected_labels),
+              static_cast<int>(result.closest_labels));
+
+    vector<ConstRRsetPtr> actual_rrsets;
+    ASSERT_TRUE(result.closest_proof);
+    actual_rrsets.push_back(result.closest_proof);
+    if (expected_sig) {
+        actual_rrsets.push_back(result.closest_proof->getRRsig());
+    }
+    rrsetsCheck(expected_closest, actual_rrsets.begin(),
+                actual_rrsets.end());
+
+    actual_rrsets.clear();
+    if (expected_next.empty()) {
+        EXPECT_FALSE(result.next_proof);
+    } else {
+        ASSERT_TRUE(result.next_proof);
+        actual_rrsets.push_back(result.next_proof);
+        if (expected_sig) {
+            actual_rrsets.push_back(result.next_proof->getRRsig());
+        }
+        rrsetsCheck(expected_next, actual_rrsets.begin(),
+                    actual_rrsets.end());
+    }
+}
+
+void
+performNSEC3Test(ZoneFinder &finder) {
+    // Parameter validation: the query name must be in or below the zone
+    EXPECT_THROW(finder.findNSEC3(Name("example.com"), false), OutOfZone);
+    EXPECT_THROW(finder.findNSEC3(Name("org"), true), OutOfZone);
+
+    Name origin("example.org");
+    const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+        string(nsec3_common);
+    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+        string(nsec3_common);
+    const string w_nsec3_text = string(w_hash) + ".example.org." +
+        string(nsec3_common);
+    const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+        string(nsec3_common);
+
+    // Apex name.  It should have a matching NSEC3.
+    {
+        SCOPED_TRACE("apex, non recursive mode");
+        findNSEC3Check(true, origin.getLabelCount(), apex_nsec3_text, "",
+                       finder.findNSEC3(origin, false));
+    }
+
+    // Recursive mode doesn't change the result in this case.
+    {
+        SCOPED_TRACE("apex, recursive mode");
+        findNSEC3Check(true, origin.getLabelCount(), apex_nsec3_text, "",
+                       finder.findNSEC3(origin, true));
+    }
+
+    // Non existent name (in the NSEC3 namespace -- the findNSEC3 does
+    // not look into the normal data).  Disabling recursion, a covering
+    // NSEC3 should be returned.
+    const Name www_name("www.example.org");
+    {
+        SCOPED_TRACE("non existent name, non recursive mode");
+        findNSEC3Check(false, www_name.getLabelCount(), apex_nsec3_text, "",
+                       finder.findNSEC3(www_name, false));
+    }
+
+    // Non existent name.  The closest provable encloser is the apex,
+    // and next closer is the query name itself (which NSEC3 for ns1
+    // covers)
+    // H(ns1) = 2T... < H(xxx) = Q0... < H(zzz) = R5...
+    {
+        SCOPED_TRACE("non existent name, recursive mode");
+        findNSEC3Check(true, origin.getLabelCount(), apex_nsec3_text,
+                       ns1_nsec3_text,
+                       finder.findNSEC3(Name("xxx.example.org"), true));
+    }
+
+    // Similar to the previous case, but next closer name is different
+    // from the query name.  The closet encloser is w.example.org, and
+    // next closer is y.w.example.org.
+    // H(ns1) = 2T.. < H(y.w) = K8.. < H(zzz) = R5
+    {
+        SCOPED_TRACE("non existent name, non qname next closer");
+        findNSEC3Check(true, Name("w.example.org").getLabelCount(),
+                       w_nsec3_text, ns1_nsec3_text,
+                       finder.findNSEC3(Name("x.y.w.example.org"),
+                                         true));
+    }
+
+    // In the rest of test we check hash comparison for wrap around cases.
+    {
+        SCOPED_TRACE("very small hash");
+        const Name smallest_name("smallest.example.org");
+        findNSEC3Check(false, smallest_name.getLabelCount(),
+                       zzz_nsec3_text, "",
+                       finder.findNSEC3(smallest_name, false));
+    }
+    {
+        SCOPED_TRACE("very large hash");
+        const Name largest_name("largest.example.org");
+        findNSEC3Check(false, largest_name.getLabelCount(),
+                       zzz_nsec3_text, "",
+                       finder.findNSEC3(largest_name, false));
+    }
+}
+
+}
+}
+}

+ 86 - 0
src/lib/datasrc/tests/faked_nsec3.h

@@ -0,0 +1,86 @@
+// 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 FAKED_NSEC3_H
+#define FAKED_NSEC3_H
+
+#include <datasrc/zone.h>
+
+#include <dns/nsec3hash.h>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace test {
+
+//
+// (Faked) NSEC3 hash data.  Arbitrarily borrowed from RFC515 examples.
+//
+// Commonly used NSEC3 suffix.  It's incorrect to use it for all NSEC3s, but
+// doesn't matter for the purpose of our tests.
+const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
+    "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
+// Likewise, common RRSIG suffix for NSEC3s.
+const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
+    "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+
+// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
+// object.
+//
+// For apex (example.org)
+const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+// For ns1.example.org
+const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+// For w.example.org
+const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+// For x.y.w.example.org (lower-cased)
+const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
+// For zzz.example.org.
+const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+
+// A simple faked NSEC3 hash calculator with a dedicated creator for it.
+//
+// This is used in some NSEC3-related tests below.
+class TestNSEC3HashCreator : public isc::dns::NSEC3HashCreator {
+private:
+    class TestNSEC3Hash;
+public:
+    virtual isc::dns::NSEC3Hash* create(const
+                                        isc::dns::rdata::generic::NSEC3PARAM&)
+        const;
+    virtual isc::dns::NSEC3Hash* create(const isc::dns::rdata::generic::NSEC3&)
+        const;
+};
+
+// Check the result against expected values. It directly calls EXPECT_ macros
+void
+findNSEC3Check(bool expected_matched, uint8_t expected_labels,
+               const std::string& expected_closest,
+               const std::string& expected_next,
+               const isc::datasrc::ZoneFinder::FindNSEC3Result& result,
+               bool expected_sig = false);
+
+// Perform the shared part of NSEC3 test (shared between in-memory and database
+// tests).
+void
+performNSEC3Test(ZoneFinder &finder);
+
+}
+}
+}
+
+#endif

+ 4 - 177
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "faked_nsec3.h"
+
 #include <exceptions/exceptions.h>
 
 #include <dns/masterload.h>
@@ -48,6 +50,7 @@ using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 using namespace isc::testutils;
 using boost::shared_ptr;
+using namespace isc::datasrc::test;
 
 namespace {
 // Commonly used result codes (Who should write the prefix all the time)
@@ -302,72 +305,6 @@ textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN(),
     return (rrset);
 }
 
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-class TestNSEC3HashCreator : public NSEC3HashCreator {
-    class TestNSEC3Hash : public NSEC3Hash {
-    private:
-        typedef map<Name, string> NSEC3HashMap;
-        typedef NSEC3HashMap::value_type NSEC3HashPair;
-        NSEC3HashMap map_;
-    public:
-        TestNSEC3Hash() {
-            // Build pre-defined hash
-            map_[Name("example.org")] = apex_hash;
-            map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("x.y.w.example.org")] =
-                "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
-            map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-            map_[Name("w.example.org")] = w_hash;
-            map_[Name("zzz.example.org")] = zzz_hash;
-            map_[Name("smallest.example.org")] =
-                "00000000000000000000000000000000";
-            map_[Name("largest.example.org")] =
-                "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
-        }
-        virtual string calculate(const Name& name) const {
-            const NSEC3HashMap::const_iterator found = map_.find(name);
-            if (found != map_.end()) {
-                return (found->second);
-            }
-            isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
-                      << name);
-        }
-        virtual bool match(const generic::NSEC3PARAM&) const {
-            return (true);
-        }
-        virtual bool match(const generic::NSEC3&) const {
-            return (true);
-        }
-    };
-
-public:
-    virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
-        return (new TestNSEC3Hash);
-    }
-    virtual NSEC3Hash* create(const generic::NSEC3&) const {
-        return (new TestNSEC3Hash);
-    }
-};
-
 /// \brief Test fixture for the InMemoryZoneFinder class
 class InMemoryZoneFinderTest : public ::testing::Test {
     // A straightforward pair of textual RR(set) and a RRsetPtr variable
@@ -1707,52 +1644,6 @@ TEST_F(InMemoryZoneFinderTest, addbadRRsig) {
                  InMemoryZoneFinder::AddError);
 }
 
-//
-// (Faked) NSEC3 hash data.  Arbitrarily borrowed from RFC515 examples.
-//
-// Commonly used NSEC3 suffix.  It's incorrect to use it for all NSEC3s, but
-// doesn't matter for the purpose of our tests.
-const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
-    "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
-// Likewise, common RRSIG suffix for NSEC3s.
-const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
-    "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
-
-void
-findNSEC3Check(bool expected_matched, uint8_t expected_labels,
-               const string& expected_closest,
-               const string& expected_next,
-               const ZoneFinder::FindNSEC3Result& result,
-               bool expected_sig = false)
-{
-    EXPECT_EQ(expected_matched, result.matched);
-    // Convert to int so the error messages would be more readable:
-    EXPECT_EQ(static_cast<int>(expected_labels),
-              static_cast<int>(result.closest_labels));
-
-    vector<ConstRRsetPtr> actual_rrsets;
-    ASSERT_TRUE(result.closest_proof);
-    actual_rrsets.push_back(result.closest_proof);
-    if (expected_sig) {
-        actual_rrsets.push_back(result.closest_proof->getRRsig());
-    }
-    rrsetsCheck(expected_closest, actual_rrsets.begin(),
-                actual_rrsets.end());
-
-    actual_rrsets.clear();
-    if (expected_next.empty()) {
-        EXPECT_FALSE(result.next_proof);
-    } else {
-        ASSERT_TRUE(result.next_proof);
-        actual_rrsets.push_back(result.next_proof);
-        if (expected_sig) {
-            actual_rrsets.push_back(result.next_proof->getRRsig());
-        }
-        rrsetsCheck(expected_next, actual_rrsets.begin(),
-                    actual_rrsets.end());
-    }
-}
-
 TEST_F(InMemoryZoneFinderTest, addNSEC3) {
     // Set up the faked hash calculator.
     setNSEC3HashCreator(&nsec3_hash_creator_);
@@ -2041,71 +1932,7 @@ TEST_F(InMemoryZoneFinderTest, findNSEC3) {
         string(nsec3_common);
     EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(zzz_nsec3_text)));
 
-    // Parameter validation: the query name must be in or below the zone
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("example.com"), false), OutOfZone);
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("org"), true), OutOfZone);
-
-    // Apex name.  It should have a matching NSEC3.
-    {
-        SCOPED_TRACE("apex, non recursive mode");
-        findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "",
-                       zone_finder_.findNSEC3(origin_, false));
-    }
-
-    // Recursive mode doesn't change the result in this case.
-    {
-        SCOPED_TRACE("apex, recursive mode");
-        findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "",
-                       zone_finder_.findNSEC3(origin_, true));
-    }
-
-    // Non existent name.  Disabling recursion, a covering NSEC3 should be
-    // returned.
-    const Name www_name("www.example.org");
-    {
-        SCOPED_TRACE("non existent name, non recursive mode");
-        findNSEC3Check(false, www_name.getLabelCount(), apex_nsec3_text, "",
-                       zone_finder_.findNSEC3(www_name, false));
-    }
-
-    // Non existent name.  The closest provable encloser is the apex,
-    // and next closer is the query name itself (which NSEC3 for ns1
-    // covers)
-    // H(ns1) = 2T... < H(xxx) = Q0... < H(zzz) = R5...
-    {
-        SCOPED_TRACE("non existent name, recursive mode");
-        findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text,
-                       ns1_nsec3_text,
-                       zone_finder_.findNSEC3(Name("xxx.example.org"), true));
-    }
-
-    // Similar to the previous case, but next closer name is different
-    // from the query name.  The closet encloser is w.example.org, and
-    // next closer is y.w.example.org.
-    // H(ns1) = 2T.. < H(y.w) = K8.. < H(zzz) = R5
-    {
-        SCOPED_TRACE("non existent name, non qname next closer");
-        findNSEC3Check(true, Name("w.example.org").getLabelCount(),
-                       w_nsec3_text, ns1_nsec3_text,
-                       zone_finder_.findNSEC3(Name("x.y.w.example.org"),
-                                              true));
-    }
-
-    // In the rest of test we check hash comparison for wrap around cases.
-    {
-        SCOPED_TRACE("very small hash");
-        const Name smallest_name("smallest.example.org");
-        findNSEC3Check(false, smallest_name.getLabelCount(),
-                       zzz_nsec3_text, "",
-                       zone_finder_.findNSEC3(smallest_name, false));
-    }
-    {
-        SCOPED_TRACE("very large hash");
-        const Name largest_name("largest.example.org");
-        findNSEC3Check(false, largest_name.getLabelCount(),
-                       zzz_nsec3_text, "",
-                       zone_finder_.findNSEC3(largest_name, false));
-    }
+    performNSEC3Test(zone_finder_);
 }
 
 TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {

+ 1 - 0
tests/lettuce/configurations/.gitignore

@@ -1 +1,2 @@
+/bindctl_commands.config
 /example.org.config

+ 34 - 0
tests/lettuce/configurations/bindctl_commands.config.orig

@@ -0,0 +1,34 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "*"
+        } ]
+    },
+    "Auth": {
+        "database_file": "data/example.org.sqlite3",
+        "listen_on": [ {
+            "port": 47806,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "StatsHttpd": {
+        "listen_on": [ {
+            "port": 47811,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "dispensable", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
+            "b10-stats-httpd": { "address": "StatsHttpd", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}

+ 16 - 0
tests/lettuce/configurations/default.config

@@ -0,0 +1,16 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "*"
+        } ]
+    },
+    "StatsHttpd": {
+        "listen_on": [ {
+            "port": 47811,
+            "address": "127.0.0.1"
+        } ]
+    }
+}

+ 7 - 1
tests/lettuce/configurations/example.org.config.orig

@@ -4,7 +4,7 @@
         "loggers": [ {
             "debuglevel": 99,
             "severity": "DEBUG",
-            "name": "auth"
+            "name": "*"
         } ]
     },
     "Auth": {
@@ -13,5 +13,11 @@
             "port": 47806,
             "address": "127.0.0.1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }

+ 8 - 1
tests/lettuce/configurations/example.org.inmem.config

@@ -1 +1,8 @@
-{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "auth", "debuglevel": 99}]}, "Auth": {"database_file": "", "listen_on": [{"port": 47806, "address": "127.0.0.1"}], "datasources": [{"zones": [{"origin": "example.org", "file": "data/example.org"}], "type": "memory"}]}}
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Auth": {"database_file": "", "listen_on": [{"port": 47806, "address": "127.0.0.1"}], "datasources": [{"zones": [{"origin": "example.org", "file": "data/example.org"}], "type": "memory"}]},
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}

+ 7 - 1
tests/lettuce/configurations/example2.org.config

@@ -3,7 +3,7 @@
     "Logging": {
         "loggers": [ {
             "severity": "DEBUG",
-            "name": "auth",
+            "name": "*",
             "debuglevel": 99
         }
         ]
@@ -14,5 +14,11 @@
             "port": 47807,
             "address": "::1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }

+ 11 - 1
tests/lettuce/configurations/ixfr-out/testset1-config.db

@@ -1 +1,11 @@
-{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.slite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]}}
+{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.sqlite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]},
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}

+ 1 - 1
tests/lettuce/configurations/multi_instance/multi_auth.config.orig

@@ -1 +1 @@
-{"version": 2, "Auth": {"listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth-2": {"kind": "needed", "special": "auth"}, "b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Auth": {"listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth-2": {"kind": "dispensable", "special": "auth"}, "b10-auth": {"kind": "dispensable", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}

+ 14 - 0
tests/lettuce/configurations/no_db_file.config

@@ -1,10 +1,24 @@
 {
     "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "severity": "DEBUG",
+            "name": "*",
+            "debuglevel": 99
+        }
+        ]
+    },
     "Auth": {
         "database_file": "data/test_nonexistent_db.sqlite3",
         "listen_on": [ {
             "port": 47806,
             "address": "127.0.0.1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }

+ 1 - 1
tests/lettuce/configurations/nsec3/nsec3_auth.config

@@ -1 +1 @@
-{"version": 2, "Auth": {"datasources": [{"zones": [{"origin": "example.", "file": "configurations/nsec3/rfc5155-example.zone.signed"}], "type": "memory"}], "listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Auth": {"datasources": [{"zones": [{"origin": "example.", "file": "configurations/nsec3/rfc5155-example.zone.signed"}], "type": "memory"}], "listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}

+ 1 - 1
tests/lettuce/configurations/resolver/resolver_basic.config.orig

@@ -1 +1 @@
-{"version": 2, "Resolver": {"query_acl": [{"action": "REJECT", "from": "127.0.0.1"}], "listen_on": [{"port": 47806, "address": "127.0.0.1"}]}, "Boss": {"components": {"b10-resolver": {"kind": "needed"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Resolver": {"query_acl": [{"action": "REJECT", "from": "127.0.0.1"}], "listen_on": [{"port": 47806, "address": "127.0.0.1"}]}, "Boss": {"components": {"b10-resolver": {"kind": "needed"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}

+ 9 - 1
tests/lettuce/configurations/xfrin/retransfer_master.conf

@@ -4,7 +4,7 @@
         "loggers": [ {
             "debuglevel": 99,
             "severity": "DEBUG",
-            "name": "auth"
+            "name": "*"
         } ]
     },
     "Auth": {
@@ -18,5 +18,13 @@
         "zone_config": [ {
             "origin": "example.org"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }

+ 9 - 1
tests/lettuce/configurations/xfrin/retransfer_slave.conf

@@ -4,7 +4,7 @@
         "loggers": [ {
             "debuglevel": 99,
             "severity": "DEBUG",
-            "name": "auth"
+            "name": "*"
         } ]
     },
     "Auth": {
@@ -13,5 +13,13 @@
             "port": 47806,
             "address": "127.0.0.1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }

+ 1 - 0
tests/lettuce/data/.gitignore

@@ -0,0 +1 @@
+/test_nonexistent_db.sqlite3

tests/lettuce/data/ixfr-out/zones.slite3 → tests/lettuce/data/ixfr-out/zones.sqlite3


+ 30 - 3
tests/lettuce/features/bindctl_commands.feature

@@ -8,7 +8,15 @@ Feature: control with bindctl
     # modules (note that it 'misuses' the help command for this,
     # there is a Boss command 'show_processes' but it's output is
     # currently less standardized than 'help')
-    Given I have bind10 running with configuration example.org.config
+    Given I have bind10 running with configuration bindctl_commands.config
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message ZONEMGR_STARTED
+    And wait for bind10 stderr message AUTH_SERVER_STARTED
+    And wait for bind10 stderr message XFRIN_STARTED
+    And wait for bind10 stderr message XFROUT_STARTED
+    And wait for bind10 stderr message STATS_STARTING
+    And wait for bind10 stderr message STATHTTPD_STARTED
 
     Then remove bind10 configuration Boss/components/NOSUCHMODULE
     last bindctl output should contain Error
@@ -19,20 +27,39 @@ Feature: control with bindctl
     bind10 module Xfrin should be running
     bind10 module Auth should be running
     bind10 module StatsHttpd should be running
+    bind10 module Resolver should not be running
 
     Then remove bind10 configuration Boss/components value b10-xfrout
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
     last bindctl output should not contain Error
+
     # assuming it won't error for further modules (if it does, the final
     # 'should not be running' tests would fail anyway)
+    Then remove bind10 configuration Boss/components value b10-stats-httpd
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+    last bindctl output should not contain Error
+
     Then remove bind10 configuration Boss/components value b10-stats
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+    last bindctl output should not contain Error
+
     Then remove bind10 configuration Boss/components value b10-zonemgr
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+    last bindctl output should not contain Error
+
     Then remove bind10 configuration Boss/components value b10-xfrin
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+    last bindctl output should not contain Error
+
     Then remove bind10 configuration Boss/components value b10-auth
-    Then remove bind10 configuration Boss/components value b10-stats-httpd
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+    last bindctl output should not contain Error
 
+    # After these ^^^ have been stopped...
     bind10 module Xfrout should not be running
-    bind10 module Stats should not be running
     bind10 module Zonemgr should not be running
     bind10 module Xfrin should not be running
     bind10 module Auth should not be running
     bind10 module StatsHttpd should not be running
+    bind10 module Stats should not be running
+    bind10 module Resolver should not be running

+ 22 - 0
tests/lettuce/features/default.feature

@@ -0,0 +1,22 @@
+Feature: default bind10 config
+    Tests for the default configuration of bind10.
+
+    Scenario: Check that only the default components are running
+    Given I have bind10 running with configuration default.config
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message STATS_STARTING
+    And wait for bind10 stderr message STATHTTPD_STARTED
+
+    # These should be running
+    bind10 module Boss should be running
+    And bind10 module Logging should be running
+    And bind10 module Stats should be running
+    And bind10 module StatsHttpd should be running
+
+    # These should not be running
+    bind10 module Resolver should not be running
+    And bind10 module Xfrout should not be running
+    And bind10 module Zonemgr should not be running
+    And bind10 module Xfrin should not be running
+    And bind10 module Auth should not be running

+ 54 - 6
tests/lettuce/features/example.feature

@@ -8,6 +8,18 @@ Feature: Example feature
     
     Scenario: A simple example
         Given I have bind10 running with configuration example.org.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for www.example.org should have rcode NOERROR
         A query for www.doesnotexist.org should have rcode REFUSED
         The SOA serial for example.org should be 1234
@@ -26,8 +38,18 @@ Feature: Example feature
         # is actually a compound step consisting of the following two
         # one to start the server
         When I start bind10 with configuration no_db_file.config
-        # And one to wait until it reports that b10-auth has started
-        Then wait for bind10 auth to start
+
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
 
         # This is a general step to stop a named process. By convention,
         # the default name for any process is the same as the one we
@@ -50,6 +72,17 @@ Feature: Example feature
         # This is a compound statement that starts and waits for the
         # started message
         Given I have bind10 running with configuration example.org.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
 
         # Some simple queries that is not examined further
         A query for www.example.com should have rcode REFUSED
@@ -113,8 +146,18 @@ Feature: Example feature
         # the system
 
         When I start bind10 with configuration example.org.config
-        Then wait for bind10 auth to start
-        Wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for www.example.org should have rcode NOERROR
         Wait for new bind10 stderr message AUTH_SEND_NORMAL_RESPONSE
         Then set bind10 configuration Auth/database_file to data/empty_db.sqlite3
@@ -128,10 +171,15 @@ Feature: Example feature
     Scenario: two bind10 instances
         # This is more a test of the test system, start 2 bind10's
         When I start bind10 with configuration example.org.config as bind10_one
+        And wait for bind10_one stderr message BIND10_STARTED_CC
+        And wait for bind10_one stderr message CMDCTL_STARTED
+        And wait for bind10_one stderr message AUTH_SERVER_STARTED
+
         And I start bind10 with configuration example2.org.config with cmdctl port 47804 as bind10_two
+        And wait for bind10_two stderr message BIND10_STARTED_CC
+        And wait for bind10_two stderr message CMDCTL_STARTED
+        And wait for bind10_two stderr message AUTH_SERVER_STARTED
 
-        Then wait for bind10 auth of bind10_one to start
-        Then wait for bind10 auth of bind10_two to start
         A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
         A query for www.example.org to [::1]:47807 should have rcode NOERROR
 

+ 16 - 2
tests/lettuce/features/ixfr_out_bind10.feature

@@ -31,7 +31,14 @@ Feature: IXFR out
 
     Scenario: Test Set 1
         Given I have bind10 running with configuration ixfr-out/testset1-config.db
-        Then wait for bind10 xfrout to start
+
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message XFROUT_STARTED
+        And wait for bind10 stderr message XFRIN_STARTED
+        And wait for bind10 stderr message ZONEMGR_STARTED
+
         The SOA serial for example.com should be 22
 
         #
@@ -146,7 +153,14 @@ Feature: IXFR out
 
     Scenario: Test Set 2
         Given I have bind10 running with configuration ixfr-out/testset1-config.db
-        Then wait for bind10 xfrout to start
+
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message XFROUT_STARTED
+        And wait for bind10 stderr message XFRIN_STARTED
+        And wait for bind10 stderr message ZONEMGR_STARTED
+
         The SOA serial for example.com should be 22
 
         #

+ 18 - 1
tests/lettuce/features/multi_instance.feature

@@ -5,7 +5,22 @@ Feature: Multiple instances
     Scenario: Multiple instances of Auth
         # This config should have two running instances
         Given I have bind10 running with configuration multi_instance/multi_auth.config
-        And bind10 module Auth should be running
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+
+        # This is a hack. We should actually check if b10-auth and
+        # b10-auth-2 are started by name. But there's currently no way
+        # for a component to find out its name and log it.
+        And wait 2 times for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for example.com should have rcode REFUSED
 
         # this also checks whether the process is running
@@ -13,6 +28,7 @@ Feature: Multiple instances
         And remember the pid of process b10-auth-2
 
         When I remove bind10 configuration Boss/components value b10-auth-2
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
 
         Then the pid of process b10-auth should not have changed
         And a query for example.com should have rcode REFUSED
@@ -31,5 +47,6 @@ Feature: Multiple instances
         A query for example.com should have rcode REFUSED
 
         When I remove bind10 configuration Boss/components value b10-auth
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
         Then the pid of process b10-auth-2 should not have changed
         A query for example.com should have rcode REFUSED

+ 264 - 96
tests/lettuce/features/nsec3_auth.feature

@@ -12,6 +12,18 @@ Feature: NSEC3 Authoritative service
 
     Scenario: B.1. Name Error
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for a.c.x.w.example. should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -20,18 +32,30 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.	3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.	3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.	3600	IN	NSEC3	1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM 
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
-        b4um86eghhds6nea196smvmlo4ors995.example.	3600	IN	NSEC3	1 1 12 aabbccdd  gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG 
-        b4um86eghhds6nea196smvmlo4ors995.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example.	3600	IN	NSEC3	1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG 
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      NSEC3   1 1 12 aabbccdd  gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      NSEC3   1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
         """
 
     Scenario: B.2. No Data Error
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for ns1.example. type MX should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -40,14 +64,26 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.	3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.	3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.	3600	IN	NSEC3	1 1 12 aabbccdd  2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG 
-        2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG
+        2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
         """
 
     Scenario: B2.1. No Data Error, Empty Non-Terminal
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for y.w.example. should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -56,14 +92,26 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.	3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.	3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.	3600	IN	NSEC3	1 1 12 aabbccdd  k8udemvp1j2f7eg6jebps17vp3n8i58h
-        ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.       3600    IN      NSEC3   1 1 12 aabbccdd  k8udemvp1j2f7eg6jebps17vp3n8i58h
+        ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
         """
 
     Scenario: B.3. Referral to an Opt-Out Unsigned Zone
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for mc.c.example. type MX should have rcode NOERROR
         The last query response should have flags qr rd
         The last query response should have edns_flags do
@@ -72,12 +120,12 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 3
         The authority section of the last query response should be
         """
-        c.example.	3600	IN	NS	ns1.c.example.
-        c.example.	3600	IN	NS	ns2.c.example.
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example.	3600	IN	NSEC3	1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG 
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.	3600	IN	NSEC3	1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM 
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        c.example.      3600    IN      NS      ns1.c.example.
+        c.example.      3600    IN      NS      ns2.c.example.
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      NSEC3   1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
         """
         The additional section of the last query response should be
         """
@@ -87,6 +135,18 @@ Feature: NSEC3 Authoritative service
 
     Scenario: B.4. Wildcard Expansion
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for a.z.w.example. type MX should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -95,33 +155,45 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 9
         The answer section of the last query response should be
         """
-        a.z.w.example.	3600	IN	MX	1 ai.example.
-        a.z.w.example.	3600	IN	RRSIG	MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+        a.z.w.example.  3600    IN      MX      1 ai.example.
+        a.z.w.example.  3600    IN      RRSIG   MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
         """
         The authority section of the last query response should be
         """
-        example.	3600	IN	NS	ns1.example.
-        example.	3600	IN	NS	ns2.example.
-        example.	3600	IN	RRSIG	NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
-        q04jkcevqvmu85r014c7dkba38o0ji5r.example.	3600	IN	NSEC3	1 1 12 aabbccdd  r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG 
-        q04jkcevqvmu85r014c7dkba38o0ji5r.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+        example.        3600    IN      NS      ns1.example.
+        example.        3600    IN      NS      ns2.example.
+        example.        3600    IN      RRSIG   NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      NSEC3   1 1 12 aabbccdd  r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
         """
         # This is slightly different from the example in RFC5155; there are
         # more RRs in the additional section.
         The additional section of the last query response should be
         """
-        ai.example.		3600	IN	A	192.0.2.9
-        ai.example.		3600	IN	AAAA	2001:db8::f00:baa9
-        ns1.example.		3600	IN	A	192.0.2.1
-        ns2.example.		3600	IN	A	192.0.2.2
-        ai.example.		3600	IN	RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
-        ai.example.		3600	IN	RRSIG	AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
-        ns1.example.		3600	IN	RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
-        ns2.example.		3600	IN	RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+        ai.example.             3600    IN      A       192.0.2.9
+        ai.example.             3600    IN      AAAA    2001:db8::f00:baa9
+        ns1.example.            3600    IN      A       192.0.2.1
+        ns2.example.            3600    IN      A       192.0.2.2
+        ai.example.             3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+        ai.example.             3600    IN      RRSIG   AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+        ns1.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+        ns2.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
         """
 
     Scenario: B.5. Wildcard No Data Error
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for a.z.w.example. type AAAA should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -130,18 +202,30 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.	3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.	3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        k8udemvp1j2f7eg6jebps17vp3n8i58h.example.	3600	IN	NSEC3	1 1 12 aabbccdd  kohar7mbb8dc2ce8a9qvl8hon4k53uhi
-        k8udemvp1j2f7eg6jebps17vp3n8i58h.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
-        q04jkcevqvmu85r014c7dkba38o0ji5r.example.	3600	IN	NSEC3	1 1 12 aabbccdd  r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG 
-        q04jkcevqvmu85r014c7dkba38o0ji5r.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
-        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.	3600	IN	NSEC3	1 1 12 aabbccdd  t644ebqk9bibcna874givr6joj62mlhv MX RRSIG 
-        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example.       3600    IN      NSEC3   1 1 12 aabbccdd  kohar7mbb8dc2ce8a9qvl8hon4k53uhi
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      NSEC3   1 1 12 aabbccdd  r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.       3600    IN      NSEC3   1 1 12 aabbccdd  t644ebqk9bibcna874givr6joj62mlhv MX RRSIG
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
         """
 
     Scenario: B.6. DS Child Zone No Data Error
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for example. type DS should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -150,10 +234,10 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.	3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.	3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.	3600	IN	NSEC3	1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM 
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
         """
 
     #
@@ -162,6 +246,18 @@ Feature: NSEC3 Authoritative service
 
     Scenario: 7.2.2 other; Name Error where one NSEC3 covers multiple parts of proof (closest encloser)
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for b.x.w.example. should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -170,16 +266,28 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.	3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.	3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        b4um86eghhds6nea196smvmlo4ors995.example.	3600	IN	NSEC3	1 1 12 aabbccdd  gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG 
-        b4um86eghhds6nea196smvmlo4ors995.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example.	3600	IN	NSEC3	1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG 
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example.	3600	IN	RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      NSEC3   1 1 12 aabbccdd  gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      NSEC3   1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
         """
 
     Scenario: 7.2.2 other; Name Error where one NSEC3 covers multiple parts of proof (wildcard)
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for a.w.example. should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -188,16 +296,28 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.		3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.		3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3	1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
-        k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
-        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3	1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
-        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3 1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
         """
 
     Scenario: Wildcard other: Wildcard name itself
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for *.w.example. type MX should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -206,29 +326,41 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 9
         The answer section of the last query response should be
         """
-        *.w.example.		3600	IN	MX	1 ai.example.
-        *.w.example.		3600	IN	RRSIG	MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+        *.w.example.            3600    IN      MX      1 ai.example.
+        *.w.example.            3600    IN      RRSIG   MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
         """
         The authority section of the last query response should be
         """
-        example.		3600	IN	NS	ns1.example.
-        example.		3600	IN	NS	ns2.example.
-        example.		3600	IN	RRSIG	NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+        example.                3600    IN      NS      ns1.example.
+        example.                3600    IN      NS      ns2.example.
+        example.                3600    IN      RRSIG   NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
         """
         The additional section of the last query response should be
         """
-        ai.example.		3600	IN	A	192.0.2.9
-        ai.example.		3600	IN	AAAA	2001:db8::f00:baa9
-        ns1.example.		3600	IN	A	192.0.2.1
-        ns2.example.		3600	IN	A	192.0.2.2
-        ai.example.		3600	IN	RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
-        ai.example.		3600	IN	RRSIG	AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
-        ns1.example.		3600	IN	RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
-        ns2.example.		3600	IN	RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+        ai.example.             3600    IN      A       192.0.2.9
+        ai.example.             3600    IN      AAAA    2001:db8::f00:baa9
+        ns1.example.            3600    IN      A       192.0.2.1
+        ns2.example.            3600    IN      A       192.0.2.2
+        ai.example.             3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+        ai.example.             3600    IN      RRSIG   AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+        ns1.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+        ns2.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
         """
 
     Scenario: Wildcard other: Wildcard name itself nodata
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for *.w.example. type A should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -237,14 +369,26 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.		3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.		3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3	1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
-        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
         """
 
     Scenario: Direct query for NSEC3 record
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. type NSEC3 should have rcode NXDOMAIN
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -253,18 +397,30 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.		3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.		3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3	1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
-        q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3	1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
-        q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
-        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3	1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
-        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
         """
 
     Scenario: No data, type DS, in-zone
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for ai.example. type DS should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -273,14 +429,26 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.		3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.		3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3	1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
-        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
         """
 
     Scenario: No data, type DS, optout delegation
         Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A dnssec query for c.example. type DS should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have edns_flags do
@@ -289,10 +457,10 @@ Feature: NSEC3 Authoritative service
         The last query response should have adcount 1
         The authority section of the last query response should be
         """
-        example.		3600	IN	SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
-        example.		3600	IN	RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3	1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
-        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3	1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
-        35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
         """

+ 42 - 18
tests/lettuce/features/queries.feature

@@ -5,6 +5,18 @@ Feature: Querying feature
 
     Scenario: Repeated queries
         Given I have bind10 running with configuration example.org.inmem.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for www.example.org should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have ancount 1
@@ -13,17 +25,17 @@ Feature: Querying feature
 
         The answer section of the last query response should be
         """
-        www.example.org.	3600	IN	A	192.0.2.1
+        www.example.org.        3600    IN      A       192.0.2.1
         """
         The authority section of the last query response should be
         """
-        example.org.		3600	IN	NS	ns1.example.org.
-        example.org.		3600	IN	NS	ns2.example.org.
+        example.org.            3600    IN      NS      ns1.example.org.
+        example.org.            3600    IN      NS      ns2.example.org.
         """
         The additional section of the last query response should be
         """
-        ns1.example.org.	3600	IN	A	192.0.2.3
-        ns2.example.org.	3600	IN	A	192.0.2.4
+        ns1.example.org.        3600    IN      A       192.0.2.3
+        ns2.example.org.        3600    IN      A       192.0.2.4
         """
 
         # Repeat of the above
@@ -35,17 +47,17 @@ Feature: Querying feature
 
         The answer section of the last query response should be
         """
-        www.example.org.	3600	IN	A	192.0.2.1
+        www.example.org.        3600    IN      A       192.0.2.1
         """
         The authority section of the last query response should be
         """
-        example.org.		3600	IN	NS	ns1.example.org.
-        example.org.		3600	IN	NS	ns2.example.org.
+        example.org.            3600    IN      NS      ns1.example.org.
+        example.org.            3600    IN      NS      ns2.example.org.
         """
         The additional section of the last query response should be
         """
-        ns1.example.org.	3600	IN	A	192.0.2.3
-        ns2.example.org.	3600	IN	A	192.0.2.4
+        ns1.example.org.        3600    IN      A       192.0.2.3
+        ns2.example.org.        3600    IN      A       192.0.2.4
         """
 
         # And now query something completely different
@@ -56,11 +68,23 @@ Feature: Querying feature
         The last query response should have adcount 0
         The authority section of the last query response should be
         """
-        example.org.		3600	IN	SOA	ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+        example.org.            3600    IN      SOA     ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
         """
 
     Scenario: ANY query
         Given I have bind10 running with configuration example.org.inmem.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for example.org type ANY should have rcode NOERROR
         The last query response should have flags qr aa rd
         The last query response should have ancount 4
@@ -68,16 +92,16 @@ Feature: Querying feature
         The last query response should have adcount 3
         The answer section of the last query response should be
         """
-        example.org.		3600	IN	NS	ns1.example.org.
-        example.org.		3600	IN	NS	ns2.example.org.
-        example.org.		3600	IN	SOA	ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
-        example.org.		3600	IN	MX	10 mail.example.org.
+        example.org.            3600    IN      NS      ns1.example.org.
+        example.org.            3600    IN      NS      ns2.example.org.
+        example.org.            3600    IN      SOA     ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+        example.org.            3600    IN      MX      10 mail.example.org.
         """
         The additional section of the last query response should be
         """
-        ns1.example.org.	3600	IN	A	192.0.2.3
-        ns2.example.org.	3600	IN	A	192.0.2.4
-        mail.example.org.	3600	IN	A	192.0.2.10
+        ns1.example.org.        3600    IN      A       192.0.2.3
+        ns2.example.org.        3600    IN      A       192.0.2.4
+        mail.example.org.       3600    IN      A       192.0.2.10
         """
     Scenario: Delegation query for unsigned child zone
         Given I have bind10 running with configuration example.org.inmem.config

+ 11 - 1
tests/lettuce/features/resolver_basic.feature

@@ -11,7 +11,17 @@ Feature: Basic Resolver
         # to be revised (as it would then leak, which is probably true
         # for any resolver system test)
         When I start bind10 with configuration resolver/resolver_basic.config
-        And wait for new bind10 stderr message RESOLVER_STARTED
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message RESOLVER_STARTED
+
+        bind10 module Resolver should be running
+        And bind10 module Auth should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
 
         # The ACL is set to reject any queries
         A query for l.root-servers.net. should have rcode REFUSED

+ 20 - 9
tests/lettuce/features/terrain/bind10_control.py

@@ -14,10 +14,19 @@
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 from lettuce import *
+import time
 import subprocess
 import re
 import json
 
+@step('sleep for (\d+) seconds')
+def wait_seconds(step, seconds):
+    """Sleep for some seconds.
+       Parameters:
+       seconds number of seconds to sleep for.
+    """
+    time.sleep(float(seconds))
+
 @step('start bind10(?: with configuration (\S+))?' +\
       '(?: with cmdctl port (\d+))?' +\
       '(?: with msgq socket file (\S+))?' +\
@@ -100,18 +109,15 @@ def wait_for_xfrout(step, process_name):
 def have_bind10_running(step, config_file, cmdctl_port, process_name):
     """
     Compound convenience step for running bind10, which consists of
-    start_bind10 and wait_for_auth.
+    start_bind10.
     Currently only supports the 'with configuration' option.
     """
     start_step = 'start bind10 with configuration ' + config_file
-    wait_step = 'wait for bind10 auth to start'
     if cmdctl_port is not None:
         start_step += ' with cmdctl port ' + str(cmdctl_port)
     if process_name is not None:
         start_step += ' as ' + process_name
-        wait_step = 'wait for bind10 auth of ' + process_name + ' to start'
     step.given(start_step)
-    step.given(wait_step)
 
 # function to send lines to bindctl, and store the result
 def run_bindctl(commands, cmdctl_port=None):
@@ -142,8 +148,8 @@ def run_bindctl(commands, cmdctl_port=None):
                         "stderr:\n" + str(stderr)
 
 
-@step('last bindctl( stderr)? output should( not)? contain (\S+)')
-def check_bindctl_output(step, stderr, notv, string):
+@step('last bindctl( stderr)? output should( not)? contain (\S+)( exactly)?')
+def check_bindctl_output(step, stderr, notv, string, exactly):
     """Checks the stdout (or stderr) stream of the last run of bindctl,
        fails if the given string is not found in it (or fails if 'not' was
        set and it is found
@@ -151,14 +157,19 @@ def check_bindctl_output(step, stderr, notv, string):
        stderr ('stderr'): Check stderr instead of stdout output
        notv ('not'): reverse the check (fail if string is found)
        string ('contain <string>') string to look for
+       exactly ('exactly'): Make an exact match delimited by whitespace
     """
     if stderr is None:
         output = world.last_bindctl_stdout
     else:
         output = world.last_bindctl_stderr
     found = False
-    if string in output:
-        found = True
+    if exactly is None:
+        if string in output:
+            found = True
+    else:
+        if re.search(r'^\s+' + string + r'\s+', output, re.IGNORECASE | re.MULTILINE) is not None:
+            found = True
     if notv is None:
         assert found == True, "'" + string +\
                               "' was not found in bindctl output:\n" +\
@@ -322,4 +333,4 @@ def module_is_running(step, name, not_str):
     if not_str is None:
         not_str = ""
     step.given('send bind10 the command help')
-    step.given('last bindctl output should' + not_str + ' contain ' + name)
+    step.given('last bindctl output should' + not_str + ' contain ' + name + ' exactly')

+ 12 - 6
tests/lettuce/features/terrain/steps.py

@@ -30,12 +30,13 @@ def stop_a_named_process(step, process_name):
     """
     world.processes.stop_process(process_name)
 
-@step('wait for (new )?(\w+) stderr message (\w+)(?: not (\w+))?')
-def wait_for_message(step, new, process_name, message, not_message):
+@step('wait (?:(\d+) times )?for (new )?(\w+) stderr message (\w+)(?: not (\w+))?')
+def wait_for_stderr_message(step, times, new, process_name, message, not_message):
     """
     Block until the given message is printed to the given process's stderr
     output.
     Parameter:
+    times: Check for the string this many times.
     new: (' new', optional): Only check the output printed since last time
                              this step was used for this process.
     process_name ('<name> stderr'): Name of the process to check the output of.
@@ -46,16 +47,19 @@ def wait_for_message(step, new, process_name, message, not_message):
     strings = [message]
     if not_message is not None:
         strings.append(not_message)
-    (found, line) = world.processes.wait_for_stderr_str(process_name, strings, new)
+    if times is None:
+        times = 1
+    (found, line) = world.processes.wait_for_stderr_str(process_name, strings, new, int(times))
     if not_message is not None:
         assert found != not_message, line
 
-@step('wait for (new )?(\w+) stdout message (\w+)(?: not (\w+))?')
-def wait_for_message(step, process_name, message, not_message):
+@step('wait (?:(\d+) times )?for (new )?(\w+) stdout message (\w+)(?: not (\w+))?')
+def wait_for_stdout_message(step, times, new, process_name, message, not_message):
     """
     Block until the given message is printed to the given process's stdout
     output.
     Parameter:
+    times: Check for the string this many times.
     new: (' new', optional): Only check the output printed since last time
                              this step was used for this process.
     process_name ('<name> stderr'): Name of the process to check the output of.
@@ -66,7 +70,9 @@ def wait_for_message(step, process_name, message, not_message):
     strings = [message]
     if not_message is not None:
         strings.append(not_message)
-    (found, line) = world.processes.wait_for_stdout_str(process_name, strings, new)
+    if times is None:
+        times = 1
+    (found, line) = world.processes.wait_for_stdout_str(process_name, strings, new, int(times))
     if not_message is not None:
         assert found != not_message, line
 

+ 26 - 12
tests/lettuce/features/terrain/terrain.py

@@ -42,6 +42,8 @@ import time
 # The first element is the original, the second is the target that will be
 # used by the tests that need them
 copylist = [
+    ["configurations/bindctl_commands.config.orig",
+     "configurations/bindctl_commands.config"],
     ["configurations/example.org.config.orig",
      "configurations/example.org.config"],
     ["configurations/resolver/resolver_basic.config.orig",
@@ -164,7 +166,7 @@ class RunningProcess:
         os.remove(self.stderr_filename)
         os.remove(self.stdout_filename)
 
-    def _wait_for_output_str(self, filename, running_file, strings, only_new):
+    def _wait_for_output_str(self, filename, running_file, strings, only_new, matches = 1):
         """
         Wait for a line of output in this process. This will (if only_new is
         False) first check all previous output from the process, and if not
@@ -178,18 +180,22 @@ class RunningProcess:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
+        match_count = 0
         if not only_new:
             full_file = open(filename, "r")
             for line in full_file:
                 for string in strings:
                     if line.find(string) != -1:
-                        full_file.close()
-                        return (string, line)
+                        match_count += 1
+                        if match_count >= matches:
+                            full_file.close()
+                            return (string, line)
         wait_count = 0
         while wait_count < OUTPUT_WAIT_MAX_INTERVALS:
             where = running_file.tell()
@@ -197,42 +203,46 @@ class RunningProcess:
             if line:
                 for string in strings:
                     if line.find(string) != -1:
-                        return (string, line)
+                        match_count += 1
+                        if match_count >= matches:
+                            return (string, line)
             else:
                 wait_count += 1
                 time.sleep(OUTPUT_WAIT_INTERVAL)
                 running_file.seek(where)
         assert False, "Timeout waiting for process output: " + str(strings)
 
-    def wait_for_stderr_str(self, strings, only_new = True):
+    def wait_for_stderr_str(self, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in this process's stderr output.
         Parameters:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
         return self._wait_for_output_str(self.stderr_filename, self.stderr,
-                                         strings, only_new)
+                                         strings, only_new, matches)
 
-    def wait_for_stdout_str(self, strings, only_new = True):
+    def wait_for_stdout_str(self, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in this process's stdout output.
         Parameters:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
         return self._wait_for_output_str(self.stdout_filename, self.stdout,
-                                         strings, only_new)
+                                         strings, only_new, matches)
 
 # Container class for a number of running processes
 # i.e. servers like bind10, etc
@@ -298,7 +308,7 @@ class RunningProcesses:
         for process in self.processes.values():
             process.remove_files_on_exit = False
 
-    def wait_for_stderr_str(self, process_name, strings, only_new = True):
+    def wait_for_stderr_str(self, process_name, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in the given process's stderr output.
         Parameters:
@@ -306,6 +316,7 @@ class RunningProcesses:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns the matched string.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
@@ -314,9 +325,10 @@ class RunningProcesses:
         assert process_name in self.processes,\
            "Process " + process_name + " unknown"
         return self.processes[process_name].wait_for_stderr_str(strings,
-                                                                only_new)
+                                                                only_new,
+                                                                matches)
 
-    def wait_for_stdout_str(self, process_name, strings, only_new = True):
+    def wait_for_stdout_str(self, process_name, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in the given process's stdout output.
         Parameters:
@@ -324,6 +336,7 @@ class RunningProcesses:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns the matched string.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
@@ -332,7 +345,8 @@ class RunningProcesses:
         assert process_name in self.processes,\
            "Process " + process_name + " unknown"
         return self.processes[process_name].wait_for_stdout_str(strings,
-                                                                only_new)
+                                                                only_new,
+                                                                matches)
 
 @before.each_scenario
 def initialize(scenario):

+ 12 - 0
tests/lettuce/features/xfrin_bind10.feature

@@ -3,7 +3,19 @@ Feature: Xfrin
     
     Scenario: Retransfer command
     Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+    And wait for master stderr message BIND10_STARTED_CC
+    And wait for master stderr message CMDCTL_STARTED
+    And wait for master stderr message AUTH_SERVER_STARTED
+    And wait for master stderr message XFROUT_STARTED
+    And wait for master stderr message ZONEMGR_STARTED
+
     And I have bind10 running with configuration xfrin/retransfer_slave.conf
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message AUTH_SERVER_STARTED
+    And wait for bind10 stderr message XFRIN_STARTED
+    And wait for bind10 stderr message ZONEMGR_STARTED
+
     A query for www.example.org should have rcode REFUSED
     Wait for bind10 stderr message CMDCTL_STARTED
     When I send bind10 the command Xfrin retransfer example.org IN ::1 47807

+ 1 - 1
tests/lettuce/setup_intree_bind10.sh.in

@@ -23,7 +23,7 @@ BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@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:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
-PYTHONPATH=@abs_top_builddir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@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:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
+PYTHONPATH=@abs_top_builddir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@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:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs:$PYTHONPATH
 export PYTHONPATH
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries

+ 17 - 5
tests/system/bindctl/tests.sh

@@ -32,7 +32,18 @@ cnt_value1=0
 cnt_value2=0
 cnt_value3=0
 
-echo "I:Checking b10-auth is working by default ($n)"
+echo "I:Checking b10-auth is disabled by default ($n)"
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A > /dev/null && status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Starting b10-auth and checking that it works ($n)"
+echo 'config add Boss/components b10-auth
+config set Boss/components/b10-auth { "special": "auth", "kind": "needed" }
+config commit
+quit
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
 # perform a simple check on the output (digcomp would be too much for this)
 grep 192.0.2.1 dig.out.$n > /dev/null || status=1
@@ -46,9 +57,9 @@ sleep 2
 echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
-# the server should have received 1 UDP and 1 TCP queries (TCP query was
-# sent from the server startup script)
-cnt_value1=`expr $cnt_value1 + 1`
+# the server should have received 1 UDP and 0 TCP queries (the server
+# startup script no longer sends any TCP queries)
+cnt_value1=`expr $cnt_value1 + 0`
 cnt_value2=`expr $cnt_value2 + 1`
 cnt_value3=`expr $cnt_value1 + $cnt_value2`
 grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
@@ -64,7 +75,7 @@ quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 # dig should exit with a failure code.
-$DIG +tcp +norec @10.53.0.1 -p 53210 ns.example.com. A && status=1
+$DIG +tcp +norec @10.53.0.1 -p 53210 ns.example.com. A > /dev/null && status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
@@ -76,6 +87,7 @@ quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+# perform a simple check on the output (digcomp would be too much for this)
 grep 192.0.2.1 dig.out.$n > /dev/null || status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`

+ 10 - 0
tests/system/glue/nsx1/b10-config.db.in

@@ -2,5 +2,15 @@
  "Auth": {
    "listen_on": [{"address": "10.53.0.1", "port": 53210}],
    "database_file": "@abs_builddir@/zone.sqlite3"
+ },
+ "Boss": {
+   "components": {
+     "b10-auth": {"kind": "needed", "special": "auth" },
+     "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+     "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+     "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+     "b10-stats": { "address": "Stats", "kind": "dispensable" },
+     "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+   }
  }
 }

+ 10 - 0
tests/system/ixfr/b10-config.db.in

@@ -19,5 +19,15 @@
             "name": "example.",
             "class": "IN"
         }]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": {"kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }

+ 5 - 2
tests/system/start.pl

@@ -53,6 +53,8 @@ if ($server && !-d "$test/$server") {
 my $topdir = abs_path("$test/..");
 my $testdir = abs_path("$test");
 my $RUN_BIND10 = $ENV{'RUN_BIND10'};
+my $RUN_BINDCTL = $ENV{'RUN_BINDCTL'};
+my $BINDCTL_CSV_DIR = $ENV{'BINDCTL_CSV_DIR'};
 my $NAMED = $ENV{'BIND9_NAMED'};
 my $LWRESD = $ENV{'LWRESD'};
 my $DIG = $ENV{'DIG'};
@@ -211,14 +213,15 @@ sub verify_server {
 
 	my $tries = 0;
 	while (1) {
-		my $return = system("$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd -p 53210 version.bind. chaos txt \@10.53.0.$n > dig.out");
+		my $return = system("echo \"Stats show\" | $RUN_BINDCTL --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out");
 		last if ($return == 0);
-		print `grep ";" dig.out`;
 		if (++$tries >= 30) {
 			print "I:no response from $server\n";
 			print "R:FAIL\n";
 			system("$PERL $topdir/stop.pl $testdir");
 			exit 1;
+		} else {
+			print "I:no response from $server. retrying.\n";
 		}
 		sleep 2;
 	}