From: Michal 'vorner' Vaner Date: Mon, 13 Feb 2012 14:41:16 +0000 (+0100) Subject: Merge #1581 X-Git-Tag: trac2351_base~97^2~38^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=72b239285aa03f9afa5685a4665451d89a941143;p=thirdparty%2Fkea.git Merge #1581 Conflicts: src/bin/auth/tests/query_unittest.cc --- 72b239285aa03f9afa5685a4665451d89a941143 diff --cc src/bin/auth/tests/query_unittest.cc index 9bdd3a0c75,ca90853e3f..8658ef4369 --- a/src/bin/auth/tests/query_unittest.cc +++ b/src/bin/auth/tests/query_unittest.cc @@@ -1645,221 -1651,39 +1671,254 @@@ TEST_F(QueryTest, findNSEC3) 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); + } + // 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.