Browse Source

[master] Merge branch 'trac1578'

JINMEI Tatuya 13 years ago
parent
commit
03a80ffa46

+ 6 - 4
src/lib/datasrc/datasrc_messages.mes

@@ -376,10 +376,12 @@ Some resource types are singletons -- only one is allowed in a domain
 % DATASRC_MEM_SUCCESS query for '%1/%2' successful
 Debug information. The requested record was found.
 
-% DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty
-Debug information. The search stopped at a superdomain of the requested
-domain. The domain is an empty nonterminal, therefore it is treated  as NXRRSET
-case (eg. the domain exists, but it doesn't have the requested record type).
+% DATASRC_MEM_SUPER_STOP stopped as '%1' is superdomain of a zone node, meaning it's empty
+Debug information. The search stopped because the requested domain was
+detected to be a superdomain of some existing node of zone (while there
+was no exact match).  This means that the domain is an empty nonterminal,
+therefore it is treated  as NXRRSET case (eg. the domain exists, but it
+doesn't have the requested record type).
 
 % DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')
 Debug information. The contents of two in-memory zones are being exchanged.

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

@@ -626,6 +626,24 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         }
     }
 
+    // Set up FindResult object as a return value of find(), taking into
+    // account wildcard matches and DNSSEC information.  We set the NSEC/NSEC3
+    // flag when applicable regardless of the find option; the caller would
+    // simply ignore these when they didn't request DNSSEC related results.
+    FindResult createFindResult(Result code, ConstRRsetPtr rrset,
+                                bool wild) const
+    {
+        FindResultFlags flags = RESULT_DEFAULT;
+        if (wild) {
+            flags = flags | RESULT_WILDCARD;
+        }
+        if ((code == NXRRSET || code == NXDOMAIN || wild) &&
+            zone_data_->nsec3_data_) {
+            flags = flags | RESULT_NSEC3_SIGNED;
+        }
+        return (FindResult(code, rrset, flags));
+    }
+
     // Implementation of InMemoryZoneFinder::find
     FindResult find(const Name& name, RRType type,
                     std::vector<ConstRRsetPtr> *target,
@@ -665,13 +683,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_,
-                        rename)));
+                                                           false)));
                 }
                 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_, rename)));
+                    return (FindResult(DELEGATION,
+                                       prepareRRset(name, state.rrset_,
+                                                    false)));
                 }
 
                 // If the RBTree search stopped at a node for a super domain
@@ -680,8 +699,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 if (node_path.getLastComparisonResult().getRelation() ==
                     NameComparisonResult::SUPERDOMAIN) {
                     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
-                        arg(node_path.getAbsoluteName()).arg(name);
-                    return (FindResult(NXRRSET, ConstRRsetPtr()));
+                        arg(name);
+                    return (createFindResult(NXRRSET, ConstRRsetPtr(), false));
                 }
 
                 /*
@@ -720,9 +739,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                         getLastComparisonResult().getCommonLabels() > 1) {
                         LOG_DEBUG(logger, DBG_TRACE_DATA,
                                      DATASRC_MEM_WILDCARD_CANCEL).arg(name);
-                        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+                        return (createFindResult(NXDOMAIN, ConstRRsetPtr(),
+                                                 false));
                     }
-                    Name wildcard(Name("*").concatenate(
+                    const Name wildcard(Name("*").concatenate(
                         node_path.getAbsoluteName()));
                     DomainTree::Result result =
                         zone_data_->domains_.find(wildcard, &node);
@@ -745,7 +765,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             case DomainTree::NOTFOUND:
                 LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
                     arg(name);
-                return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+                return (createFindResult(NXDOMAIN, ConstRRsetPtr(), false));
             case DomainTree::EXACTMATCH: // This one is OK, handle it
                 break;
             default:
@@ -758,7 +778,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         if (node->isEmpty()) {
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
                 arg(name);
-            return (FindResult(NXRRSET, ConstRRsetPtr()));
+            return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
         }
 
         Domain::const_iterator found;
@@ -773,8 +793,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             if (found != node->getData()->end()) {
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
                           DATASRC_MEM_EXACT_DELEGATION).arg(name);
-                return (FindResult(DELEGATION, prepareRRset(name,
-                    found->second, rename)));
+                return (FindResult(DELEGATION,
+                                   prepareRRset(name, found->second, rename)));
             }
         }
 
@@ -788,7 +808,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             }
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
                 arg(name);
-            return (FindResult(SUCCESS, ConstRRsetPtr()));
+            return (createFindResult(SUCCESS, ConstRRsetPtr(), rename));
         }
 
         found = node->getData()->find(type);
@@ -796,21 +816,23 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             // Good, it is here
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
                 arg(type);
-            return (FindResult(SUCCESS, prepareRRset(name, found->second,
-                rename)));
+            return (createFindResult(SUCCESS, prepareRRset(name,
+                                                           found->second,
+                                                           rename), rename));
         } else {
             // Next, try CNAME.
             found = node->getData()->find(RRType::CNAME());
             if (found != node->getData()->end()) {
                 LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
-                return (FindResult(CNAME, prepareRRset(name, found->second,
-                    rename)));
+                return (createFindResult(CNAME,
+                                         prepareRRset(name, found->second,
+                                                      rename), rename));
             }
         }
         // No exact match or CNAME.  Return NXRRSET.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
             arg(name);
-        return (FindResult(NXRRSET, ConstRRsetPtr()));
+        return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
     }
 };
 

+ 289 - 123
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -283,6 +283,25 @@ class InMemoryZoneFinderTest : public ::testing::Test {
         const char* const text; // textual representation of an RRset
         RRsetPtr* rrset;
     };
+protected:
+    // The following sub tests are shared by multiple test cases, changing
+    // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
+    // expected_flags is set to either RESULT_NSEC_SIGNED or
+    // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
+    // find() is expected to set the corresponding flags.
+    void findCheck(ZoneFinder::FindResultFlags expected_flags =
+                   ZoneFinder::RESULT_DEFAULT);
+    void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
+                        ZoneFinder::RESULT_DEFAULT);
+    void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                       ZoneFinder::RESULT_DEFAULT);
+    void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                               ZoneFinder::RESULT_DEFAULT);
+    void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                          ZoneFinder::RESULT_DEFAULT);
+    void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                            ZoneFinder::RESULT_DEFAULT);
+
 public:
     InMemoryZoneFinderTest() :
         class_(RRClass::IN()),
@@ -320,6 +339,8 @@ public:
              &rr_child_dname_},
             {"example.com. 300 IN A 192.0.2.10", &rr_out_},
             {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
+            {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
+             &rr_cnamewild_},
             {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
             {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
             {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
@@ -331,6 +352,9 @@ public:
             {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
             {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
              &rr_not_wild_another_},
+            {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
+             "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
+             &rr_nsec3_},
             {NULL, NULL}
         };
 
@@ -379,7 +403,8 @@ public:
     RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
     RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
     RRsetPtr rr_child_dname_; // A DNAME under NS
-    RRsetPtr rr_wild_;
+    RRsetPtr rr_wild_;        // Wildcard record
+    RRsetPtr rr_cnamewild_;     // CNAME at a wildcard
     RRsetPtr rr_emptywild_;
     RRsetPtr rr_nested_emptywild_;
     RRsetPtr rr_nswild_, rr_dnamewild_;
@@ -387,6 +412,7 @@ public:
     RRsetPtr rr_under_wild_;
     RRsetPtr rr_not_wild_;
     RRsetPtr rr_not_wild_another_;
+    RRsetPtr rr_nsec3_;
 
     /**
      * \brief Test one find query to the zone finder.
@@ -401,6 +427,8 @@ public:
      * \param check_answer Should a check against equality of the answer be
      *     done?
      * \param answer The expected rrset, if any should be returned.
+     * \param expected_flags The expected result flags returned via find().
+     *     These can be tested using isWildcard() etc.
      * \param zone_finder Check different InMemoryZoneFinder object than
      *     zone_finder_ (if NULL, uses zone_finder_)
      * \param check_wild_answer Checks that the answer has the same RRs, type
@@ -412,6 +440,8 @@ public:
                   ZoneFinder::Result result,
                   bool check_answer = true,
                   const ConstRRsetPtr& answer = ConstRRsetPtr(),
+                  ZoneFinder::FindResultFlags expected_flags =
+                  ZoneFinder::RESULT_DEFAULT,
                   InMemoryZoneFinder* zone_finder = NULL,
                   ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
                   bool check_wild_answer = false)
@@ -423,71 +453,75 @@ public:
         // we can't assign to FindResult
         EXPECT_NO_THROW({
                 ZoneFinder::FindResult find_result(zone_finder->find(
-                                                       name, rrtype,
-                                                       options));
+                                                       name, rrtype, options));
                 // Check it returns correct answers
                 EXPECT_EQ(result, find_result.code);
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                          find_result.isWildcard());
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+                          != 0, find_result.isNSECSigned());
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+                          != 0, find_result.isNSEC3Signed());
                 if (check_answer) {
-                    EXPECT_EQ(answer, find_result.rrset);
+                    if (!answer) {
+                        ASSERT_FALSE(find_result.rrset);
+                    } else {
+                        ASSERT_TRUE(find_result.rrset);
+                        rrsetCheck(answer, find_result.rrset);
+                    }
                 } else if (check_wild_answer) {
                     ASSERT_NE(ConstRRsetPtr(), answer) <<
                         "Wrong test, don't check for wild names if you expect "
                         "empty answer";
                     ASSERT_NE(ConstRRsetPtr(), find_result.rrset) <<
                         "No answer found";
+                    // Build the expected answer using the given name and
+                    // other parameter of the base wildcard RRset.
+                    RRsetPtr wildanswer(new RRset(name, answer->getClass(),
+                                                  answer->getType(),
+                                                  answer->getTTL()));
                     RdataIteratorPtr expectedIt(answer->getRdataIterator());
-                    RdataIteratorPtr actualIt(
-                        find_result.rrset->getRdataIterator());
-                    while (!expectedIt->isLast() && !actualIt->isLast()) {
-                        EXPECT_EQ(0, expectedIt->getCurrent().compare(
-                            actualIt->getCurrent())) << "The RRs differ ('" <<
-                            expectedIt->getCurrent().toText() << "', '" <<
-                            actualIt->getCurrent().toText() << "')";
-                        expectedIt->next();
-                        actualIt->next();
+                    for (; !expectedIt->isLast(); expectedIt->next()) {
+                        wildanswer->addRdata(expectedIt->getCurrent());
                     }
-                    EXPECT_TRUE(expectedIt->isLast()) <<
-                        "Result has less RRs than expected";
-                    EXPECT_TRUE(actualIt->isLast()) <<
-                        "Result has more RRs than expected";
-                    EXPECT_EQ(answer->getClass(),
-                        find_result.rrset->getClass());
-                    EXPECT_EQ(answer->getType(),
-                        find_result.rrset->getType());
-                    EXPECT_EQ(answer->getTTL(),
-                        find_result.rrset->getTTL());
-                    EXPECT_EQ(name, find_result.rrset->getName());
+                    rrsetCheck(wildanswer, find_result.rrset);
                 }
             });
     }
     /**
      * \brief Calls the findAll on the finder and checks the result.
      */
-    std::vector<ConstRRsetPtr> findAllTest(const Name& name,
-                                           ZoneFinder::Result result,
-                                           size_t expected_size,
-                                           InMemoryZoneFinder* finder = NULL,
-                                           const ConstRRsetPtr &rrset_result =
-                                           ConstRRsetPtr(),
-                                           ZoneFinder::FindOptions options =
-                                           ZoneFinder::FIND_DEFAULT)
+    void findAllTest(const Name& name, ZoneFinder::Result result,
+                     const vector<ConstRRsetPtr>& expected_rrsets,
+                     ZoneFinder::FindResultFlags expected_flags =
+                     ZoneFinder::RESULT_DEFAULT,
+                     InMemoryZoneFinder* finder = NULL,
+                     const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
+                     ZoneFinder::FindOptions options =
+                     ZoneFinder::FIND_DEFAULT)
     {
         if (finder == NULL) {
             finder = &zone_finder_;
         }
         std::vector<ConstRRsetPtr> target;
-        ZoneFinder::FindResult findResult(finder->findAll(name, target,
-                                                          options));
-        EXPECT_EQ(result, findResult.code);
-        EXPECT_EQ(rrset_result, findResult.rrset);
-        BOOST_FOREACH(const ConstRRsetPtr& rrset, target) {
-            EXPECT_EQ(name, rrset->getName());
+        ZoneFinder::FindResult find_result(finder->findAll(name, target,
+                                                           options));
+        EXPECT_EQ(result, find_result.code);
+        if (!rrset_result) {
+            EXPECT_FALSE(find_result.rrset);
+        } else {
+            ASSERT_TRUE(find_result.rrset);
+            rrsetCheck(rrset_result, find_result.rrset);
         }
-        EXPECT_EQ(expected_size, target.size());
-        return (target);
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                  find_result.isWildcard());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+                  != 0, find_result.isNSECSigned());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+                  != 0, find_result.isNSEC3Signed());
+        rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                    target.begin(), target.end());
     }
-    // Internal part of the cancelWildcard test that is multiple times
-    void doCancelWildcardTest();
 
     ConstRRsetPtr textToRRset(const string& text_rrset,
                               const RRClass& rrclass = RRClass::IN()) const
@@ -581,8 +615,8 @@ TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
         "cname.child.example.org. 300 IN CNAME target.child.example.org.");
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_under_cut_));
     findTest(Name("cname.child.example.org"), RRType::AAAA(),
-             ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL,
-             ZoneFinder::FIND_GLUE_OK);
+             ZoneFinder::CNAME, true, rr_cname_under_cut_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
 }
 
 // Two DNAMEs at single domain are disallowed by RFC 2672, section 3)
@@ -657,7 +691,7 @@ TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
 
     findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
     findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
-             NULL, ZoneFinder::FIND_GLUE_OK);
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
 }
 
 // Test adding child zones and zone cut handling
@@ -714,34 +748,33 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
 
+    vector<ConstRRsetPtr> expected_sets;
+
     // origin
-    std::vector<ConstRRsetPtr> rrsets(findAllTest(origin_, ZoneFinder::SUCCESS,
-                                                  2));
-    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
-                                           rr_a_));
-    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
-                                           rr_ns_));
+    expected_sets.push_back(rr_a_);
+    expected_sets.push_back(rr_ns_);
+    findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
 
     // out zone name
-    findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN, 0);
+    findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
+                vector<ConstRRsetPtr>());
 
-    rrsets = findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, 1);
-    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
-                                           rr_child_glue_));
-
-    // TODO: test NXRRSET case after rbtree non-terminal logic has
-    // been implemented
+    expected_sets.clear();
+    expected_sets.push_back(rr_child_glue_);
+    findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
 
     // add zone cut
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
 
     // zone cut
-    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION, 0, NULL,
-                rr_child_ns_);
+    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
+                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+                NULL, rr_child_ns_);
 
     // glue for this zone cut
-    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION, 0, NULL,
-                rr_child_ns_);
+    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
+                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+                NULL, rr_child_ns_);
 }
 
 TEST_F(InMemoryZoneFinderTest, glue) {
@@ -762,27 +795,30 @@ TEST_F(InMemoryZoneFinderTest, glue) {
 
     // If we do it in the "glue OK" mode, we should find the exact match.
     findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
-             rr_child_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
+             rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
 
     // glue OK + NXRRSET case
     findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
-             true, ConstRRsetPtr(), NULL, ZoneFinder::FIND_GLUE_OK);
+             true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
 
     // glue OK + NXDOMAIN case
     findTest(Name("www.child.example.org"), RRType::A(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
-             ZoneFinder::FIND_GLUE_OK);
+             ZoneFinder::DELEGATION, true, rr_child_ns_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
 
     // nested cut case.  The glue should be found.
     findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
              ZoneFinder::SUCCESS,
-             true, rr_grandchild_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
+             true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
 
     // A non-existent name in nested cut.  This should result in delegation
     // at the highest zone cut.
     findTest(Name("www.grand.child.example.org"), RRType::TXT(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
-             ZoneFinder::FIND_GLUE_OK);
+             ZoneFinder::DELEGATION, true, rr_child_ns_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
 }
 
 /**
@@ -792,13 +828,17 @@ TEST_F(InMemoryZoneFinderTest, glue) {
  * \todo This doesn't do any kind of CNAME and so on. If it isn't
  *     directly there, it just tells it doesn't exist.
  */
-TEST_F(InMemoryZoneFinderTest, find) {
+void
+InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags) {
     // Fill some data inside
     // Now put all the data we have there. It should throw nothing
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_a_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_ns_aaaa_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_a_)));
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
+    }
 
     // These two should be successful
     findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
@@ -806,15 +846,30 @@ TEST_F(InMemoryZoneFinderTest, find) {
              rr_ns_a_);
 
     // These domain exist but don't have the provided RRType
-    findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET);
-    findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET);
+    findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+    findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
 
     // These domains don't exist (and one is out of the zone)
-    findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN);
-    findTest(Name("example.net"), RRType::A(), ZoneFinder::NXDOMAIN);
+    findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN,
+             true, ConstRRsetPtr(), expected_flags);
+    findTest(Name("example.net"), RRType::A(), ZoneFinder::NXDOMAIN, true,
+             ConstRRsetPtr(), expected_flags);
 }
 
-TEST_F(InMemoryZoneFinderTest, emptyNode) {
+TEST_F(InMemoryZoneFinderTest, find) {
+    findCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3) {
+    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+void
+InMemoryZoneFinderTest::emptyNodeCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
     /*
      * The backend RBTree for this test should look like as follows:
      *          example.org
@@ -836,21 +891,35 @@ TEST_F(InMemoryZoneFinderTest, emptyNode) {
                                           " 300 IN A 192.0.2.1");
         EXPECT_EQ(SUCCESS, zone_finder_.add(rrset));
     }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
+    }
 
     // empty node matching, easy case: the node for 'baz' exists with
     // no data.
-    findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET);
+    findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
 
     // empty node matching, a trickier case: the node for 'foo' is part of
     // "x.foo", which should be considered an empty node.
-    findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET);
+    findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
 
     // "org" is contained in "example.org", but it shouldn't be treated as
     // NXRRSET because it's out of zone.
     // Note: basically we don't expect such a query to be performed (the common
     // operation is to identify the best matching zone first then perform
     // search it), but we shouldn't be confused even in the unexpected case.
-    findTest(Name("org"), RRType::A(), ZoneFinder::NXDOMAIN);
+    findTest(Name("org"), RRType::A(), ZoneFinder::NXDOMAIN, true,
+             ConstRRsetPtr(), expected_flags);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNode) {
+    emptyNodeCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
+    emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
 }
 
 TEST_F(InMemoryZoneFinderTest, load) {
@@ -870,14 +939,14 @@ TEST_F(InMemoryZoneFinderTest, load) {
 
     // Now see there are some rrsets (we don't look inside, though)
     findTest(Name("."), RRType::SOA(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), &rootzone);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
     findTest(Name("."), RRType::NS(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), &rootzone);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
     findTest(Name("a.root-servers.net."), RRType::A(), ZoneFinder::SUCCESS,
-             false, ConstRRsetPtr(), &rootzone);
+             false, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
     // But this should no longer be here
     findTest(rr_ns_a_->getName(), RRType::AAAA(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), &rootzone);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &rootzone);
 
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
@@ -888,21 +957,31 @@ TEST_F(InMemoryZoneFinderTest, load) {
  * Test that puts a (simple) wildcard into the zone and checks we can
  * correctly find the data.
  */
-TEST_F(InMemoryZoneFinderTest, wildcard) {
+void
+InMemoryZoneFinderTest::wildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
     /*
      *            example.org.
      *                 |
-     *                wild (not *.wild, should have wild mark)
+     *             [cname]wild (not *.wild, should have wild mark)
      *                 |
      *                 *
      */
     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.
+    // (the content of the NSEC3 shouldn't matter)
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
+    }
 
     // Search at the parent. The parent will not have the A, but it will
     // be in the wildcard (so check the wildcard isn't matched at the parent)
     {
-        SCOPED_TRACE("Search at parrent");
-        findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET);
+        SCOPED_TRACE("Search at parent");
+        findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(), expected_flags);
     }
 
     // Search the original name of wildcard
@@ -915,14 +994,30 @@ TEST_F(InMemoryZoneFinderTest, wildcard) {
     {
         SCOPED_TRACE("Search at created child");
         findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
-                 false, rr_wild_, NULL, ZoneFinder::FIND_DEFAULT, true);
+                 false, rr_wild_,
+                 ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+                 ZoneFinder::FIND_DEFAULT, true);
+        // Wildcard match, but no data
+        findTest(Name("a.wild.example.org"), RRType::AAAA(),
+                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+                 ZoneFinder::RESULT_WILDCARD | expected_flags);
+    }
+
+    // Search name that has CNAME.
+    {
+        SCOPED_TRACE("Matching CNAME");
+        findTest(Name("a.cnamewild.example.org"), RRType::A(),
+                 ZoneFinder::CNAME, false, rr_cnamewild_,
+                 ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+                 ZoneFinder::FIND_DEFAULT, true);
     }
 
     // Search another created name, this time little bit lower
     {
         SCOPED_TRACE("Search at created grand-child");
         findTest(Name("a.b.wild.example.org"), RRType::A(),
-                 ZoneFinder::SUCCESS, false, rr_wild_, NULL,
+                 ZoneFinder::SUCCESS, false, rr_wild_,
+                 ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
                  ZoneFinder::FIND_DEFAULT, true);
     }
 
@@ -930,10 +1025,20 @@ TEST_F(InMemoryZoneFinderTest, wildcard) {
     {
         SCOPED_TRACE("Search under non-wildcard");
         findTest(Name("bar.foo.wild.example.org"), RRType::A(),
-            ZoneFinder::NXDOMAIN);
+                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
     }
 }
 
+TEST_F(InMemoryZoneFinderTest, wildcard) {
+    // Normal case
+    wildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardNSEC3) {
+    // Similar to the previous one, but the zone signed with NSEC3
+    wildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
 /*
  * Test that we don't match a wildcard if we get under delegation.
  * By 4.3.3 of RFC1034:
@@ -954,40 +1059,60 @@ TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
     {
         SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
         findTest(Name("a.child.example.org"), RRType::A(),
-                 ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
-                 ZoneFinder::FIND_GLUE_OK);
+                 ZoneFinder::DELEGATION, true, rr_child_ns_,
+                 ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
     }
 }
 
 // Tests combination of wildcard and ANY.
-TEST_F(InMemoryZoneFinderTest, anyWildcard) {
+void
+InMemoryZoneFinderTest::anyWildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
+    }
+
+    vector<ConstRRsetPtr> expected_sets;
 
     // First try directly the name (normal match)
     {
         SCOPED_TRACE("Asking direcly for *");
-        const std::vector<ConstRRsetPtr>
-            target(findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
-                               1));
-        ASSERT_EQ(1, target.size());
-        EXPECT_EQ(RRType::A(), (*target.begin())->getType());
-        EXPECT_EQ(Name("*.wild.example.org"), (*target.begin())->getName());
+        expected_sets.push_back(rr_wild_);
+        findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
+                    expected_sets);
     }
 
     // Then a wildcard match
     {
         SCOPED_TRACE("Asking in the wild way");
-        const std::vector<ConstRRsetPtr>
-            target(findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
-                               1));
-        EXPECT_EQ(RRType::A(), (*target.begin())->getType());
-        EXPECT_EQ(Name("a.wild.example.org"), (*target.begin())->getName());
+        expected_sets.clear();
+        RRsetPtr expected(new RRset(Name("a.wild.example.org"),
+                                    rr_wild_->getClass(), rr_wild_->getType(),
+                                    rr_wild_->getTTL()));
+        expected->addRdata(rr_wild_->getRdataIterator()->getCurrent());
+        expected_sets.push_back(expected);
+        findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
+                    expected_sets,
+                    ZoneFinder::RESULT_WILDCARD | expected_flags);
     }
 }
 
+TEST_F(InMemoryZoneFinderTest, anyWildcard) {
+    anyWildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
+    anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
 // Test there's nothing in the wildcard in the middle if we load
 // wild.*.foo.example.org.
-TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
+void
+InMemoryZoneFinderTest::emptyWildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
     /*
      *            example.org.
      *                foo
@@ -995,6 +1120,9 @@ TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
      *               wild
      */
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_emptywild_));
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
+    }
 
     {
         SCOPED_TRACE("Asking for the original record under wildcard");
@@ -1004,25 +1132,41 @@ TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
 
     {
         SCOPED_TRACE("Asking for A record");
-        findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET);
-        findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET);
-        findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET);
+        findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(),
+                 ZoneFinder::RESULT_WILDCARD | expected_flags);
+        findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(), expected_flags);
+        findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(), expected_flags);
     }
 
     {
         SCOPED_TRACE("Asking for ANY record");
-        findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET, 0);
+        findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET,
+                    vector<ConstRRsetPtr>(), expected_flags);
 
-        findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET, 0);
+        findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET,
+                    vector<ConstRRsetPtr>(),
+                    ZoneFinder::RESULT_WILDCARD | expected_flags);
     }
 
     {
         SCOPED_TRACE("Asking on the non-terminal");
         findTest(Name("wild.bar.foo.example.org"), RRType::A(),
-            ZoneFinder::NXRRSET);
+                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+                 ZoneFinder::RESULT_WILDCARD | expected_flags);
     }
 }
 
+TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
+    emptyWildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
+    emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
 // Same as emptyWildcard, but with multiple * in the path.
 TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nested_emptywild_));
@@ -1045,7 +1189,8 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
 
         for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
-            findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
+            findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true,
+                     ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD);
         }
     }
 
@@ -1073,7 +1218,8 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
         for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
 
-            findAllTest(Name(*name), ZoneFinder::NXRRSET, 0);
+            findAllTest(Name(*name), ZoneFinder::NXRRSET,
+                        vector<ConstRRsetPtr>());
         }
     }
 }
@@ -1081,14 +1227,16 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
 // We run this part twice from the below test, in two slightly different
 // situations
 void
-InMemoryZoneFinderTest::doCancelWildcardTest() {
+InMemoryZoneFinderTest::doCancelWildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
     // These should be canceled
     {
         SCOPED_TRACE("Canceled under foo.wild.example.org");
         findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
-            ZoneFinder::NXDOMAIN);
+                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
         findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
-            ZoneFinder::NXDOMAIN);
+                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
     }
 
     // This is existing, non-wildcard domain, shouldn't wildcard at all
@@ -1113,7 +1261,9 @@ InMemoryZoneFinderTest::doCancelWildcardTest() {
             SCOPED_TRACE(string("Node ") + *name);
 
             findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
-                     rr_wild_, NULL, ZoneFinder::FIND_DEFAULT, true);
+                     rr_wild_,
+                     ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+                     ZoneFinder::FIND_DEFAULT, true);
         }
     }
 
@@ -1121,7 +1271,7 @@ InMemoryZoneFinderTest::doCancelWildcardTest() {
     {
         SCOPED_TRACE("The foo.wild.example.org itself");
         findTest(Name("foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXRRSET);
+                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags);
     }
 }
 
@@ -1141,7 +1291,7 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
 
     {
         SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
-        doCancelWildcardTest();
+        doCancelWildcardCheck();
     }
 
     // Try putting another one under foo.wild....
@@ -1150,7 +1300,23 @@ TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_));
     {
         SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
-        doCancelWildcardTest();
+        doCancelWildcardCheck();
+    }
+}
+
+TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
+    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
+    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_));
+    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
+
+    {
+        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+    }
+    EXPECT_EQ(SUCCESS, zone_finder_.add(rr_not_wild_another_));
+    {
+        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
     }
 }
 
@@ -1183,13 +1349,13 @@ TEST_F(InMemoryZoneFinderTest, swap) {
     EXPECT_EQ(RRClass::IN(), finder2.getClass());
     // make sure the zone data is swapped, too
     findTest(origin_, RRType::NS(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), &finder1);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder1);
     findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), &finder1);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder1);
     findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), &finder2);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder2);
     findTest(other_origin, RRType::TXT(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), &finder2);
+             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder2);
 }
 
 TEST_F(InMemoryZoneFinderTest, getFileName) {