Parcourir la source

[1580] Update NXDOMAIN case to support NSEC3.

Xie Jiagui il y a 13 ans
Parent
commit
263d38ef6a
3 fichiers modifiés avec 93 ajouts et 1 suppressions
  1. 54 1
      src/bin/auth/query.cc
  2. 16 0
      src/bin/auth/query.h
  3. 23 0
      src/bin/auth/tests/query_unittest.cc

+ 54 - 1
src/bin/auth/query.cc

@@ -167,6 +167,55 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     }
 }
 
+// Note: unless the data source client implementation or the zone content
+// is broken, 'nsec3' should be a valid NSEC3 RR.  Likewise, the call to
+// findNSEC3() in this method should result an FindNSEC3Result that proves
+// the Closest Encloser Proof and non existent of matching wildcard.  
+// If these assumptions aren't met due to a buggy data source implementation 
+// or a broken zone, we'll let underlying libdns++ modules throw an exception,
+// which would result in either an SERVFAIL response or just ignoring the query.
+// We at least prevent a complete crash due to such broken behavior.
+void
+Query::addNSEC3NXDOMAINProof(ZoneFinder& finder) {
+    // Firstly get the NSEC3 proves for Closest Encloser Proof
+    const ZoneFinder::FindNSEC3Result fresult1 =  finder.findNSEC3(qname_, true);
+    if (fresult1.closest_proof->getRdataCount() == 0) {
+        isc_throw(BadNSEC3, 
+        "NSEC3 for NXDOMAIN proving that matches the closest encloser is empty.");
+    }
+    // Add the NSEC3 proving that matches the closest (provable) encloser.
+    response_.addRRset(Message::SECTION_AUTHORITY,
+                       boost::const_pointer_cast<AbstractRRset>(fresult1.closest_proof),
+                       dnssec_);
+    // Add the NSEC3 RR that covers the "next closer" name to the closest encloser
+    if (fresult1.next_proof->getRdataCount() == 0) {
+        response_.addRRset(Message::SECTION_AUTHORITY,
+                           boost::const_pointer_cast<AbstractRRset>(fresult1.next_proof), 
+                           dnssec_);
+    }
+    // Next, identify the best possible wildcard name that would match
+    // the query name.  It's the longer common suffix with the qname
+    // between the owner or the next domain of the NSEC that proves NXDOMAIN,
+    // prefixed by the wildcard label, "*".
+    const Name wildname(Name("*").concatenate(qname_.split(1)));
+    const ZoneFinder::FindNSEC3Result fresult2 = finder.findNSEC3(wildname, false);
+    if (fresult2.closest_proof->getRdataCount() == 0) {
+            isc_throw(BadNSEC3, 
+                      "NSEC3 for NXDOMAIN covering the wildcard RR at the closest encloser is empty.");
+    }
+    // Add the (no-) wildcard proof only when it's different from the NSEC
+    // that proves NXDOMAIN; sometimes they can be the same.
+    // Note: name comparison is relatively expensive.  When we are at the
+    // stage of performance optimization, we should consider optimizing this
+    // for some optimized data source implementations.
+    if (fresult1.next_proof->getName() != fresult2.closest_proof->getName()) {
+        response_.addRRset(Message::SECTION_AUTHORITY,
+                           boost::const_pointer_cast<AbstractRRset>(fresult2.closest_proof),
+                           dnssec_);
+        
+    }
+}
+
 void
 Query::addWildcardProof(ZoneFinder& finder) {
     // The query name shouldn't exist in the zone if there were no wildcard
@@ -403,8 +452,12 @@ Query::process() {
         case ZoneFinder::NXDOMAIN:
             response_.setRcode(Rcode::NXDOMAIN());
             addSOA(*result.zone_finder);
-            if (dnssec_ && db_result.rrset) {
+            if (dnssec_ && db_result.isNSECSigned()) {
                 addNXDOMAINProof(zfinder, db_result.rrset);
+                break;
+            }
+            if (dnssec_ && db_result.isNSEC3Signed()) {
+                addNSEC3NXDOMAINProof(zfinder);
             }
             break;
         case ZoneFinder::NXRRSET:

+ 16 - 0
src/bin/auth/query.h

@@ -102,6 +102,11 @@ private:
     /// This corresponds to Section 3.1.3.2 of RFC 4035.
     void addNXDOMAINProof(isc::datasrc::ZoneFinder& finder,
                           isc::dns::ConstRRsetPtr nsec);
+    
+    /// Add NSEC3 RRs that prove an NXDOMAIN result.
+    ///
+    /// This corresponds to Section 7.2.2 of RFC 5155.
+    void addNSEC3NXDOMAINProof(isc::datasrc::ZoneFinder& finder);
 
     /// Add NSEC RRs that prove a wildcard answer is the best one.
     ///
@@ -270,6 +275,17 @@ public:
         {}
     };
 
+    /// An invalid result is given when a valid NSEC3 is expected
+    ///
+    /// This can only happen when the underlying data source implementation or
+    /// the zone is broken.  By throwing an exception we treat such cases
+    /// as SERVFAIL.
+    struct BadNSEC3 : public BadZone {
+        BadNSEC3(const char* file, size_t line, const char* what) :
+            BadZone(file, line, what)
+        {}
+    };
+
     /// An invalid result is given when a valid DS records (or NXRRSET) is
     /// expected
     ///

+ 23 - 0
src/bin/auth/tests/query_unittest.cc

@@ -260,6 +260,9 @@ public:
             "q00jkcevqvmu85r014c7dkba38o0ji5r";
         hash_map_[Name("nxdomain3.example.com")] =
             "009mhaveqvm6t7vbl5lop2u3t2rp3tom";
+        hash_map_[Name("*.example.com")] =
+            "r53bq7cc2uvmubfu5ocmm6pers9tk9en";
+
     }
     virtual isc::dns::Name getOrigin() const { return (origin_); }
     virtual isc::dns::RRClass getClass() const { return (rrclass_); }
@@ -1625,6 +1628,26 @@ TEST_F(QueryTest, findNSEC3) {
                mock_finder->findNSEC3(Name("nxdomain3.example.com"), false));
 }
 
+TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
+    mock_finder->setNSEC3Flag(true);
+    Query(memory_client, Name("nxdomain.example.com"), qtype,
+              response, true).process();
+
+    std::cout<<response.toText()<<std::endl;
+    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
+                  NULL, (string(soa_txt) +
+                         string("example.com. 3600 IN RRSIG ") +
+                         getCommonRRSIGText("SOA") + "\n" +
+                         string(nsec3_apex_txt) + "\n" +
+                         string("0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG ") +
+                         getCommonRRSIGText("NSEC") + "\n" +
+                         string(nsec3_www_txt) + "\n" +
+                         string("q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG ") +
+                         getCommonRRSIGText("NSEC")).c_str(),
+                  NULL, mock_finder->getOrigin());
+
+}
+
 // The following are tentative tests until we really add tests for the
 // query logic for these cases.  At that point it's probably better to
 // clean them up.