Commit b7e1847c authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac1309'

parents 1c8dfb0c b3af32e1
......@@ -167,6 +167,24 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
}
}
void
Query::addWildcardProof(ZoneFinder& finder) {
// The query name shouldn't exist in the zone if there were no wildcard
// substitution. Confirm that by specifying NO_WILDCARD. It should result
// in NXDOMAIN and an NSEC RR that proves it should be returned.
const ZoneFinder::FindResult fresult =
finder.find(qname_, RRType::NSEC(), NULL,
dnssec_opt_ | ZoneFinder::NO_WILDCARD);
if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
fresult.rrset->getRdataCount() == 0) {
isc_throw(BadNSEC, "Unexpected result for wildcard proof");
return;
}
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(fresult.rrset),
dnssec_);
}
void
Query::addAuthAdditional(ZoneFinder& finder) {
// Fill in authority and addtional sections.
......@@ -259,6 +277,7 @@ Query::process() {
break;
}
case ZoneFinder::CNAME:
case ZoneFinder::WILDCARD_CNAME:
/*
* We don't do chaining yet. Therefore handling a CNAME is
* mostly the same as handling SUCCESS, but we didn't get
......@@ -271,8 +290,15 @@ Query::process() {
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset),
dnssec_);
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
addWildcardProof(*result.zone_finder);
}
break;
case ZoneFinder::SUCCESS:
case ZoneFinder::WILDCARD:
if (qtype_is_any) {
// If quety type is ANY, insert all RRs under the domain
// into answer section.
......@@ -299,6 +325,12 @@ Query::process() {
{
addAuthAdditional(*result.zone_finder);
}
// If the answer is a result of wildcard substitution,
// add a proof that there's no closer name.
if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
addWildcardProof(*result.zone_finder);
}
break;
case ZoneFinder::DELEGATION:
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
......@@ -324,10 +356,9 @@ Query::process() {
}
break;
default:
// These are new result codes (WILDCARD and WILDCARD_NXRRSET)
// They should not happen from the in-memory and the database
// backend isn't used yet.
// TODO: Implement before letting the database backends in
// This is basically a bug of the data source implementation,
// but could also happen in the middle of development where
// we try to add a new result code.
isc_throw(isc::NotImplemented, "Unknown result code");
break;
}
......
......@@ -77,6 +77,11 @@ private:
void addNXDOMAINProof(isc::datasrc::ZoneFinder& finder,
isc::dns::ConstRRsetPtr nsec);
/// Add NSEC RRs that prove a wildcard answer is the best one.
///
/// This corresponds to Section 3.1.3.3 of RFC 4035.
void addWildcardProof(isc::datasrc::ZoneFinder& finder);
/// \brief Look up additional data (i.e., address records for the names
/// included in NS or MX records) and add them to the additional section.
///
......
......@@ -92,6 +92,14 @@ const char* const other_zone_rrs =
"cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
"cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
"mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
// Wildcards
const char* const wild_txt = "*.wild.example.com. 3600 IN A 192.0.2.7\n";
const char* const nsec_wild_txt =
"*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG\n";
const char* const cnamewild_txt =
"*.cnamewild.example.com. 3600 IN CNAME www.example.org.\n";
const char* const nsec_cnamewild_txt = "*.cnamewild.example.com. "
"3600 IN NSEC delegation.example.com. CNAME NSEC RRSIG\n";
// Used in NXDOMAIN proof test. We are going to test some unusual case where
// the best possible wildcard is below the "next domain" of the NSEC RR that
// proves the NXDOMAIN, i.e.,
......@@ -170,7 +178,8 @@ public:
cname_nxdom_txt << cname_out_txt << dname_txt << dname_a_txt <<
other_zone_rrs << no_txt << nz_txt <<
nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt;
nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt <<
wild_txt << nsec_wild_txt << cnamewild_txt << nsec_cnamewild_txt;
masterLoad(zone_stream, origin_, rrclass_,
boost::bind(&MockZoneFinder::loadRRset, this, _1));
......@@ -259,6 +268,24 @@ private:
boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
};
// A helper function that generates a new RRset based on "wild_rrset",
// replacing its owner name with 'real_name'.
ConstRRsetPtr
substituteWild(const RRset& wild_rrset, const Name& real_name) {
RRsetPtr rrset(new RRset(real_name, wild_rrset.getClass(),
wild_rrset.getType(), wild_rrset.getTTL()));
// For simplicity we only consider the case with one RDATA (for now)
rrset->addRdata(wild_rrset.getRdataIterator()->getCurrent());
ConstRRsetPtr wild_sig = wild_rrset.getRRsig();
if (wild_sig) {
RRsetPtr sig(new RRset(real_name, wild_sig->getClass(),
wild_sig->getType(), wild_sig->getTTL()));
sig->addRdata(wild_sig->getRdataIterator()->getCurrent());
rrset->addRRsig(sig);
}
return (rrset);
}
ZoneFinder::FindResult
MockZoneFinder::find(const Name& name, const RRType& type,
RRsetList* target, const FindOptions options)
......@@ -365,6 +392,33 @@ MockZoneFinder::find(const Name& name, const RRType& type,
return (FindResult(NXRRSET, RRsetPtr()));
}
// Another possibility is wildcard. For simplicity we only check
// hardcoded specific cases, ignoring other details such as canceling
// due to the existence of closer name.
if ((options & NO_WILDCARD) == 0) {
const Name wild_suffix("wild.example.com");
if (name.compare(wild_suffix).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
domain = domains_.find(Name("*").concatenate(wild_suffix));
assert(domain != domains_.end());
RRsetStore::const_iterator found_rrset = domain->second.find(type);
assert(found_rrset != domain->second.end());
return (FindResult(WILDCARD,
substituteWild(*found_rrset->second, name)));
}
const Name cnamewild_suffix("cnamewild.example.com");
if (name.compare(cnamewild_suffix).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
domain = domains_.find(Name("*").concatenate(cnamewild_suffix));
assert(domain != domains_.end());
RRsetStore::const_iterator found_rrset =
domain->second.find(RRType::CNAME());
assert(found_rrset != domain->second.end());
return (FindResult(WILDCARD_CNAME,
substituteWild(*found_rrset->second, name)));
}
}
// This is an NXDOMAIN case.
// If we need DNSSEC proof, find the "previous name" that has an NSEC RR
// and return NXDOMAIN with the found NSEC. Otherwise, just return the
......@@ -804,6 +858,72 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
NULL, mock_finder->getOrigin());
}
TEST_F(QueryTest, wildcardNSEC) {
// The qname matches *.wild.example.com. The response should contain
// an NSEC that proves the non existence of a closer name.
Query(memory_client, Name("www.wild.example.com"), RRType::A(), response,
true).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
(string(wild_txt).replace(0, 1, "www") +
string("www.wild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("A") + "\n").c_str(),
(zone_ns_txt + string("example.com. 3600 IN RRSIG NS 5 "
"3 3600 20000101000000 "
"20000201000000 12345 "
"example.com. FAKEFAKEFAKE\n") +
string(nsec_wild_txt) +
string("*.wild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("NSEC") + "\n").c_str(),
NULL, // we are not interested in additionals in this test
mock_finder->getOrigin());
}
TEST_F(QueryTest, CNAMEwildNSEC) {
// Similar to the previous case, but the matching wildcard record is
// CNAME.
Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
response, true).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
(string(cnamewild_txt).replace(0, 1, "www") +
string("www.cnamewild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("CNAME") + "\n").c_str(),
(string(nsec_cnamewild_txt) +
string("*.cnamewild.example.com. 3600 IN RRSIG ") +
getCommonRRSIGText("NSEC") + "\n").c_str(),
NULL, // we are not interested in additionals in this test
mock_finder->getOrigin());
}
TEST_F(QueryTest, badWildcardProof1) {
// Unexpected case in wildcard proof: ZoneFinder::find() returns SUCCESS
// when NXDOMAIN is expected.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::SUCCESS,
mock_finder->delegation_rrset_);
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
}
TEST_F(QueryTest, badWildcardProof2) {
// "wildcard proof" doesn't return RRset.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::NXDOMAIN, ConstRRsetPtr());
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
}
TEST_F(QueryTest, badWildcardProof3) {
// "wildcard proof" returns empty NSEC.
mock_finder->setNSECResult(Name("www.wild.example.com"),
ZoneFinder::NXDOMAIN,
mock_finder->empty_nsec_rrset_);
EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
RRType::A(), response, true).process(),
Query::BadNSEC);
}
/*
* This tests that when there's no SOA and we need a negative answer. It should
* throw in that case.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment