Commit 696cec5b authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge branch 'work/dname/query'

parents 9cf25c82 2c54e9c5
......@@ -141,13 +141,56 @@ Query::process() const {
// Found a zone which is the nearest ancestor to QNAME, set the AA bit
response_.setHeaderFlag(Message::HEADERFLAG_AA);
response_.setRcode(Rcode::NOERROR());
while (keep_doing) {
keep_doing = false;
std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
Zone::FindResult db_result =
result.zone->find(qname_, qtype_, target.get());
const Zone::FindResult db_result(result.zone->find(qname_, qtype_,
target.get()));
switch (db_result.code) {
case Zone::DNAME: {
// First, put the dname into the answer
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset));
/*
* Empty DNAME should never get in, as it is impossible to
* create one in master file.
*
* FIXME: Other way to prevent this should be done
*/
assert(db_result.rrset->getRdataCount() > 0);
// Get the data of DNAME
const rdata::generic::DNAME& dname(
dynamic_cast<const rdata::generic::DNAME&>(
db_result.rrset->getRdataIterator()->getCurrent()));
// The yet unmatched prefix dname
const Name prefix(qname_.split(0, qname_.getLabelCount() -
db_result.rrset->getName().getLabelCount()));
// If we put it together, will it be too long?
// (The prefix contains trailing ., which will be removed
if (prefix.getLength() - Name::ROOT_NAME().getLength() +
dname.getDname().getLength() > Name::MAX_WIRE) {
/*
* In case the synthesized name is too long, section 4.1
* of RFC 2672 mandates we return YXDOMAIN.
*/
response_.setRcode(Rcode::YXDOMAIN());
return;
}
// The new CNAME we are creating (it will be unsigned even
// with DNSSEC, the DNAME is signed and it can be validated
// by that)
RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
RRType::CNAME(), db_result.rrset->getTTL()));
// Construct the new target by replacing the end
cname->addRdata(rdata::generic::CNAME(qname_.split(0,
qname_.getLabelCount() -
db_result.rrset->getName().getLabelCount()).
concatenate(dname.getDname())));
response_.addRRset(Message::SECTION_ANSWER, cname);
break;
}
case Zone::CNAME:
/*
* We don't do chaining yet. Therefore handling a CNAME is
......@@ -155,10 +198,13 @@ Query::process() const {
* what we expected. It means no exceptions in ANY or NS
* on the origin (though CNAME in origin is probably
* forbidden anyway).
*
* So, just put it there.
*/
// No break; here, fall trough.
response_.addRRset(Message::SECTION_ANSWER,
boost::const_pointer_cast<RRset>(db_result.rrset));
break;
case Zone::SUCCESS:
response_.setRcode(Rcode::NOERROR());
if (qtype_is_any) {
// If quety type is ANY, insert all RRs under the domain
// into answer section.
......@@ -184,7 +230,6 @@ Query::process() const {
break;
case Zone::DELEGATION:
response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
response_.setRcode(Rcode::NOERROR());
response_.addRRset(Message::SECTION_AUTHORITY,
boost::const_pointer_cast<RRset>(db_result.rrset));
getAdditional(*result.zone, *db_result.rrset);
......@@ -196,12 +241,8 @@ Query::process() const {
break;
case Zone::NXRRSET:
// Just empty answer with SOA in authority section
response_.setRcode(Rcode::NOERROR());
putSOA(*result.zone);
break;
case Zone::DNAME:
// TODO : replace qname, continue lookup
break;
}
}
}
......
......@@ -75,6 +75,17 @@ const char* const cname_nxdom_txt =
// CNAME Leading out of zone
const char* const cname_out_txt =
"cnameout.example.com. 3600 IN CNAME www.example.org.\n";
// The DNAME to do tests against
const char* const dname_txt =
"dname.example.com. 3600 IN DNAME "
"somethinglong.dnametarget.example.com.\n";
// Some data at the dname node (allowed by RFC 2672)
const char* const dname_a_txt =
"dname.example.com. 3600 IN A 192.0.2.5\n";
// This is not inside the zone, this is created at runtime
const char* const synthetized_cname_txt =
"www.dname.example.com. 3600 IN CNAME "
"www.somethinglong.dnametarget.example.com.\n";
// The rest of data won't be referenced from the test cases.
const char* const other_zone_rrs =
"cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
......@@ -88,13 +99,16 @@ const char* const other_zone_rrs =
// behavior.
// For simplicity, most names are assumed to be "in zone"; there's only
// one zone cut at the point of name "delegation.example.com".
// It doesn't handle empty non terminal nodes (if we need to test such cases
// find() should have specialized code for it).
// Another special name is "dname.example.com". Query names under this name
// will result in DNAME.
// This mock zone doesn't handle empty non terminal nodes (if we need to test
// such cases find() should have specialized code for it).
class MockZone : public Zone {
public:
MockZone() :
origin_(Name("example.com")),
delegation_name_("delegation.example.com"),
dname_name_("dname.example.com"),
has_SOA_(true),
has_apex_NS_(true),
rrclass_(RRClass::IN())
......@@ -102,7 +116,8 @@ public:
stringstream zone_stream;
zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
delegation_txt << mx_txt << www_a_txt << cname_txt <<
cname_nxdom_txt << cname_out_txt << other_zone_rrs;
cname_nxdom_txt << cname_out_txt << dname_txt << dname_a_txt <<
other_zone_rrs;
masterLoad(zone_stream, origin_, rrclass_,
boost::bind(&MockZone::loadRRset, this, _1));
......@@ -131,14 +146,20 @@ private:
if (rrset->getName() == delegation_name_ &&
rrset->getType() == RRType::NS()) {
delegation_rrset_ = rrset;
} else if (rrset->getName() == dname_name_ &&
rrset->getType() == RRType::DNAME()) {
dname_rrset_ = rrset;
}
}
const Name origin_;
// Names where we delegate somewhere else
const Name delegation_name_;
const Name dname_name_;
bool has_SOA_;
bool has_apex_NS_;
ConstRRsetPtr delegation_rrset_;
ConstRRsetPtr dname_rrset_;
const RRClass rrclass_;
};
......@@ -160,6 +181,10 @@ MockZone::find(const Name& name, const RRType& type,
name.compare(delegation_name_).getRelation() ==
NameComparisonResult::SUBDOMAIN)) {
return (FindResult(DELEGATION, delegation_rrset_));
// And under DNAME
} else if (name.compare(dname_name_).getRelation() ==
NameComparisonResult::SUBDOMAIN) {
return (FindResult(DNAME, dname_rrset_));
}
// normal cases. names are searched for only per exact-match basis
......@@ -176,8 +201,7 @@ MockZone::find(const Name& name, const RRType& type,
// If not found but we have a target, fill it with all RRsets here
if (!found_domain->second.empty() && target != NULL) {
for (found_rrset = found_domain->second.begin();
found_rrset != found_domain->second.end(); found_rrset++)
{
found_rrset != found_domain->second.end(); found_rrset++) {
// Insert RRs under the domain name into target
target->addRRset(
boost::const_pointer_cast<RRset>(found_rrset->second));
......@@ -443,8 +467,8 @@ TEST_F(QueryTest, CNAME) {
Query(memory_datasrc, Name("cname.example.com"), RRType::A(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
cname_txt, zone_ns_txt, ns_addrs_txt);
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
cname_txt, NULL, NULL);
}
TEST_F(QueryTest, explicitCNAME) {
......@@ -465,8 +489,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
Query(memory_datasrc, Name("cname.example.com"), RRType::TXT(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
cname_txt, zone_ns_txt, ns_addrs_txt);
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
cname_txt, NULL, NULL);
}
TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
......@@ -488,8 +512,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
Query(memory_datasrc, Name("cnamenxdom.example.com"), RRType::A(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
cname_nxdom_txt, NULL, NULL);
}
TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
......@@ -513,8 +537,8 @@ TEST_F(QueryTest, CNAME_OUT) {
Query(memory_datasrc, Name("cnameout.example.com"), RRType::A(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
cname_out_txt, zone_ns_txt, ns_addrs_txt);
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
cname_out_txt, NULL, NULL);
}
TEST_F(QueryTest, explicitCNAME_OUT) {
......@@ -526,4 +550,129 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
cname_out_txt, zone_ns_txt, ns_addrs_txt);
}
/*
* Test a query under a domain with DNAME. We should get a synthetized CNAME
* as well as the DNAME.
*
* TODO: Once we have CNAME chaining, check it works with synthetized CNAMEs
* as well. This includes tests pointing inside the zone, outside the zone,
* pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
*/
TEST_F(QueryTest, DNAME) {
Query(memory_datasrc, Name("www.dname.example.com"), RRType::A(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
(string(dname_txt) + synthetized_cname_txt).c_str(),
NULL, NULL);
}
/*
* Ask an ANY query below a DNAME. Should return the DNAME and synthetized
* CNAME.
*
* ANY is handled specially sometimes. We check it is not the case with
* DNAME.
*/
TEST_F(QueryTest, DNAME_ANY) {
Query(memory_datasrc, Name("www.dname.example.com"), RRType::ANY(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
(string(dname_txt) + synthetized_cname_txt).c_str(), NULL, NULL);
}
// Test when we ask for DNAME explicitly, it does no synthetizing.
TEST_F(QueryTest, explicitDNAME) {
Query(memory_datasrc, Name("dname.example.com"), RRType::DNAME(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
dname_txt, zone_ns_txt, ns_addrs_txt);
}
/*
* Request a RRset at the domain with DNAME. It should not synthetize
* the CNAME, it should return the RRset.
*/
TEST_F(QueryTest, DNAME_A) {
Query(memory_datasrc, Name("dname.example.com"), RRType::A(),
response).process();
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
dname_a_txt, zone_ns_txt, ns_addrs_txt);
}
/*
* Request a RRset at the domain with DNAME that is not there (NXRRSET).
* It should not synthetize the CNAME.
*/
TEST_F(QueryTest, DNAME_NX_RRSET) {
EXPECT_NO_THROW(Query(memory_datasrc, Name("dname.example.com"),
RRType::TXT(), response).process());
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
NULL, soa_txt, NULL, mock_zone->getOrigin());
}
/*
* Constructing the CNAME will result in a name that is too long. This,
* however, should not throw (and crash the server), but respond with
* YXDOMAIN.
*/
TEST_F(QueryTest, LongDNAME) {
// A name that is as long as it can be
Name longname(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"dname.example.com.");
EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(),
response).process());
responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
dname_txt, NULL, NULL);
}
/*
* Constructing the CNAME will result in a name of maximal length.
* This tests that we don't reject valid one by some kind of off by
* one mistake.
*/
TEST_F(QueryTest, MaxLenDNAME) {
Name longname(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
"dname.example.com.");
EXPECT_NO_THROW(Query(memory_datasrc, longname, RRType::A(),
response).process());
// Check the answer is OK
responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
NULL, NULL, NULL);
// Check that the CNAME has the maximal length.
bool ok(false);
for (RRsetIterator i(response.beginSection(Message::SECTION_ANSWER));
i != response.endSection(Message::SECTION_ANSWER); ++ i) {
if ((*i)->getType() == RRType::CNAME()) {
ok = true;
RdataIteratorPtr ci((*i)->getRdataIterator());
ASSERT_FALSE(ci->isLast()) << "The CNAME is empty";
/*
* Does anybody have a clue why, if the Name::MAX_WIRE is put
* directly inside ASSERT_EQ, it fails to link and complains
* it is unresolved external?
*/
const size_t max_len(Name::MAX_WIRE);
ASSERT_EQ(max_len, dynamic_cast<const rdata::generic::CNAME&>(
ci->getCurrent()).getCname().getLength());
}
}
EXPECT_TRUE(ok) << "The synthetized CNAME not found";
}
}
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