diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes index e7b642a31f..f05ff21c60 100644 --- a/src/lib/datasrc/datasrc_messages.mes +++ b/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. diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc index 0a3280be4a..bfff5c13d9 100644 --- a/src/lib/datasrc/memory_datasrc.cc +++ b/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 *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)); } }; diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc index d0d2bc72ed..f10adacc39 100644 --- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc +++ b/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 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& 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 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 expected_sets; + // origin - std::vector 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()); - 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(), 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(), 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 expected_sets; // First try directly the name (normal match) { SCOPED_TRACE("Asking direcly for *"); - const std::vector - 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 - 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(), expected_flags); - findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET, 0); + findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET, + vector(), + 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()); } } } @@ -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) {