Commit 9bfbbabf authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[trac550] an initial step of loading wildcard names.

make sure a separate RBTree level exists for the wildcard and its
"wildcarding (parent)" names.
reject loading unsupported RR types with wildcard.
parent 3715569d
......@@ -71,6 +71,46 @@ struct MemoryZone::MemoryZoneImpl {
// The actual zone data
DomainTree domains_;
// Add the necessary magic for any wildcard contained in 'name'
// (including itself) to be found in the zone.
//
// In order for wildcard matching to work correctly in find(),
// we must ensure that a node for the wildcarding level exists in the
// backend RBTree.
// E.g. if the wildcard name is "*.sub.example." then we must ensure
// that "sub.example." exists and is marked as a wildcard level.
// Note: the "wildcarding level" is for the parent name of the wildcard
// name (such as "sub.example.").
//
// We also perform the same trick for empty wild card names possibly
// contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
void addWildcards(DomainTree& domains, const Name& name) {
Name wname(name);
const unsigned int labels(wname.getLabelCount());
const unsigned int origin_labels(origin_.getLabelCount());
for (unsigned int l = labels;
l > origin_labels;
--l, wname = wname.split(1)) {
if (wname.isWildcard()) {
DomainNode* node;
DomainTree::Result result;
// Ensure a separate level exists for the wildcard name.
// Note: for 'name' itself we do this later anyway, but the
// overhead should be marginal because wildcard names should
// be rare.
result = domains.insert(wname.split(1), &node);
assert(result == DomainTree::SUCCESS ||
result == DomainTree::ALREADYEXISTS);
// Ensure a separate level exists for the "wildcarding" name
result = domains.insert(wname, &node);
assert(result == DomainTree::SUCCESS ||
result == DomainTree::ALREADYEXISTS);
}
}
}
/*
* Does some checks in context of the data that are already in the zone.
* Currently checks for forbidden combinations of RRsets in the same
......@@ -144,6 +184,30 @@ struct MemoryZone::MemoryZoneImpl {
isc_throw(OutOfZone, "The name " << name <<
" is not contained in zone " << origin_);
}
// Some RR types do not really work well with a wildcard.
// Even though the protocol specifically doesn't completely ban such
// usage, we refuse to load a zone containing such RR in order to
// keep the lookup logic simpler and more predictable.
// See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
// for more technical background. Note also that BIND 9 refuses
// NS at a wildcard, so in that sense we simply provide compatible
// behavior.
if (name.isWildcard()) {
if (rrset->getType() == RRType::NS()) {
isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
name);
}
if (rrset->getType() == RRType::DNAME()) {
isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
name);
}
}
// Add wildcards possibly contained in the owner name to the domain
// tree.
addWildcards(*domains, name);
// Get the node
DomainNode* node;
switch (domains->insert(name, &node)) {
......
......@@ -197,7 +197,12 @@ public:
&rr_child_dname_},
{"example.com. 300 IN A 192.0.2.10", &rr_out_},
{"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
{"bar.tmp.example.org. 300 IN A 192.0.2.1", &rr_tmp2_},
{"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
{"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
&rr_nested_emptywild_},
{"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
{"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
&rr_dnamewild_},
{NULL, NULL}
};
......@@ -243,7 +248,10 @@ public:
RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
RRsetPtr rr_child_dname_; // A DNAME under NS
RRsetPtr rr_wild_, rr_tmp2_;
RRsetPtr rr_wild_;
RRsetPtr rr_emptywild_;
RRsetPtr rr_nested_emptywild_;
RRsetPtr rr_nswild_, rr_dnamewild_;
/**
* \brief Test one find query to the zone.
......@@ -609,10 +617,49 @@ TEST_F(MemoryZoneTest, load) {
MasterLoadError);
}
// Note: once #507 is merged, findTest() would succeed whether or not
// we load the wildcard correctly, so the test will become meaningless.
// The plan is to clean them up when we complete #551 (then the effect of
// load will be indirectly tested via find() tests).
TEST_F(MemoryZoneTest, loadWildcard) {
/*
* example.org.
* |
* wild (not *.wild, should have wild mark)
* |
* *
*/
EXPECT_EQ(SUCCESS, zone_.add(rr_wild_));
EXPECT_EQ(SUCCESS, zone_.add(rr_tmp2_));
findTest(Name("tmp.example.org"), RRType::A(), Zone::NXRRSET);
findTest(Name("wild.example.org"), RRType::A(), Zone::NXRRSET);
}
// same note as loadWildcard applies.
TEST_F(MemoryZoneTest, loadEmptyWildcard) {
/*
* example.org.
* foo
* *
* wild
*/
EXPECT_EQ(SUCCESS, zone_.add(rr_emptywild_));
findTest(Name("*.foo.example.org"), RRType::A(), Zone::NXRRSET);
findTest(Name("foo.example.org"), RRType::A(), Zone::NXRRSET);
}
// same note as loadWildcard applies.
TEST_F(MemoryZoneTest, loadNestedEmptyWildcard) {
EXPECT_EQ(SUCCESS, zone_.add(rr_nested_emptywild_));
findTest(Name("*.foo.*.bar.example.org"), RRType::A(), Zone::NXRRSET);
findTest(Name("foo.*.bar.example.org"), RRType::A(), Zone::NXRRSET);
findTest(Name("*.bar.example.org"), RRType::A(), Zone::NXRRSET);
findTest(Name("bar.example.org"), RRType::A(), Zone::NXRRSET);
}
TEST_F(MemoryZoneTest, loadBadWildcard) {
// We reject loading the zone if it contains a wildcard name for
// NS or DNAME.
EXPECT_THROW(zone_.add(rr_nswild_), MemoryZone::AddError);
EXPECT_THROW(zone_.add(rr_dnamewild_), MemoryZone::AddError);
}
TEST_F(MemoryZoneTest, swap) {
......
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