Browse Source

[master] Merge remote-tracking branch 'origin/trac1306'

JINMEI Tatuya 13 years ago
parent
commit
f8c76dbe97
2 changed files with 96 additions and 6 deletions
  1. 6 3
      src/bin/auth/query.cc
  2. 90 3
      src/bin/auth/tests/query_unittest.cc

+ 6 - 3
src/bin/auth/query.cc

@@ -310,15 +310,18 @@ Query::process() {
             case ZoneFinder::NXDOMAIN:
                 response_.setRcode(Rcode::NXDOMAIN());
                 addSOA(*result.zone_finder);
-
-                // If DNSSEC proof is requested and we've got it, add it.
                 if (dnssec_ && db_result.rrset) {
                     addNXDOMAINProof(zfinder, db_result.rrset);
                 }
                 break;
             case ZoneFinder::NXRRSET:
-                // Just empty answer with SOA in authority section
                 addSOA(*result.zone_finder);
+                if (dnssec_ && db_result.rrset) {
+                    response_.addRRset(Message::SECTION_AUTHORITY,
+                                       boost::const_pointer_cast<RRset>(
+                                           db_result.rrset),
+                                       dnssec_);
+                }
                 break;
             default:
                 // These are new result codes (WILDCARD and WILDCARD_NXRRSET)

+ 90 - 3
src/bin/auth/tests/query_unittest.cc

@@ -121,7 +121,14 @@ const char* const nz_txt =
 const char* const nsec_nz_txt =
     "nz.no.example.com. 3600 IN NSEC noglue.example.com. AAAA NSEC RRSIG\n";
 const char* const nsec_nxdomain_txt =
-    "noglue.example.com. 3600 IN NSEC www.example.com. A\n";
+    "noglue.example.com. 3600 IN NSEC nonsec.example.com. A\n";
+
+// NSEC for the normal NXRRSET case
+const char* const nsec_www_txt =
+    "www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG\n";
+
+// Authoritative data without NSEC
+const char* const nonsec_a_txt = "nonsec.example.com. 3600 IN A 192.0.2.0\n";
 
 // A helper function that generates a textual representation of RRSIG RDATA
 // for the given covered type.  The resulting RRSIG may not necessarily make
@@ -163,7 +170,7 @@ public:
             cname_nxdom_txt << cname_out_txt << dname_txt << dname_a_txt <<
             other_zone_rrs << no_txt << nz_txt <<
             nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
-            nsec_nxdomain_txt;
+            nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt;
 
         masterLoad(zone_stream, origin_, rrclass_,
                    boost::bind(&MockZoneFinder::loadRRset, this, _1));
@@ -324,10 +331,41 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         }
 
         // Otherwise it's NXRRSET case.
+        if ((options & FIND_DNSSEC) != 0) {
+            found_rrset = found_domain->second.find(RRType::NSEC());
+            if (found_rrset != found_domain->second.end()) {
+                return (FindResult(NXRRSET, found_rrset->second));
+            }
+        }
+        return (FindResult(NXRRSET, RRsetPtr()));
+    }
+
+    // query name isn't found in our domains.
+    // We first check if the query name is an empty non terminal name
+    // of the zone by naive linear search.
+    Domains::const_iterator domain;
+    for (domain = domains_.begin(); domain != domains_.end(); ++domain) {
+        if (name.compare((*domain).first).getRelation() ==
+            NameComparisonResult::SUPERDOMAIN) {
+            break;
+        }
+    }
+    if (domain != domains_.end()) {
+        // The query name is in an empty non terminal node followed by 'domain'
+        // (for simplicity we ignore the pathological case of 'domain' is
+        // the origin of the zone)
+        --domain;               // reset domain to the "previous name"
+        if ((options & FIND_DNSSEC) != 0) {
+            RRsetStore::const_iterator found_rrset =
+                (*domain).second.find(RRType::NSEC());
+            if (found_rrset != (*domain).second.end()) {
+                return (FindResult(NXRRSET, found_rrset->second));
+            }
+        }
         return (FindResult(NXRRSET, RRsetPtr()));
     }
 
-    // query name isn't found in our domains.  This is an NXDOMAIN case.
+    // This is an NXDOMAIN case.
     // If we need DNSSEC proof, find the "previous name" that has an NSEC RR
     // and return NXDOMAIN with the found NSEC.  Otherwise, just return the
     // NXDOMAIN code and NULL.  If DNSSEC proof is requested but no NSEC is
@@ -717,6 +755,55 @@ TEST_F(QueryTest, nxrrset) {
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
 
+TEST_F(QueryTest, nxrrsetWithNSEC) {
+    // NXRRSET with DNSSEC proof.  We should have SOA, NSEC that proves the
+    // NXRRSET and their RRSIGs.
+    Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
+          true).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                   getCommonRRSIGText("SOA") + "\n" +
+                   string(nsec_www_txt) + "\n" +
+                   string("www.example.com. 3600 IN RRSIG ") +
+                   getCommonRRSIGText("NSEC")).c_str(),
+                  NULL, mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, emptyNameWithNSEC) {
+    // Empty non terminal with DNSSEC proof.  This is one of the cases of
+    // Section 3.1.3.2 of RFC4035.
+    // mx.example.com. NSEC ).no.example.com. proves no.example.com. is a
+    // non empty terminal node.  Note that it also implicitly proves there
+    // should be no closer wildcard match (because the empty name is an
+    // exact match), so we only need one NSEC.
+    // From the point of the Query::process(), this is actually no different
+    // from the other NXRRSET case, but we check that explicitly just in case.
+    Query(memory_client, Name("no.example.com"), RRType::A(), response,
+          true).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                   getCommonRRSIGText("SOA") + "\n" +
+                   string(nsec_mx_txt) + "\n" +
+                   string("mx.example.com. 3600 IN RRSIG ") +
+                   getCommonRRSIGText("NSEC")).c_str(),
+                  NULL, mock_finder->getOrigin());
+}
+
+TEST_F(QueryTest, nxrrsetWithoutNSEC) {
+    // NXRRSET with DNSSEC proof requested, but there's no NSEC at that node.
+    // This is an unexpected event (if the zone is supposed to be properly
+    // signed with NSECs), but we accept and ignore the oddity.
+    Query(memory_client, Name("nonsec.example.com"), RRType::TXT(), response,
+          true).process();
+
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
+                  (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                   getCommonRRSIGText("SOA") + "\n").c_str(),
+                  NULL, mock_finder->getOrigin());
+}
+
 /*
  * This tests that when there's no SOA and we need a negative answer. It should
  * throw in that case.