]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
Merge #1581
authorMichal 'vorner' Vaner <michal.vaner@nic.cz>
Mon, 13 Feb 2012 14:41:16 +0000 (15:41 +0100)
committerMichal 'vorner' Vaner <michal.vaner@nic.cz>
Mon, 13 Feb 2012 14:54:54 +0000 (15:54 +0100)
Conflicts:
src/bin/auth/tests/query_unittest.cc

1  2 
src/bin/auth/query.cc
src/bin/auth/query.h
src/bin/auth/tests/query_unittest.cc

Simple merge
Simple merge
index 9bdd3a0c757f71d998e9f0d562451f10565ffe6f,ca90853e3f40fe2743c18bc22de25fa4efd44c8a..8658ef4369c12292fe3e89281d974be1810c307b
@@@ -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.