Parcourir la source

[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.
JINMEI Tatuya il y a 14 ans
Parent
commit
9bfbbabf77

+ 64 - 0
src/lib/datasrc/memory_datasrc.cc

@@ -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)) {

+ 51 - 4
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -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) {