Browse Source

[1678] Merge branch 'master' into trac1678

Jelte Jansen 13 years ago
parent
commit
5ba4c6e1f3

+ 18 - 0
ChangeLog

@@ -1,3 +1,21 @@
+386.	[bug]		jelte
+	Upon initial sqlite3 database creation, the 'diffs' table is now
+	always created. This already happened most of the time, but there
+	are a few cases where it was skipped, resulting in potential errors
+	in xfrout later.
+	(Trac #1717, git 30d7686cb6e2fa64866c983e0cfb7b8fabedc7a2)
+
+385.	[bug]		jinmei
+	libdns++: masterLoad() didn't accept comments placed at the end of
+	an RR.  Due to this the in-memory data source cannot load a master
+	file for a signed zone even if it's preprocessed with BIND 9's
+	named-compilezone.
+	Note: this fix is considered temporary and still only accepts some
+	limited form of such comments.  The main purpose is to allow the
+	in-memory data source to load any signed or unsigned zone files as
+	long as they are at least normalized with named-compilezone.
+	(Trac #1667, git 6f771b28eea25c693fe93a0e2379af924464a562)
+
 384.	[func]		jinmei, jelte, vorner, haikuo, kevin
 	b10-auth now supports NSEC3-signed zones in the in-memory data
 	source.

+ 1 - 1
src/lib/datasrc/datasrc_messages.mes

@@ -585,7 +585,7 @@ The underlying data source failed to answer the query for referral information.
 1 means some error, 2 is not implemented. The data source should have logged
 the specific error already.
 
-% DATASRC_QUERY_RRSIG unable to answer RRSIG query
+% DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1
 The server is unable to answer a direct query for RRSIG type, but was asked
 to do so.
 

+ 34 - 17
src/lib/datasrc/memory_datasrc.cc

@@ -605,27 +605,40 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      *
      * If rename is false, it returns the one provided. If it is true, it
      * creates a new rrset with the same data but with provided name.
+     * In addition, if DNSSEC records are required by the original caller of
+     * find(), it also creates expanded RRSIG based on the RRSIG of the
+     * wildcard RRset.
      * It is designed for wildcard case, where we create the rrsets
      * dynamically.
      */
-    static ConstRRsetPtr prepareRRset(const Name& name, const ConstRRsetPtr&
-        rrset, bool rename)
+    static ConstRRsetPtr prepareRRset(const Name& name,
+                                      const ConstRRsetPtr& rrset,
+                                      bool rename, FindOptions options)
     {
         if (rename) {
             LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
                 arg(rrset->getName()).arg(name);
-            /*
-             * We lose a signature here. But it would be wrong anyway, because
-             * the name changed. This might turn out to be unimportant in
-             * future, because wildcards will probably be handled somehow
-             * by DNSSEC.
-             */
             RRsetPtr result(new RRset(name, rrset->getClass(),
-                rrset->getType(), rrset->getTTL()));
+                                      rrset->getType(), rrset->getTTL()));
             for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
-                i->next()) {
+                 i->next()) {
                 result->addRdata(i->getCurrent());
             }
+            if ((options & FIND_DNSSEC) != 0) {
+                ConstRRsetPtr sig_rrset = rrset->getRRsig();
+                if (sig_rrset) {
+                    RRsetPtr result_sig(new RRset(name, sig_rrset->getClass(),
+                                                  RRType::RRSIG(),
+                                                  sig_rrset->getTTL()));
+                    for (RdataIteratorPtr i(sig_rrset->getRdataIterator());
+                         !i->isLast();
+                         i->next())
+                    {
+                        result_sig->addRdata(i->getCurrent());
+                    }
+                    result->addRRsig(result_sig);
+                }
+            }
             return (result);
         } else {
             return (rrset);
@@ -652,7 +665,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
 
     // Implementation of InMemoryZoneFinder::find
     FindResult find(const Name& name, RRType type,
-                    std::vector<ConstRRsetPtr> *target,
+                    std::vector<ConstRRsetPtr>* target,
                     const FindOptions options) const
     {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
@@ -689,14 +702,14 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                     // We were traversing a DNAME node (and wanted to go
                     // lower below it), so return the DNAME
                     return (FindResult(DNAME, prepareRRset(name, state.rrset_,
-                                                           false)));
+                                                           false, options)));
                 }
                 if (state.zonecut_node_ != NULL) {
                     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
                         arg(state.rrset_->getName());
                     return (FindResult(DELEGATION,
                                        prepareRRset(name, state.rrset_,
-                                                    false)));
+                                                    false, options)));
                 }
 
                 // If the RBTree search stopped at a node for a super domain
@@ -800,7 +813,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
                           DATASRC_MEM_EXACT_DELEGATION).arg(name);
                 return (FindResult(DELEGATION,
-                                   prepareRRset(name, found->second, rename)));
+                                   prepareRRset(name, found->second, rename,
+                                                options)));
             }
         }
 
@@ -810,7 +824,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             for (found = node->getData()->begin();
                  found != node->getData()->end(); ++found)
             {
-                target->push_back(prepareRRset(name, found->second, rename));
+                target->push_back(prepareRRset(name, found->second, rename,
+                                               options));
             }
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
                 arg(name);
@@ -824,7 +839,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 arg(type);
             return (createFindResult(SUCCESS, prepareRRset(name,
                                                            found->second,
-                                                           rename), rename));
+                                                           rename, options),
+                                     rename));
         } else {
             // Next, try CNAME.
             found = node->getData()->find(RRType::CNAME());
@@ -832,7 +848,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
                 return (createFindResult(CNAME,
                                          prepareRRset(name, found->second,
-                                                      rename), rename));
+                                                      rename, options),
+                                         rename));
             }
         }
         // No exact match or CNAME.  Return NXRRSET.

+ 8 - 0
src/lib/datasrc/sqlite3_datasrc.cc

@@ -76,6 +76,14 @@ const char* const SCHEMA_LIST[] = {
     "ttl INTEGER NOT NULL, rdtype STRING NOT NULL COLLATE NOCASE, "
     "rdata STRING NOT NULL)",
     "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
+    "CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
+        "zone_id INTEGER NOT NULL, "
+        "version INTEGER NOT NULL, "
+        "operation INTEGER NOT NULL, "
+        "name STRING NOT NULL COLLATE NOCASE, "
+        "rrtype STRING NOT NULL COLLATE NOCASE, "
+        "ttl INTEGER NOT NULL, "
+        "rdata STRING NOT NULL)",
     NULL
 };
 

+ 51 - 7
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -539,6 +539,8 @@ public:
         if (zone_finder == NULL) {
             zone_finder = &zone_finder_;
         }
+        const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
+            RRsetPtr(); // note we use the same type as of retval of getRRsig()
         // The whole block is inside, because we need to check the result and
         // we can't assign to FindResult
         EXPECT_NO_THROW({
@@ -558,6 +560,11 @@ public:
                     } else {
                         ASSERT_TRUE(find_result.rrset);
                         rrsetCheck(answer, find_result.rrset);
+                        if (answer_sig) {
+                            ASSERT_TRUE(find_result.rrset->getRRsig());
+                            rrsetCheck(answer_sig,
+                                       find_result.rrset->getRRsig());
+                        }
                     }
                 } else if (check_wild_answer) {
                     ASSERT_NE(ConstRRsetPtr(), answer) <<
@@ -575,6 +582,22 @@ public:
                         wildanswer->addRdata(expectedIt->getCurrent());
                     }
                     rrsetCheck(wildanswer, find_result.rrset);
+
+                    // Same for the RRSIG, if any.
+                    if (answer_sig) {
+                        ASSERT_TRUE(find_result.rrset->getRRsig());
+
+                        RRsetPtr wildsig(new RRset(name,
+                                                   answer_sig->getClass(),
+                                                   RRType::RRSIG(),
+                                                   answer_sig->getTTL()));
+                        RdataIteratorPtr expectedIt(
+                            answer_sig->getRdataIterator());
+                        for (; !expectedIt->isLast(); expectedIt->next()) {
+                            wildsig->addRdata(expectedIt->getCurrent());
+                        }
+                        rrsetCheck(wildsig, find_result.rrset->getRRsig());
+                    }
                 }
             });
     }
@@ -1079,6 +1102,24 @@ InMemoryZoneFinderTest::wildcardCheck(
      *                 |
      *                 *
      */
+
+    // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
+    // add RRSIGs to the records.
+    ZoneFinder::FindOptions find_options = ZoneFinder::FIND_DEFAULT;
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
+        (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        // Convenience shortcut.  The RDATA is not really validatable, but
+        // it doesn't matter for our tests.
+        const char* const rrsig_common = "5 3 3600 "
+            "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+
+        find_options = find_options | ZoneFinder::FIND_DNSSEC;
+        rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
+                                       string(rrsig_common)));
+        rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
+                                            "RRSIG CNAME " +
+                                            string(rrsig_common)));
+    }
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cnamewild_));
     // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
@@ -1092,14 +1133,15 @@ InMemoryZoneFinderTest::wildcardCheck(
     {
         SCOPED_TRACE("Search at parent");
         findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(), expected_flags);
+                 true, ConstRRsetPtr(), expected_flags, NULL, find_options);
     }
 
     // Search the original name of wildcard
     {
         SCOPED_TRACE("Search directly at *");
         findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
-                 true, rr_wild_);
+                 true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
+                 find_options);
     }
     // Search "created" name.
     {
@@ -1107,11 +1149,12 @@ InMemoryZoneFinderTest::wildcardCheck(
         findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
                  false, rr_wild_,
                  ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                 ZoneFinder::FIND_DEFAULT, true);
+                 find_options, true);
         // Wildcard match, but no data
         findTest(Name("a.wild.example.org"), RRType::AAAA(),
                  ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
-                 ZoneFinder::RESULT_WILDCARD | expected_flags);
+                 ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+                 find_options);
     }
 
     // Search name that has CNAME.
@@ -1120,7 +1163,7 @@ InMemoryZoneFinderTest::wildcardCheck(
         findTest(Name("a.cnamewild.example.org"), RRType::A(),
                  ZoneFinder::CNAME, false, rr_cnamewild_,
                  ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                 ZoneFinder::FIND_DEFAULT, true);
+                 find_options, true);
     }
 
     // Search another created name, this time little bit lower
@@ -1129,14 +1172,15 @@ InMemoryZoneFinderTest::wildcardCheck(
         findTest(Name("a.b.wild.example.org"), RRType::A(),
                  ZoneFinder::SUCCESS, false, rr_wild_,
                  ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                 ZoneFinder::FIND_DEFAULT, true);
+                 find_options, true);
     }
 
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
     {
         SCOPED_TRACE("Search under non-wildcard");
         findTest(Name("bar.foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
+                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags,
+                 NULL, find_options);
     }
 }
 

+ 34 - 1
src/lib/dns/masterload.cc

@@ -37,6 +37,30 @@ using namespace isc::dns::rdata;
 
 namespace isc {
 namespace dns {
+namespace {
+// A helper function that strips off any comment placed at the end of an RR.
+// This is an incomplete implementation, and cannot handle all such comments;
+// it's considered a short term workaround to deal with some real world
+// cases.
+string
+stripComment(string& s, const Exception& ex) {
+    // Find any ';' in the text data, and locate the position of the last
+    // occurrence.  Note that unless/until we support empty RDATA it
+    // shouldn't be placed at the beginning of the data.
+    const size_t pos_semicolon = s.rfind(';');
+    if (pos_semicolon == string::npos || pos_semicolon == 0) {
+        throw ex;
+    }
+    // Remove any trailing space and comments and return the resulting text.
+    const size_t pos_end_data = s.find_last_not_of(" /t", pos_semicolon - 1);
+    if (pos_end_data != string::npos) {
+        s.erase(pos_end_data + 1);
+        return (s);
+    }
+    throw ex;
+}
+}
+
 void
 masterLoad(const char* const filename, const Name& origin,
            const RRClass& zone_class, MasterLoadCallback callback)
@@ -116,7 +140,16 @@ masterLoad(istream& input, const Name& origin, const RRClass& zone_class,
             ttl.reset(new RRTTL(ttl_txt));
             rrclass.reset(new RRClass(rrclass_txt));
             rrtype.reset(new RRType(rrtype_txt));
-            rdata = createRdata(*rrtype, *rrclass, rdatabuf.str());
+            string rdtext = rdatabuf.str();
+            try {
+                rdata = createRdata(*rrtype, *rrclass, rdtext);
+            } catch (const Exception& ex) {
+                // If the parse for the RDATA fails, check if it has comments
+                // at the end, and if so, retry the conversion after stripping
+                // off the comment.
+                rdata = createRdata(*rrtype, *rrclass, stripComment(rdtext,
+                                                                    ex));
+            }
         } catch (const Exception& ex) {
             isc_throw(MasterLoadError, "Invalid RR text at line " << line_count
                       << ": " << ex.what());

+ 50 - 0
src/lib/dns/tests/masterload_unittest.cc

@@ -25,6 +25,7 @@
 
 #include <dns/masterload.h>
 #include <dns/name.h>
+#include <dns/rdata.h>
 #include <dns/rrclass.h>
 #include <dns/rrset.h>
 
@@ -80,6 +81,11 @@ const char* const rrsig_rr2 =
     "www.example.com. 60 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
     "12345 example.com. FAKEFAKEFAKE\n";
 
+// Commonly used for some tests to check the constructed RR content.
+const char* const dnskey_rdata =
+    "256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+    "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=\n";
+
 TEST_F(MasterLoadTest, loadRRs) {
     // a simple case: loading 3 RRs, each consists of a single RRset.
     rr_stream << txt_rr << a_rr1 << soa_rr;
@@ -161,6 +167,50 @@ TEST_F(MasterLoadTest, loadRRsigs) {
     EXPECT_EQ(2, results.size());
 }
 
+TEST_F(MasterLoadTest, loadRRWithComment) {
+    // Comment at the end of line should be ignored and the RR should be
+    // accepted.
+    rr_stream << "example.com. 3600 IN DNSKEY	256 3 7 "
+        "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+        "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=  ; key id = 40430\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::DNSKEY(), zclass,
+                                      dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentNoSpace) {
+    // Similar to the previous one, but there's no space before comments.
+    // It should still work.
+    rr_stream << "example.com. 3600 IN DNSKEY	256 3 7 "
+        "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+        "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=; key id = 40430\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::DNSKEY(), zclass,
+                                      dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRNoComment) {
+    // A semicolon in a character-string shouldn't confuse the parser.
+    rr_stream << "example.com. 3600 IN TXT \"aaa;bbb\"\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    EXPECT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::TXT(), zclass,
+                                      "\"aaa;bbb\"")));
+}
+
+TEST_F(MasterLoadTest, loadRREmptyAndComment) {
+    // There's no RDATA (invalid in this case) but a comment.  This position
+    // shouldn't cause any disruption and should be treated as a normal error.
+    rr_stream << "example.com. 3600 IN A ;\n";
+    EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback),
+                 MasterLoadError);
+}
+
 TEST_F(MasterLoadTest, loadWithNoEOF) {
     // the input stream doesn't end with a new line (and the following blank
     // line).  It should be accepted.

+ 0 - 9
src/lib/python/isc/log/Makefile.am

@@ -23,15 +23,6 @@ log_la_LIBADD += $(PYTHON_LIB)
 # This is not installed, it helps locate the module during tests
 EXTRA_DIST = __init__.py
 
-# We're going to abuse install-data-local for a pre-install check.
-# This is to be considered a short term hack and is expected to be removed
-# in a near future version.
-install-data-local:
-	if test -d @pyexecdir@/isc/log; then \
-		echo "@pyexecdir@/isc/log is deprecated, and will confuse newer versions.  Please (re)move it by hand."; \
-		exit 1; \
-	fi
-
 pytest:
 	$(SHELL) tests/log_test
 

+ 2 - 2
src/lib/testutils/testdata/rfc5155-example.zone.signed

@@ -7,8 +7,8 @@ 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 MX	1 xx.example.
 example.				      3600 IN RRSIG	MX 7 1 3600 20150420235959 20051021000000 40430 example. GgQ1A9xs47k42VPvpL/a1BWUz/6XsnHkjotw9So8MQtZtl2wJBsnOQsa oHrRCrRbyriEl/GZn9Mto/Kx+wBo+w==
-example.				      3600 IN DNSKEY	256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=
-example.				      3600 IN DNSKEY	257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=
+example.				      3600 IN DNSKEY	256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=  ; key id = 40430
+example.				      3600 IN DNSKEY	257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=  ; key id = 12708
 example.				      3600 IN RRSIG	DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31uzMZ/U/FpsUb8aC6QZS+ sTsJXnLnz7flGOsmMGQZf3bH+QsCtg==
 example.				      3600 IN NSEC3PARAM 1 0 12 AABBCCDD
 example.				      3600 IN RRSIG	NSEC3PARAM 7 1 3600 20150420235959 20051021000000 40430 example. C1Gl8tPZNtnjlrYWDeeUV/sGLCyy/IHie2rerN05XSA3Pq0U3+4VvGWY WdUMfflOdxqnXHwJTLQsjlkynhG6Cg==

+ 7 - 1
src/lib/util/io/fd_share.cc

@@ -20,6 +20,7 @@
 #include <sys/uio.h>
 #include <errno.h>
 #include <stdlib.h>             // for malloc and free
+#include <unistd.h>
 #include "fd_share.h"
 
 namespace isc {
@@ -106,7 +107,12 @@ recv_fd(const int sock) {
         std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
     }
     free(msghdr.msg_control);
-    return (fd);
+    // It is strange, but the call can return the same file descriptor as
+    // one returned previously, even if that one is not closed yet. So,
+    // we just re-number every one we get, so they are unique.
+    int new_fd(dup(fd));
+    close(fd);
+    return (new_fd);
 }
 
 int

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

@@ -0,0 +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"}}}}

+ 72 - 0
tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed

@@ -0,0 +1,72 @@
+;; The example NSEC3-signed zone used in RFC5155.
+
+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==
+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 MX	1 xx.example.
+example.				      3600 IN RRSIG	MX 7 1 3600 20150420235959 20051021000000 40430 example. GgQ1A9xs47k42VPvpL/a1BWUz/6XsnHkjotw9So8MQtZtl2wJBsnOQsa oHrRCrRbyriEl/GZn9Mto/Kx+wBo+w==
+example.				      3600 IN DNSKEY	256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=
+example.				      3600 IN DNSKEY	257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=
+example.				      3600 IN RRSIG	DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31uzMZ/U/FpsUb8aC6QZS+ sTsJXnLnz7flGOsmMGQZf3bH+QsCtg==
+example.				      3600 IN NSEC3PARAM 1 0 12 AABBCCDD
+example.				      3600 IN RRSIG	NSEC3PARAM 7 1 3600 20150420235959 20051021000000 40430 example. C1Gl8tPZNtnjlrYWDeeUV/sGLCyy/IHie2rerN05XSA3Pq0U3+4VvGWY WdUMfflOdxqnXHwJTLQsjlkynhG6Cg==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.     3600 IN A		192.0.2.127
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.     3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. h6c++bzhRuWWt2bykN6mjaTNBcXNq5UuL5EdK+iDP4eY8I0kSiKaCjg3 tC1SQkeloMeub2GWk8p6xHMPZumXlw==
+a.example.				      3600 IN NS	ns1.a.example.
+a.example.				      3600 IN NS	ns2.a.example.
+a.example.				      3600 IN DS	58470 5 1 3079F1593EBAD6DC121E202A8B766A6A4837206C
+a.example.				      3600 IN RRSIG	DS 7 2 3600 20150420235959 20051021000000 40430 example. XacFcQVHLVzdoc45EJhN616zQ4mEXtE8FzUhM2KWjfy1VfRKD9r1MeVG wwoukOKgJxBPFsWoo722vZ4UZ2dIdA==
+ns1.a.example.				      3600 IN A		192.0.2.5
+ns2.a.example.				      3600 IN A		192.0.2.6
+ai.example.				      3600 IN A		192.0.2.9
+ai.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+ai.example.				      3600 IN HINFO	"KLH-10" "ITS"
+ai.example.				      3600 IN RRSIG	HINFO 7 2 3600 20150420235959 20051021000000 40430 example. Yi42uOq43eyO6qXHNvwwfFnIustWgV5urFcxenkLvs6pKRh00VBjODmf 3Z4nMO7IOl6nHSQ1v0wLHpEZG7Xj2w==
+ai.example.				      3600 IN AAAA	2001:db8::f00:baa9
+ai.example.				      3600 IN RRSIG	AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+c.example.				      3600 IN NS	ns1.c.example.
+c.example.				      3600 IN NS	ns2.c.example.
+ns1.c.example.				      3600 IN A		192.0.2.7
+ns2.c.example.				      3600 IN A		192.0.2.8
+ns1.example.				      3600 IN A		192.0.2.1
+ns1.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+ns2.example.				      3600 IN A		192.0.2.2
+ns2.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+*.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==
+x.w.example.				      3600 IN MX	1 xx.example.
+x.w.example.				      3600 IN RRSIG	MX 7 3 3600 20150420235959 20051021000000 40430 example. IrK3tq/tHFIBF0scHiE/1IwMAvckS/55hAVvQyxTFbkAdDloP3NbZzu+ yoSsr3b3OX6qbBpY7WCtwwekLKRAwQ==
+x.y.w.example.				      3600 IN MX	1 xx.example.
+x.y.w.example.				      3600 IN RRSIG	MX 7 4 3600 20150420235959 20051021000000 40430 example. MqSt5HqJIN8+SLlzTOImrh5h9Xa6gDvAW/GnnbdPc6Z7nXvCpLPJj/5l Cwx3VuzVOjkbvXze8/8Ccl2Zn2hbug==
+xx.example.				      3600 IN A		192.0.2.10
+xx.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. T35hBWEZ017VC5u2c4OriKyVn/pu+fVK4AlXYOxJ6iQylfV2HQIKjv6b 7DzINB3aF/wjJqgXpQvhq+Ac6+ZiFg==
+xx.example.				      3600 IN HINFO	"KLH-10" "TOPS-20"
+xx.example.				      3600 IN RRSIG	HINFO 7 2 3600 20150420235959 20051021000000 40430 example. KimG+rDd+7VA1zRsu0ITNAQUTRlpnsmqWrihFRnU+bRa93v2e5oFNFYC s3Rqgv62K93N7AhW6Jfqj/8NzWjvKg==
+xx.example.				      3600 IN AAAA	2001:db8::f00:baaa
+xx.example.				      3600 IN RRSIG	AAAA 7 2 3600 20150420235959 20051021000000 40430 example. IXBcXORITNwd8h3gNwyxtYFvAupS/CYWufVeuBUX0O25ivBCULjZjpDx FSxfohb/KA7YRdxENzYfMItpILl/Xw==
+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==
+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==
+2vptu5timamqttgl4luu9kg21e0aor3s.example.     3600 IN NSEC3	1 1 12 AABBCCDD 35MTHGPGCU1QG68FAB165KLNSNK3DPVL MX RRSIG
+2vptu5timamqttgl4luu9kg21e0aor3s.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. KL1V2oFYghNV0Hm7Tf2vpJjM6l+0g1JCcVYGVfI0lKrhPmTsOA96cLEA Cgo1x8I7kApJX+obTuktZ+sdsZPY1w==
+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==
+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==
+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==
+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==
+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==
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example.     3600 IN NSEC3	1 1 12 AABBCCDD Q04JKCEVQVMU85R014C7DKBA38O0JI5R A RRSIG
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. VrDXs2uVW21N08SyQIz88zml+y4ZCInTwgDr6zz43yAg+LFERjOrj3Oj ct51ac7Dp4eZbf9FQJazmASFKGxGXg==
+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==
+t644ebqk9bibcna874givr6joj62mlhv.example.     3600 IN NSEC3	1 1 12 AABBCCDD 0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM A HINFO AAAA RRSIG
+t644ebqk9bibcna874givr6joj62mlhv.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. RAjGECB8P7O+F4Pa4Dx3tC0M+Z3KmlLKImcafb9XWwx+NWUNz7NBEDBQ HivIyKPVDkChcePIX1xPl1ATNa+8Dw==

+ 302 - 0
tests/lettuce/features/nsec3_auth.feature

@@ -0,0 +1,302 @@
+Feature: NSEC3 Authoritative service
+    This feature tests NSEC3 as defined in RFC5155, using the example
+    zone from appendix A and testing the example responses from appendix B.
+    Additional tests can be added as well.
+
+    # Response section data is taken directly from RFC5155
+    # It has been modified slightly; it has been 'flattened' (i.e. converted
+    # to 1-line RRs with TTL and class data), and whitespace has been added
+    # in the places where dig adds them too.
+    # Any other changes from the specific example data are added as inline
+    # comments.
+
+    Scenario: B.1. Name Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 8
+        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==
+        """
+
+    Scenario: B.2. No Data Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        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==
+        """
+
+    Scenario: B2.1. No Data Error, Empty Non-Terminal
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        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==
+        """
+
+    Scenario: B.3. Referral to an Opt-Out Unsigned Zone
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 6
+        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==
+        """
+        The additional section of the last query response should be
+        """
+        ns1.c.example. 3600 IN A       192.0.2.7
+        ns2.c.example. 3600 IN A       192.0.2.8
+        """
+
+    Scenario: B.4. Wildcard Expansion
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 2
+        The last query response should have nscount 5
+        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==
+        """
+        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==
+        """
+        # 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==
+        """
+
+    Scenario: B.5. Wildcard No Data Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 8
+        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==
+        """
+
+    Scenario: B.6. DS Child Zone No Data Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        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==
+        """
+
+    #
+    # Below are additional tests, not explicitely stated in RFC5155
+    #
+
+    # THIS TEST CURRENTLY FAILS: An NSEC3 record is added twice
+    # See ticket #1688
+    #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
+    #    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
+    #    The last query response should have ancount 0
+    #    The last query response should have nscount 6
+    #    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==
+    #    """
+
+    # THIS TEST CURRENTLY FAILS: An NSEC3 record is added twice
+    # See ticket #1688
+    #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
+    #    A dnssec query for a.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
+    #    The last query response should have ancount 0
+    #    The last query response should have nscount 6
+    #    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==
+    #    """
+
+    Scenario: Wildcard other: Wildcard name itself
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 2
+        The last query response should have nscount 3
+        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==
+        """
+        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==
+        """
+        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==
+        """
+
+    Scenario: Wildcard other: Wildcard name itself nodata
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        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==
+        """
+
+    Scenario: Direct query for NSEC3 record
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 8
+        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==
+        """
+
+    Scenario: No data, type DS, in-zone
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        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==
+        """
+
+    Scenario: No data, type DS, optout delegation
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        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
+        The last query response should have ancount 0
+        The last query response should have nscount 6
+        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==
+        """

+ 63 - 14
tests/lettuce/features/terrain/querying.py

@@ -41,9 +41,10 @@ import re
 #
 # The following attributes are 'parsed' from the response, all as strings,
 # and end up as direct attributes of the QueryResult object:
-# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount
-# (flags is one string with all flags, in the order they appear in the
-# response packet.)
+# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount,
+# edns_version, edns_flags, and edns_udp_size
+# (flags and edns_flags are both one string with all flags, in the order
+# in which they appear in the response message.)
 #
 # this will set 'rcode' as the result code, we 'define' one additional
 # rcode, "NO_ANSWER", if the dig process returned an error code itself
@@ -55,10 +56,12 @@ import re
 # See server_from_sqlite3.feature for various examples to perform queries
 class QueryResult(object):
     status_re = re.compile("opcode: ([A-Z])+, status: ([A-Z]+), id: ([0-9]+)")
+    edns_re = re.compile("; EDNS: version: ([0-9]+), flags: ([a-z ]*); udp: ([0-9]+)")
     flags_re = re.compile("flags: ([a-z ]+); QUERY: ([0-9]+), ANSWER: " +
                           "([0-9]+), AUTHORITY: ([0-9]+), ADDITIONAL: ([0-9]+)")
 
-    def __init__(self, name, qtype, qclass, address, port):
+    def __init__(self, name, qtype, qclass, address, port,
+                 additional_args=None):
         """
         Constructor. This fires of a query using dig.
         Parameters:
@@ -67,6 +70,7 @@ class QueryResult(object):
         qclass: The RR class to query. Defaults to IN if it is None.
         address: The IP adress to send the query to.
         port: The port number to send the query to.
+        additional_args: List of additional arguments (e.g. '+dnssec').
         All parameters must be either strings or have the correct string
         representation.
         Only one query attempt will be made.
@@ -78,6 +82,8 @@ class QueryResult(object):
         if qclass is not None:
             args.append('-c')
             args.append(str(qclass))
+        if additional_args is not None:
+            args.extend(additional_args)
         args.append(name)
         dig_process = subprocess.Popen(args, 1, None, None, subprocess.PIPE,
                                        None)
@@ -102,6 +108,8 @@ class QueryResult(object):
         """
         if line == ";; ANSWER SECTION:\n":
             self.line_handler = self.parse_answer
+        elif line == ";; OPT PSEUDOSECTION:\n":
+            self.line_handler = self.parse_opt
         elif line == ";; AUTHORITY SECTION:\n":
             self.line_handler = self.parse_authority
         elif line == ";; ADDITIONAL SECTION:\n":
@@ -131,6 +139,19 @@ class QueryResult(object):
                 self.nscount = flags_match.group(4)
                 self.adcount = flags_match.group(5)
 
+    def parse_opt(self, line):
+        """
+        Parse the header lines of the query response.
+        Parameters:
+        line: The current line of the response.
+        """
+        if not self._check_next_header(line):
+            edns_match = self.edns_re.search(line)
+            if edns_match is not None:
+                self.edns_version = edns_match.group(1)
+                self.edns_flags = edns_match.group(2)
+                self.edns_udp_size = edns_match.group(3)
+
     def parse_question(self, line):
         """
         Parse the question section lines of the query response.
@@ -179,9 +200,10 @@ class QueryResult(object):
         """
         pass
 
-@step('A query for ([\w.-]+) (?:type ([A-Z0-9]+) )?(?:class ([A-Z]+) )?' +
-      '(?:to ([^:]+)(?::([0-9]+))? )?should have rcode ([\w.]+)')
-def query(step, query_name, qtype, qclass, addr, port, rcode):
+@step('A (dnssec )?query for ([\S]+) (?:type ([A-Z0-9]+) )?' +
+      '(?:class ([A-Z]+) )?(?:to ([^:]+)(?::([0-9]+))? )?' +
+      'should have rcode ([\w.]+)')
+def query(step, dnssec, query_name, qtype, qclass, addr, port, rcode):
     """
     Run a query, check the rcode of the response, and store the query
     result in world.last_query_result.
@@ -203,7 +225,11 @@ def query(step, query_name, qtype, qclass, addr, port, rcode):
         addr = "127.0.0.1"
     if port is None:
         port = 47806
-    query_result = QueryResult(query_name, qtype, qclass, addr, port)
+    additional_arguments = []
+    if dnssec is not None:
+        additional_arguments.append("+dnssec")
+    query_result = QueryResult(query_name, qtype, qclass, addr, port,
+                               additional_arguments)
     assert query_result.rcode == rcode,\
         "Expected: " + rcode + ", got " + query_result.rcode
     world.last_query_result = query_result
@@ -255,9 +281,15 @@ def check_last_query_section(step, section):
     section ('<section> section'): The name of the section (QUESTION, ANSWER,
                                    AUTHORITY or ADDITIONAL).
     The expected response is taken from the multiline part of the step in the
-    scenario. Differing whitespace is ignored, but currently the order is
-    significant.
+    scenario. Differing whitespace is ignored, the order of the lines is
+    ignored, and the comparison is case insensitive.
     Fails if they do not match.
+    WARNING: Case insensitivity is not strictly correct; for instance the
+    data of TXT RRs would be case sensitive. But most other output is, so
+    currently the checks are always case insensitive. Should we decide
+    these checks do need to be case sensitive, we can either remove it
+    or make it optional (for the former, we'll need to update a number of
+    tests).
     """
     response_string = None
     if section.lower() == 'question':
@@ -265,15 +297,32 @@ def check_last_query_section(step, section):
     elif section.lower() == 'answer':
         response_string = "\n".join(world.last_query_result.answer_section)
     elif section.lower() == 'authority':
-        response_string = "\n".join(world.last_query_result.answer_section)
+        response_string = "\n".join(world.last_query_result.authority_section)
     elif section.lower() == 'additional':
-        response_string = "\n".join(world.last_query_result.answer_section)
+        response_string = "\n".join(world.last_query_result.additional_section)
     else:
         assert False, "Unknown section " + section
+
+    # Now mangle the data for 'conformance'
+    # This could be done more efficiently, but is done one
+    # by one on a copy of the original data, so it is clear
+    # what is done. Final error output is currently still the
+    # original unchanged multiline strings
+
     # replace whitespace of any length by one space
     response_string = re.sub("[ \t]+", " ", response_string)
     expect = re.sub("[ \t]+", " ", step.multiline)
+    # lowercase them
+    response_string = response_string.lower()
+    expect = expect.lower()
+    # sort them
+    response_string_parts = response_string.split("\n")
+    response_string_parts.sort()
+    response_string = "\n".join(response_string_parts)
+    expect_parts = expect.split("\n")
+    expect_parts.sort()
+    expect = "\n".join(expect_parts)
+
     assert response_string.strip() == expect.strip(),\
         "Got:\n'" + response_string + "'\nExpected:\n'" + step.multiline +"'"
-    
-    
+