Commit c86612cd authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #1176

RRSIG support for database DataSources
parents 75e756cd c1c2ddf5
......@@ -67,20 +67,21 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname,
// Find A rrset
if (qname_ != qname || qtype_ != RRType::A()) {
ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL,
options);
options | dnssec_opt_);
if (a_result.code == ZoneFinder::SUCCESS) {
response_.addRRset(Message::SECTION_ADDITIONAL,
boost::const_pointer_cast<RRset>(a_result.rrset));
boost::const_pointer_cast<RRset>(a_result.rrset), dnssec_);
}
}
// Find AAAA rrset
if (qname_ != qname || qtype_ != RRType::AAAA()) {
ZoneFinder::FindResult aaaa_result =
zone.find(qname, RRType::AAAA(), NULL, options);
zone.find(qname, RRType::AAAA(), NULL, options | dnssec_opt_);
if (aaaa_result.code == ZoneFinder::SUCCESS) {
response_.addRRset(Message::SECTION_ADDITIONAL,
boost::const_pointer_cast<RRset>(aaaa_result.rrset));
boost::const_pointer_cast<RRset>(aaaa_result.rrset),
dnssec_);
}
}
}
......@@ -88,7 +89,7 @@ Query::findAddrs(ZoneFinder& zone, const Name& qname,
void
Query::putSOA(ZoneFinder& zone) const {
ZoneFinder::FindResult soa_result(zone.find(zone.getOrigin(),
RRType::SOA()));
RRType::SOA(), NULL, dnssec_opt_));
if (soa_result.code != ZoneFinder::SUCCESS) {
isc_throw(NoSOA, "There's no SOA record in zone " <<
zone.getOrigin().toText());
......@@ -99,7 +100,7 @@ Query::putSOA(ZoneFinder& zone) const {
* to insist.
*/
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(soa_result.rrset));
boost::const_pointer_cast<RRset>(soa_result.rrset), dnssec_);
}
}
......@@ -107,14 +108,15 @@ void
Query::getAuthAdditional(ZoneFinder& zone) const {
// Fill in authority and addtional sections.
ZoneFinder::FindResult ns_result = zone.find(zone.getOrigin(),
RRType::NS());
RRType::NS(), NULL,
dnssec_opt_);
// zone origin name should have NS records
if (ns_result.code != ZoneFinder::SUCCESS) {
isc_throw(NoApexNS, "There's no apex NS records in zone " <<
zone.getOrigin().toText());
} else {
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(ns_result.rrset));
boost::const_pointer_cast<RRset>(ns_result.rrset), dnssec_);
// Handle additional for authority section
getAdditional(zone, *ns_result.rrset);
}
......@@ -147,12 +149,14 @@ Query::process() const {
keep_doing = false;
std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
const ZoneFinder::FindResult db_result(
result.zone_finder->find(qname_, qtype_, target.get()));
result.zone_finder->find(qname_, qtype_, target.get(),
dnssec_opt_));
switch (db_result.code) {
case ZoneFinder::DNAME: {
// First, put the dname into the answer
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset));
boost::const_pointer_cast<RRset>(db_result.rrset),
dnssec_);
/*
* Empty DNAME should never get in, as it is impossible to
* create one in master file.
......@@ -188,7 +192,7 @@ Query::process() const {
qname_.getLabelCount() -
db_result.rrset->getName().getLabelCount()).
concatenate(dname.getDname())));
response_.addRRset(Message::SECTION_ANSWER, cname);
response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
break;
}
case ZoneFinder::CNAME:
......@@ -202,20 +206,23 @@ Query::process() const {
* So, just put it there.
*/
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset));
boost::const_pointer_cast<RRset>(db_result.rrset),
dnssec_);
break;
case ZoneFinder::SUCCESS:
if (qtype_is_any) {
// If quety type is ANY, insert all RRs under the domain
// into answer section.
BOOST_FOREACH(RRsetPtr rrset, *target) {
response_.addRRset(Message::SECTION_ANSWER, rrset);
response_.addRRset(Message::SECTION_ANSWER, rrset,
dnssec_);
// Handle additional for answer section
getAdditional(*result.zone_finder, *rrset.get());
}
} else {
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset));
boost::const_pointer_cast<RRset>(db_result.rrset),
dnssec_);
// Handle additional for answer section
getAdditional(*result.zone_finder, *db_result.rrset);
}
......@@ -233,7 +240,8 @@ Query::process() const {
case ZoneFinder::DELEGATION:
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(db_result.rrset));
boost::const_pointer_cast<RRset>(db_result.rrset),
dnssec_);
getAdditional(*result.zone_finder, *db_result.rrset);
break;
case ZoneFinder::NXDOMAIN:
......
......@@ -139,11 +139,15 @@ public:
/// \param qname The query name
/// \param qtype The RR type of the query
/// \param response The response message to store the answer to the query.
/// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
/// possible.
Query(const isc::datasrc::DataSourceClient& datasrc_client,
const isc::dns::Name& qname, const isc::dns::RRType& qtype,
isc::dns::Message& response) :
isc::dns::Message& response, bool dnssec = false) :
datasrc_client_(datasrc_client), qname_(qname), qtype_(qtype),
response_(response)
response_(response), dnssec_(dnssec),
dnssec_opt_(dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC :
isc::datasrc::ZoneFinder::FIND_DEFAULT)
{}
/// Process the query.
......@@ -211,6 +215,8 @@ private:
const isc::dns::Name& qname_;
const isc::dns::RRType& qtype_;
isc::dns::Message& response_;
const bool dnssec_;
const isc::datasrc::ZoneFinder::FindOptions dnssec_opt_;
};
}
......
......@@ -111,7 +111,8 @@ public:
dname_name_("dname.example.com"),
has_SOA_(true),
has_apex_NS_(true),
rrclass_(RRClass::IN())
rrclass_(RRClass::IN()),
include_rrsig_anyway_(false)
{
stringstream zone_stream;
zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
......@@ -137,11 +138,14 @@ public:
// the apex NS.
void setApexNSFlag(bool on) { has_apex_NS_ = on; }
// Turn this on if you want it to return RRSIGs regardless of FIND_GLUE_OK
void setIncludeRRSIGAnyway(bool on) { include_rrsig_anyway_ = on; }
private:
typedef map<RRType, ConstRRsetPtr> RRsetStore;
typedef map<Name, RRsetStore> Domains;
Domains domains_;
void loadRRset(ConstRRsetPtr rrset) {
void loadRRset(RRsetPtr rrset) {
domains_[rrset->getName()][rrset->getType()] = rrset;
if (rrset->getName() == delegation_name_ &&
rrset->getType() == RRType::NS()) {
......@@ -149,6 +153,26 @@ private:
} else if (rrset->getName() == dname_name_ &&
rrset->getType() == RRType::DNAME()) {
dname_rrset_ = rrset;
// Add some signatures
} else if (rrset->getName() == Name("example.com.") &&
rrset->getType() == RRType::NS()) {
rrset->addRRsig(RdataPtr(new generic::RRSIG("NS 5 3 3600 "
"20000101000000 "
"20000201000000 "
"12345 example.com. "
"FAKEFAKEFAKE")));
} else if (rrset->getType() == RRType::A()) {
rrset->addRRsig(RdataPtr(new generic::RRSIG("A 5 3 3600 "
"20000101000000 "
"20000201000000 "
"12345 example.com. "
"FAKEFAKEFAKE")));
} else if (rrset->getType() == RRType::AAAA()) {
rrset->addRRsig(RdataPtr(new generic::RRSIG("AAAA 5 3 3600 "
"20000101000000 "
"20000201000000 "
"12345 example.com. "
"FAKEFAKEFAKE")));
}
}
......@@ -161,6 +185,7 @@ private:
ConstRRsetPtr delegation_rrset_;
ConstRRsetPtr dname_rrset_;
const RRClass rrclass_;
bool include_rrsig_anyway_;
};
ZoneFinder::FindResult
......@@ -195,7 +220,26 @@ MockZoneFinder::find(const Name& name, const RRType& type,
RRsetStore::const_iterator found_rrset =
found_domain->second.find(type);
if (found_rrset != found_domain->second.end()) {
return (FindResult(SUCCESS, found_rrset->second));
ConstRRsetPtr rrset;
// Strip whatever signature there is in case DNSSEC is not required
// Just to make sure the Query asks for it when it is needed
if (options & ZoneFinder::FIND_DNSSEC ||
include_rrsig_anyway_ ||
!found_rrset->second->getRRsig()) {
rrset = found_rrset->second;
} else {
RRsetPtr noconst(new RRset(found_rrset->second->getName(),
found_rrset->second->getClass(),
found_rrset->second->getType(),
found_rrset->second->getTTL()));
for (RdataIteratorPtr
i(found_rrset->second->getRdataIterator());
!i->isLast(); i->next()) {
noconst->addRdata(i->getCurrent());
}
rrset = noconst;
}
return (FindResult(SUCCESS, rrset));
}
// If not found but we have a target, fill it with all RRsets here
......@@ -304,6 +348,58 @@ TEST_F(QueryTest, exactMatch) {
www_a_txt, zone_ns_txt, ns_addrs_txt);
}
TEST_F(QueryTest, exactMatchIgnoreSIG) {
// Check that we do not include the RRSIG when not requested even when
// we receive it from the data source.
mock_finder->setIncludeRRSIGAnyway(true);
Query query(memory_client, qname, qtype, response);
EXPECT_NO_THROW(query.process());
// find match rrset
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
www_a_txt, zone_ns_txt, ns_addrs_txt);
}
TEST_F(QueryTest, dnssecPositive) {
// Just like exactMatch, but the signatures should be included as well
Query query(memory_client, qname, qtype, response, true);
EXPECT_NO_THROW(query.process());
// find match rrset
// We can't let responseCheck to check the additional section as well,
// it gets confused by the two RRs for glue.delegation.../RRSIG due
// to it's design and fixing it would be hard. Therefore we simply
// check manually this one time.
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
(www_a_txt + std::string("www.example.com. 3600 IN RRSIG "
"A 5 3 3600 20000101000000 "
"20000201000000 12345 example.com. "
"FAKEFAKEFAKE\n")).c_str(),
(zone_ns_txt + std::string("example.com. 3600 IN RRSIG NS 5 "
"3 3600 20000101000000 "
"20000201000000 12345 "
"example.com. FAKEFAKEFAKE\n")).
c_str(), NULL);
RRsetIterator iterator(response.beginSection(Message::SECTION_ADDITIONAL));
const char* additional[] = {
"glue.delegation.example.com. 3600 IN A 192.0.2.153\n",
"glue.delegation.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 "
"20000201000000 12345 example.com. FAKEFAKEFAKE\n",
"glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n",
"glue.delegation.example.com. 3600 IN RRSIG AAAA 5 3 3600 "
"20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE\n",
"noglue.example.com. 3600 IN A 192.0.2.53\n",
"noglue.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 "
"20000201000000 12345 example.com. FAKEFAKEFAKE\n",
NULL
};
for (const char** rr(additional); *rr != NULL; ++ rr) {
ASSERT_FALSE(iterator ==
response.endSection(Message::SECTION_ADDITIONAL));
EXPECT_EQ(*rr, (*iterator)->toText());
iterator ++;
}
EXPECT_TRUE(iterator == response.endSection(Message::SECTION_ADDITIONAL));
}
TEST_F(QueryTest, exactAddrMatch) {
// find match rrset, omit additional data which has already been provided
// in the answer section from the additional.
......
......@@ -470,6 +470,7 @@ private:
// Something for wildcards
addRecord("A", "3600", "", "192.0.2.5");
addRecord("RRSIG", "3600", "", "A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE");
addCurName("*.wild.example.org.");
addRecord("AAAA", "3600", "", "2001:db8::5");
addCurName("cancel.here.wild.example.org.");
......@@ -1167,7 +1168,10 @@ TEST_F(DatabaseClientTest, wildcard) {
shared_ptr<DatabaseClient::Finder> finder(getFinder());
// First, simple wildcard match
// Check also that the RRSIG is added from the wildcard (not modified)
expected_rdatas_.push_back("192.0.2.5");
expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 "
"12345 example.org. FAKEFAKEFAKE");
doFindTest(finder, isc::dns::Name("a.wild.example.org"),
isc::dns::RRType::A(), isc::dns::RRType::A(),
isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
......@@ -1177,6 +1181,7 @@ TEST_F(DatabaseClientTest, wildcard) {
isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
expected_sig_rdatas_);
expected_rdatas_.clear();
expected_sig_rdatas_.clear();
doFindTest(finder, isc::dns::Name("a.wild.example.org"),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_,
......@@ -1188,11 +1193,14 @@ TEST_F(DatabaseClientTest, wildcard) {
// Direct request for thi wildcard
expected_rdatas_.push_back("192.0.2.5");
expected_sig_rdatas_.push_back("A 5 3 3600 20000101000000 20000201000000 "
"12345 example.org. FAKEFAKEFAKE");
doFindTest(finder, isc::dns::Name("*.wild.example.org"),
isc::dns::RRType::A(), isc::dns::RRType::A(),
isc::dns::RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas_,
expected_sig_rdatas_);
expected_rdatas_.clear();
expected_sig_rdatas_.clear();
doFindTest(finder, isc::dns::Name("*.wild.example.org"),
isc::dns::RRType::AAAA(), isc::dns::RRType::AAAA(),
isc::dns::RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas_,
......
......@@ -107,7 +107,11 @@ public:
/// performed on these values to express compound options.
enum FindOptions {
FIND_DEFAULT = 0, ///< The default options
FIND_GLUE_OK = 1 ///< Allow search under a zone cut
FIND_GLUE_OK = 1, ///< Allow search under a zone cut
FIND_DNSSEC = 2 ///< Require DNSSEC data in the answer
///< (RRSIG, NSEC, etc.). The implementation
///< is allowed to include it even if it is
///< not set.
};
///
......@@ -201,6 +205,19 @@ public:
//@}
};
/// \brief Operator to combine FindOptions
///
/// We would need to manually static-cast the options if we put or
/// between them, which is undesired with bit-flag options. Therefore
/// we hide the cast here, which is the simplest solution and it still
/// provides reasonable level of type safety.
inline ZoneFinder::FindOptions operator |(ZoneFinder::FindOptions a,
ZoneFinder::FindOptions b)
{
return (static_cast<ZoneFinder::FindOptions>(static_cast<unsigned>(a) |
static_cast<unsigned>(b)));
}
/// \brief A pointer-like type pointing to a \c ZoneFinder object.
typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
......
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