From: JINMEI Tatuya Date: Thu, 16 Feb 2012 06:50:36 +0000 (-0800) Subject: [1580] Merge branch 'master' into trac1580 to reduce conflicts with results X-Git-Tag: trac2351_base~247^2~4^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e50e1bf6c169c72fb084deb42e739b7c9ebfb94e;p=thirdparty%2Fkea.git [1580] Merge branch 'master' into trac1580 to reduce conflicts with results of other NSEC3 tickets. --- e50e1bf6c169c72fb084deb42e739b7c9ebfb94e diff --cc src/bin/auth/tests/query_unittest.cc index 6f98bda577,c5d8a79713..eea46a0a2c --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@@ -282,9 -329,18 +329,20 @@@ public "q00jkcevqvmu85r014c7dkba38o0ji5r"; hash_map_[Name("nxdomain3.example.com")] = "009mhaveqvm6t7vbl5lop2u3t2rp3tom"; + hash_map_[Name("*.example.com")] = + "r53bq7cc2uvmubfu5ocmm6pers9tk9en"; - + hash_map_[Name("unsigned-delegation.example.com")] = + "q81r598950igr1eqvc60aedlq66425b5"; // a bit larger than H(www) + hash_map_[Name("*.uwild.example.com")] = + "b4um86eghhds6nea196smvmlo4ors995"; + hash_map_[Name("unsigned-delegation-optout.example.com")] = + "vld46lphhasfapj8og1pglgiasa5o5gt"; + + // For closest encloser proof for www1.uwild.example.com: + hash_map_[Name("uwild.example.com")] = + "t644ebqk9bibcna874givr6joj62mlhv"; + hash_map_[Name("www1.uwild.example.com")] = + "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www) } virtual isc::dns::Name getOrigin() const { return (origin_); } virtual isc::dns::RRClass getClass() const { return (rrclass_); } @@@ -1656,58 -1830,335 +1832,353 @@@ TEST_F(QueryTest, findNSEC3) // Similar to the previous case, but next closer name is different // (is the parent) of the non existent name. - nsec3Check(true, expected_closest_labels, - string(nsec3_apex_txt) + string(nsec3_www_txt), - mock_finder->findNSEC3(Name("nx.domain.example.com"), true)); + { + SCOPED_TRACE("nxdomain, next closer != qname"); + nsec3Check(true, expected_closest_labels, + string(nsec3_apex_txt) + string(nsec3_www_txt), + mock_finder->findNSEC3(Name("nx.domain.example.com"), + true)); + } // In the rest of test we check hash comparison for wrap around cases. - nsec3Check(false, 4, nsec3_apex_txt, - mock_finder->findNSEC3(Name("nxdomain2.example.com"), false)); - nsec3Check(false, 4, nsec3_www_txt, - mock_finder->findNSEC3(Name("nxdomain3.example.com"), false)); + { + SCOPED_TRACE("largest"); + nsec3Check(false, 4, nsec3_apex_txt, + mock_finder->findNSEC3(Name("nxdomain2.example.com"), + false)); + } + { + SCOPED_TRACE("smallest"); + nsec3Check(false, 4, nsec3_www_txt, + mock_finder->findNSEC3(Name("nxdomain3.example.com"), + false)); + } + } + + // This tests that the DS is returned above the delegation point as + // an authoritative answer, not a delegation. This is as described in + // RFC 4035, section 3.1.4.1. + + // This mock finder is used for some DS-query tests to support the cases + // where the query is expected to be handled in a different zone than our + // main test zone, example.com. Only limited methods are expected to called + // (and for limited purposes) on this class object in these tests, which + // are overridden below. + class AlternateZoneFinder : public MockZoneFinder { + public: + // This zone is expected not to have a DS by default and return NXRRSET + // for a DS query. If have_ds is set to true on construction, it will + // return a faked DS answer. + AlternateZoneFinder(const Name& origin, bool have_ds = false) : + MockZoneFinder(), origin_(origin), have_ds_(have_ds) + {} + virtual isc::dns::Name getOrigin() const { return (origin_); } + virtual FindResult find(const isc::dns::Name&, + const isc::dns::RRType& type, + const FindOptions) + { + if (type == RRType::SOA()) { + RRsetPtr soa = textToRRset(origin_.toText() + " 3600 IN SOA . . " + "0 0 0 0 0\n", origin_); + soa->addRRsig(RdataPtr(new generic::RRSIG( + getCommonRRSIGText("SOA")))); + return (FindResult(SUCCESS, soa)); + } + if (type == RRType::NS()) { + RRsetPtr ns = textToRRset(origin_.toText() + " 3600 IN NS " + + Name("ns").concatenate(origin_).toText()); + ns->addRRsig(RdataPtr(new generic::RRSIG( + getCommonRRSIGText("NS")))); + return (FindResult(SUCCESS, ns)); + } + if (type == RRType::DS()) { + if (have_ds_) { + RRsetPtr ds = textToRRset(origin_.toText() + + " 3600 IN DS 57855 5 1 " + + "49FD46E6C4B45C55D4AC69CBD" + "3CD34AC1AFE51DE"); + ds->addRRsig(RdataPtr(new generic::RRSIG( + getCommonRRSIGText("DS")))); + return (FindResult(SUCCESS, ds)); + } else { + RRsetPtr nsec = textToRRset(origin_.toText() + + " 3600 IN NSEC " + + origin_.toText() + + " SOA NSEC RRSIG"); + nsec->addRRsig(RdataPtr(new generic::RRSIG( + getCommonRRSIGText("NSEC")))); + return (FindResult(NXRRSET, nsec, RESULT_NSEC_SIGNED)); + } + } + + // Returning NXDOMAIN is not correct, but doesn't matter for our tests. + return (FindResult(NXDOMAIN, ConstRRsetPtr())); + } + private: + const Name origin_; + const bool have_ds_; + }; + + TEST_F(QueryTest, dsAboveDelegation) { + // Pretending to have authority for the child zone, too. + memory_client.addZone(ZoneFinderPtr(new AlternateZoneFinder( + Name("delegation.example.com")))); + + // The following will succeed only if the search goes to the parent + // zone, not the child one we added above. + EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"), + RRType::DS(), response, true).process()); + + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6, + (string(delegation_ds_txt) + "\n" + + "delegation.example.com. 3600 IN RRSIG " + + getCommonRRSIGText("DS")).c_str(), + (string(zone_ns_txt) + "\n" + + "example.com. 3600 IN RRSIG " + + getCommonRRSIGText("NS")).c_str(), + ns_addrs_and_sig_txt.c_str()); + } + + TEST_F(QueryTest, dsAboveDelegationNoData) { + // Similar to the previous case, but the query is for an unsigned zone + // (which doesn't have a DS at the parent). The response should be a + // "no data" error. The query should still be handled at the parent. + memory_client.addZone(ZoneFinderPtr( + new AlternateZoneFinder( + Name("unsigned-delegation.example.com")))); + + // The following will succeed only if the search goes to the parent + // zone, not the child one we added above. + EXPECT_NO_THROW(Query(memory_client, + Name("unsigned-delegation.example.com"), + RRType::DS(), 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(unsigned_delegation_nsec_txt) + + "unsigned-delegation.example.com. 3600 IN RRSIG " + + getCommonRRSIGText("NSEC")).c_str(), + NULL, mock_finder->getOrigin()); + } + + // This one checks that type-DS query results in a "no data" response + // when it happens to be sent to the child zone, as described in RFC 4035, + // section 3.1.4.1. The example is inspired by the B.8. example from the RFC. + TEST_F(QueryTest, dsBelowDelegation) { + EXPECT_NO_THROW(Query(memory_client, Name("example.com"), + RRType::DS(), 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_apex_txt) + "\n" + + string("example.com. 3600 IN RRSIG ") + + getCommonRRSIGText("NSEC")).c_str(), NULL, + mock_finder->getOrigin()); + } + + // Similar to the previous case, but even more pathological: the DS somehow + // exists in the child zone. The Query module should still return SOA. + // In our implementation NSEC/NSEC3 isn't attached in this case. + TEST_F(QueryTest, dsBelowDelegationWithDS) { + mock_finder->addRecord(zone_ds_txt); // add the DS to the child's apex + EXPECT_NO_THROW(Query(memory_client, Name("example.com"), + RRType::DS(), response, true).process()); + + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL, + (string(soa_txt) + string("example.com. 3600 IN RRSIG ") + + getCommonRRSIGText("SOA")).c_str(), NULL, + mock_finder->getOrigin()); + } + + // DS query received at a completely irrelevant (neither parent nor child) + // server. It should just like the "noZone" test case, but DS query involves + // special processing, so we test it explicitly. + TEST_F(QueryTest, dsNoZone) { + Query(memory_client, Name("example"), RRType::DS(), response, + true).process(); + responseCheck(response, Rcode::REFUSED(), 0, 0, 0, 0, NULL, NULL, NULL); + } + + // DS query for a "grandchild" zone. This should result in normal + // delegation (unless this server also has authority of the grandchild zone). + TEST_F(QueryTest, dsAtGrandParent) { + Query(memory_client, Name("grand.delegation.example.com"), RRType::DS(), + response, true).process(); + responseCheck(response, Rcode::NOERROR(), 0, 0, 6, 6, NULL, + (string(delegation_txt) + string(delegation_ds_txt) + + "delegation.example.com. 3600 IN RRSIG " + + getCommonRRSIGText("DS")).c_str(), + ns_addrs_and_sig_txt.c_str()); + } + + // DS query sent to a "grandparent" server that also has authority for the + // child zone. In this case the query should be handled in the child + // side and should result in no data with SOA. Note that the server doesn't + // have authority for the "parent". Unlike the dsAboveDelegation test case + // the query should be handled in the child zone, not in the grandparent. + TEST_F(QueryTest, dsAtGrandParentAndChild) { + // Pretending to have authority for the child zone, too. + const Name childname("grand.delegation.example.com"); + memory_client.addZone(ZoneFinderPtr( + new AlternateZoneFinder(childname))); + Query(memory_client, childname, RRType::DS(), response, true).process(); + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL, + (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" + + childname.toText() + " 3600 IN RRSIG " + + getCommonRRSIGText("SOA") + "\n" + + childname.toText() + " 3600 IN NSEC " + + childname.toText() + " SOA NSEC RRSIG\n" + + childname.toText() + " 3600 IN RRSIG " + + getCommonRRSIGText("NSEC")).c_str(), NULL, childname); + } + + // DS query for the root name (quite pathological). Since there's no "parent", + // the query will be handled in the root zone anyway, and should (normally) + // result in no data. + TEST_F(QueryTest, dsAtRoot) { + // Pretend to be a root server. + memory_client.addZone(ZoneFinderPtr( + new AlternateZoneFinder(Name::ROOT_NAME()))); + Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response, + true).process(); + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL, + (string(". 3600 IN SOA . . 0 0 0 0 0\n") + + ". 3600 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" + + ". 3600 IN NSEC " + ". SOA NSEC RRSIG\n" + + ". 3600 IN RRSIG " + + getCommonRRSIGText("NSEC")).c_str(), NULL); + } + + // Even more pathological case: A faked root zone actually has its own DS + // query. How we respond wouldn't matter much in practice, but check if + // it behaves as it's intended. This implementation should return the DS. + TEST_F(QueryTest, dsAtRootWithDS) { + memory_client.addZone(ZoneFinderPtr( + new AlternateZoneFinder(Name::ROOT_NAME(), + true))); + Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response, + true).process(); + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0, + (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD" + "3CD34AC1AFE51DE\n") + + ". 3600 IN RRSIG " + getCommonRRSIGText("DS")).c_str(), + (string(". 3600 IN NS ns.\n") + + ". 3600 IN RRSIG " + getCommonRRSIGText("NS")).c_str(), + NULL); + } + + // Check the signature is present when an NXRRSET is returned + TEST_F(QueryTest, nxrrsetWithNSEC3) { + mock_finder->setNSEC3Flag(true); + + // NXRRSET with DNSSEC proof. We should have SOA, NSEC3 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(nsec3_www_txt) + "\n" + + mock_finder->hash_map_[Name("www.example.com.")] + + ".example.com. 3600 IN RRSIG " + + getCommonRRSIGText("NSEC3") + "\n").c_str(), + NULL, mock_finder->getOrigin()); + } + + // Check the exception is correctly raised when the NSEC3 thing isn't in the + // zone + TEST_F(QueryTest, nxrrsetMissingNSEC3) { + mock_finder->setNSEC3Flag(true); + // We just need it to return false for "matched". This indicates + // there's no exact match for NSEC3 on www.example.com. + ZoneFinder::FindNSEC3Result nsec3(false, 0, ConstRRsetPtr(), + ConstRRsetPtr()); + mock_finder->setNSEC3Result(&nsec3); + + EXPECT_THROW(Query(memory_client, Name("www.example.com"), RRType::TXT(), + response, true).process(), Query::BadNSEC3); + } + + TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) { + mock_finder->addRecord(unsigned_delegation_nsec3_txt); + mock_finder->setNSEC3Flag(true); + + // This delegation has no DS, but does have a matching NSEC3 record + // (See RFC5155 section 7.2.4) + Query(memory_client, Name("unsigned-delegation.example.com."), + RRType::DS(), 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(unsigned_delegation_nsec3_txt) + "\n" + + mock_finder-> + hash_map_[Name("unsigned-delegation.example.com.")] + + ".example.com. 3600 IN RRSIG " + + getCommonRRSIGText("NSEC3") + "\n").c_str(), + NULL, mock_finder->getOrigin()); + } + + TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) { + mock_finder->addRecord(unsigned_delegation_nsec3_txt); + mock_finder->setNSEC3Flag(true); + + // This delegation has no DS, and no directly matching NSEC3 record + // So the response should contain closest encloser proof (and the + // 'next closer' should have opt-out set, though that is not + // actually checked) + // (See RFC5155 section 7.2.4) + Query(memory_client, Name("unsigned-delegation-optout.example.com."), + RRType::DS(), response, true).process(); + responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL, + (string(soa_txt) + string("example.com. 3600 IN RRSIG ") + + getCommonRRSIGText("SOA") + "\n" + + string(nsec3_apex_txt) + "\n" + + mock_finder->hash_map_[Name("example.com.")] + + ".example.com. 3600 IN RRSIG " + + getCommonRRSIGText("NSEC3") + "\n" + + string(unsigned_delegation_nsec3_txt) + "\n" + + mock_finder-> + hash_map_[Name("unsigned-delegation.example.com.")] + + ".example.com. 3600 IN RRSIG " + + getCommonRRSIGText("NSEC3") + "\n").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. -TEST_F(QueryTest, nxdomainWithNSEC3) { +TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) { mock_finder->setNSEC3Flag(true); - ZoneFinder::FindResult result = mock_finder->find( - Name("nxdomain.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC); - EXPECT_EQ(ZoneFinder::NXDOMAIN, result.code); - EXPECT_FALSE(result.rrset); - EXPECT_TRUE(result.isNSEC3Signed()); - EXPECT_FALSE(result.isWildcard()); + ZoneFinder::FindNSEC3Result nsec3(true, 0, textToRRset(nsec3_apex_txt), + ConstRRsetPtr()); + mock_finder->setNSEC3Result(&nsec3); + + EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), + RRType::TXT(), response, true).process(), + Query::BadNSEC3); } +TEST_F(QueryTest, nxdomainWithNSEC3Proof) { + mock_finder->setNSEC3Flag(true); + Query(memory_client, Name("nxdomain.example.com"), qtype, + response, true).process(); + 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("NSEC3") + "\n" + + string(nsec3_www_txt) + "\n" + + string("q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG ") + + getCommonRRSIGText("NSEC3")).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. - TEST_F(QueryTest, nxrrsetWithNSEC3) { - mock_finder->setNSEC3Flag(true); - ZoneFinder::FindResult result = mock_finder->find( - Name("www.example.com"), RRType::TXT(), ZoneFinder::FIND_DNSSEC); - EXPECT_EQ(ZoneFinder::NXRRSET, result.code); - EXPECT_FALSE(result.rrset); - EXPECT_TRUE(result.isNSEC3Signed()); - EXPECT_FALSE(result.isWildcard()); - } - TEST_F(QueryTest, emptyNameWithNSEC3) { mock_finder->setNSEC3Flag(true); ZoneFinder::FindResult result = mock_finder->find(