Parcourir la source

[master] Merge branch 'trac550' with resolving conflicts.

JINMEI Tatuya il y a 14 ans
Parent
commit
ca368d0083

+ 92 - 24
src/lib/datasrc/memory_datasrc.cc

@@ -36,7 +36,7 @@ struct MemoryZone::MemoryZoneImpl {
     // Constructor
     // Constructor
     MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
     MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
         zone_class_(zone_class), origin_(origin), origin_data_(NULL),
         zone_class_(zone_class), origin_(origin), origin_data_(NULL),
-        domains_(true) 
+        domains_(true)
     {
     {
         // We create the node for origin (it needs to exist anyway in future)
         // We create the node for origin (it needs to exist anyway in future)
         domains_.insert(origin, &origin_data_);
         domains_.insert(origin, &origin_data_);
@@ -62,6 +62,7 @@ struct MemoryZone::MemoryZoneImpl {
     // The tree stores domains
     // The tree stores domains
     typedef RBTree<Domain> DomainTree;
     typedef RBTree<Domain> DomainTree;
     typedef RBNode<Domain> DomainNode;
     typedef RBNode<Domain> DomainNode;
+    static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
 
 
     // Information about the zone
     // Information about the zone
     RRClass zone_class_;
     RRClass zone_class_;
@@ -72,6 +73,47 @@ struct MemoryZone::MemoryZoneImpl {
     // The actual zone data
     // The actual zone data
     DomainTree domains_;
     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()) {
+                // 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.
+                DomainNode* node;
+                DomainTree::Result result(domains.insert(wname.split(1),
+                                                         &node));
+                assert(result == DomainTree::SUCCESS ||
+                       result == DomainTree::ALREADYEXISTS);
+
+                // Ensure a separate level exists for the "wildcarding" name,
+                // and mark the node as "wild".
+                result = domains.insert(wname, &node);
+                assert(result == DomainTree::SUCCESS ||
+                       result == DomainTree::ALREADYEXISTS);
+                node->setFlag(DOMAINFLAG_WILD);
+            }
+        }
+    }
+
     /*
     /*
      * Does some checks in context of the data that are already in the zone.
      * Does some checks in context of the data that are already in the zone.
      * Currently checks for forbidden combinations of RRsets in the same
      * Currently checks for forbidden combinations of RRsets in the same
@@ -114,13 +156,10 @@ struct MemoryZone::MemoryZoneImpl {
         }
         }
     }
     }
 
 
-    /*
-     * Implementation of longer methods. We put them here, because the
-     * access is without the impl_-> and it will get inlined anyway.
-     */
-    // Implementation of MemoryZone::add
-    result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
-        // Sanitize input
+    // Validate rrset before adding it to the zone.  If something is wrong
+    // it throws an exception.  It doesn't modify the zone, and provides
+    // the strong exception guarantee.
+    void addValidation(const ConstRRsetPtr rrset) {
         if (!rrset) {
         if (!rrset) {
             isc_throw(NullRRset, "The rrset provided is NULL");
             isc_throw(NullRRset, "The rrset provided is NULL");
         }
         }
@@ -137,26 +176,55 @@ struct MemoryZone::MemoryZoneImpl {
                       << rrset->getName());
                       << rrset->getName());
         }
         }
 
 
-        Name name(rrset->getName());
-        NameComparisonResult compare(origin_.compare(name));
+        NameComparisonResult compare(origin_.compare(rrset->getName()));
         if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
         if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
             compare.getRelation() != NameComparisonResult::EQUAL)
             compare.getRelation() != NameComparisonResult::EQUAL)
         {
         {
-            isc_throw(OutOfZone, "The name " << name <<
+            isc_throw(OutOfZone, "The name " << rrset->getName() <<
                 " is not contained in zone " << origin_);
                 " 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 (rrset->getName().isWildcard()) {
+            if (rrset->getType() == RRType::NS()) {
+                isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
+                          rrset->getName());
+            }
+            if (rrset->getType() == RRType::DNAME()) {
+                isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
+                          rrset->getName());
+            }
+        }
+    }
+
+    /*
+     * Implementation of longer methods. We put them here, because the
+     * access is without the impl_-> and it will get inlined anyway.
+     */
+    // Implementation of MemoryZone::add
+    result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
+        // Sanitize input
+        addValidation(rrset);
+
+        // Add wildcards possibly contained in the owner name to the domain
+        // tree.
+        // Note: this can throw an exception, breaking strong exception
+        // guarantee.  (see also the note for contextCheck() below).
+        addWildcards(*domains, rrset->getName());
+
         // Get the node
         // Get the node
         DomainNode* node;
         DomainNode* node;
-        switch (domains->insert(name, &node)) {
-            // Just check it returns reasonable results
-            case DomainTree::SUCCESS:
-            case DomainTree::ALREADYEXISTS:
-                break;
-            // Something odd got out
-            default:
-                assert(0);
-        }
-        assert(node != NULL);
+        DomainTree::Result result = domains->insert(rrset->getName(), &node);
+        // Just check it returns reasonable results
+        assert((result == DomainTree::SUCCESS ||
+                result == DomainTree::ALREADYEXISTS) && node!= NULL);
 
 
         // Now get the domain
         // Now get the domain
         DomainPtr domain;
         DomainPtr domain;
@@ -183,10 +251,10 @@ struct MemoryZone::MemoryZoneImpl {
             // indicating the need for callback in find().
             // indicating the need for callback in find().
             if (rrset->getType() == RRType::NS() &&
             if (rrset->getType() == RRType::NS() &&
                 rrset->getName() != origin_) {
                 rrset->getName() != origin_) {
-                node->enableCallback();
+                node->setFlag(DomainNode::FLAG_CALLBACK);
             // If it is DNAME, we have a callback as well here
             // If it is DNAME, we have a callback as well here
             } else if (rrset->getType() == RRType::DNAME()) {
             } else if (rrset->getType() == RRType::DNAME()) {
-                node->enableCallback();
+                node->setFlag(DomainNode::FLAG_CALLBACK);
             }
             }
 
 
             return (result::SUCCESS);
             return (result::SUCCESS);
@@ -349,7 +417,7 @@ struct MemoryZone::MemoryZoneImpl {
 
 
         // If the node callback is enabled, this may be a zone cut.  If it
         // If the node callback is enabled, this may be a zone cut.  If it
         // has a NS RR, we should return a delegation, but not in the apex.
         // has a NS RR, we should return a delegation, but not in the apex.
-        if (node->isCallbackEnabled() && node != origin_data_) {
+        if (node->getFlag(DomainNode::FLAG_CALLBACK) && node != origin_data_) {
             found = node->getData()->find(RRType::NS());
             found = node->getData()->find(RRType::NS());
             if (found != node->getData()->end()) {
             if (found != node->getData()->end()) {
                 return (FindResult(DELEGATION, found->second));
                 return (FindResult(DELEGATION, found->second));

+ 78 - 14
src/lib/datasrc/rbtree.h

@@ -23,6 +23,8 @@
 ///     issue, the design and interface are not fixed, and RBTree isn't ready
 ///     issue, the design and interface are not fixed, and RBTree isn't ready
 ///     to be used as a base data structure by other modules.
 ///     to be used as a base data structure by other modules.
 
 
+#include <exceptions/exceptions.h>
+
 #include <dns/name.h>
 #include <dns/name.h>
 #include <boost/utility.hpp>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
@@ -110,6 +112,29 @@ public:
     /// \brief Alias for shared pointer to the data.
     /// \brief Alias for shared pointer to the data.
     typedef boost::shared_ptr<T> NodeDataPtr;
     typedef boost::shared_ptr<T> NodeDataPtr;
 
 
+    /// Node flags.
+    ///
+    /// Each flag value defines a non default property for a specific node.
+    /// These are defined as bitmask type values for the convenience of
+    /// internal implementation, but applications are expected to use
+    /// each flag separately via the enum definitions.
+    ///
+    /// All (settable) flags are off by default; they must be explicitly
+    /// set to on by the \c setFlag() method.
+    enum Flags {
+        FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
+        FLAG_USER1 = 0x8000000U ///< Application specific flag
+    };
+private:
+    // Some flag values are expected to be used for internal purposes
+    // (e.g., representing the node color) in future versions, so we
+    // limit the settable flags via the \c setFlag() method to those
+    // explicitly defined in \c Flags.  This constant represents all
+    // such flags.
+    static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1);
+
+public:
+
     /// \brief Destructor
     /// \brief Destructor
     ///
     ///
     /// It might seem strange that constructors are private and destructor
     /// It might seem strange that constructors are private and destructor
@@ -153,6 +178,52 @@ public:
     void setData(const NodeDataPtr& data) { data_ = data; }
     void setData(const NodeDataPtr& data) { data_ = data; }
     //@}
     //@}
 
 
+    /// \name Node flag manipulation methods
+    //@{
+    /// Get the status of a node flag.
+    ///
+    /// This method returns whether the given node flag is set (enabled)
+    /// on the node.  The \c flag parameter is expected to be one of the
+    /// defined \c Flags constants.  For simplicity, the method interface
+    /// does not prohibit passing an undefined flag or combined flags, but
+    /// the return value in such a case will be meaningless for the caller
+    /// (an application would have to use an ugly cast for such an unintended
+    /// form of call, which will hopefully avoid accidental misuse).
+    ///
+    /// \exception None
+    /// \param flag The flag to be tested.
+    /// \return \c true if the \c flag is set; \c false otherwise.
+    bool getFlag(Flags flag) const {
+        return ((flags_ & flag) != 0);
+    }
+
+    /// Set or clear a node flag.
+    ///
+    /// This method changes the status of the specified node flag to either
+    /// "on" (enabled) or "off" (disabled).  The new status is specified by
+    /// the \c on parameter.
+    /// Like the \c getFlag() method, \c flag is expected to be one of the
+    /// defined \c Flags constants.  If an undefined or unsettable flag is
+    /// specified, \c isc::InvalidParameter exception will be thrown.
+    ///
+    /// \exception isc::InvalidParameter Unsettable flag is specified
+    /// \exception None otherwise
+    /// \param flag The node flag to be changed.
+    /// \on If \c true, set the flag to on; otherwise set it to off.
+    void setFlag(Flags flag, bool on = true) {
+        if ((flag & ~SETTABLE_FLAGS) != 0) {
+            isc_throw(isc::InvalidParameter,
+                      "Unsettable RBTree flag is being set");
+        }
+        if (on) {
+            flags_ |= flag;
+        } else {
+            flags_ &= ~flag;
+        }
+    }
+    //@}
+
+private:
     /// \name Callback related methods
     /// \name Callback related methods
     ///
     ///
     /// See the description of \c RBTree<T>::find() about callbacks.
     /// See the description of \c RBTree<T>::find() about callbacks.
@@ -160,16 +231,8 @@ public:
     /// These methods never throw an exception.
     /// These methods never throw an exception.
     //@{
     //@{
     /// Return if callback is enabled at the node.
     /// Return if callback is enabled at the node.
-    bool isCallbackEnabled() const { return (callback_required_); }
-
-    /// Enable callback at the node.
-    void enableCallback() { callback_required_ = true; }
-
-    /// Disable callback at the node.
-    void disableCallback() { callback_required_ = false; }
     //@}
     //@}
 
 
-
 private:
 private:
     /// \brief Define rbnode color
     /// \brief Define rbnode color
     enum RBNodeColor {BLACK, RED};
     enum RBNodeColor {BLACK, RED};
@@ -224,7 +287,7 @@ private:
     /// RBTree::find().
     /// RBTree::find().
     ///
     ///
     /// \todo It might be needed to put it into more general attributes field.
     /// \todo It might be needed to put it into more general attributes field.
-    bool callback_required_;
+    uint32_t flags_;
 };
 };
 
 
 
 
@@ -238,7 +301,7 @@ RBNode<T>::RBNode() :
     // dummy name, the value doesn't matter:
     // dummy name, the value doesn't matter:
     name_(isc::dns::Name::ROOT_NAME()),
     name_(isc::dns::Name::ROOT_NAME()),
     down_(this),
     down_(this),
-    callback_required_(false)
+    flags_(0)
 {
 {
 }
 }
 
 
@@ -250,7 +313,7 @@ RBNode<T>::RBNode(const isc::dns::Name& name) :
     color_(RED),
     color_(RED),
     name_(name),
     name_(name),
     down_(NULL_NODE()),
     down_(NULL_NODE()),
-    callback_required_(false)
+    flags_(0)
 {
 {
 }
 }
 
 
@@ -650,7 +713,7 @@ public:
     ///
     ///
     /// This version of \c find() calls the callback whenever traversing (on
     /// This version of \c find() calls the callback whenever traversing (on
     /// the way from root down the tree) a marked node on the way down through
     /// the way from root down the tree) a marked node on the way down through
-    /// the domain namespace (see RBNode::enableCallback and related
+    /// the domain namespace (see \c RBNode::enableCallback and related
     /// functions).
     /// functions).
     ///
     ///
     /// If you return true from the callback, the search is stopped and a
     /// If you return true from the callback, the search is stopped and a
@@ -944,7 +1007,8 @@ RBTree<T>::find(const isc::dns::Name& target_name,
                 if (needsReturnEmptyNode_ || !node->isEmpty()) {
                 if (needsReturnEmptyNode_ || !node->isEmpty()) {
                     ret = PARTIALMATCH;
                     ret = PARTIALMATCH;
                     *target = node;
                     *target = node;
-                    if (callback != NULL && node->callback_required_) {
+                    if (callback != NULL &&
+                        node->getFlag(RBNode<T>::FLAG_CALLBACK)) {
                         if ((callback)(*node, callback_arg)) {
                         if ((callback)(*node, callback_arg)) {
                             break;
                             break;
                         }
                         }
@@ -1096,7 +1160,7 @@ RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
     // consistent behavior (i.e., a weak form of strong exception guarantee)
     // consistent behavior (i.e., a weak form of strong exception guarantee)
     // even if code after the call to this function throws an exception.
     // even if code after the call to this function throws an exception.
     std::swap(node.data_, down_node->data_);
     std::swap(node.data_, down_node->data_);
-    std::swap(node.callback_required_, down_node->callback_required_);
+    std::swap(node.flags_, down_node->flags_);
     down_node->down_ = node.down_;
     down_node->down_ = node.down_;
     node.down_ = down_node.get();
     node.down_ = down_node.get();
     // root node of sub tree, the initial color is BLACK
     // root node of sub tree, the initial color is BLACK

+ 151 - 74
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -13,8 +13,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
+#include <sstream>
+#include <vector>
+
+#include <boost/bind.hpp>
+
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
+#include <dns/masterload.h>
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #include <dns/rdataclass.h>
@@ -27,6 +33,7 @@
 
 
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
+using namespace std;
 using namespace isc::dns;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using namespace isc::dns::rdata;
 using namespace isc::datasrc;
 using namespace isc::datasrc;
@@ -140,62 +147,86 @@ TEST_F(MemoryDataSrcTest, getZoneCount) {
     EXPECT_EQ(2, memory_datasrc.getZoneCount());
     EXPECT_EQ(2, memory_datasrc.getZoneCount());
 }
 }
 
 
+// A helper callback of masterLoad() used in MemoryZoneTest.
+void
+setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
+    *(*it) = rrset;
+    ++it;
+}
+
 /// \brief Test fixture for the MemoryZone class
 /// \brief Test fixture for the MemoryZone class
 class MemoryZoneTest : public ::testing::Test {
 class MemoryZoneTest : public ::testing::Test {
+    // A straightforward pair of textual RR(set) and a RRsetPtr variable
+    // to store the RRset.  Used to build test data below.
+    struct RRsetData {
+        const char* const text; // textual representation of an RRset
+        RRsetPtr* rrset;
+    };
 public:
 public:
     MemoryZoneTest() :
     MemoryZoneTest() :
         class_(RRClass::IN()),
         class_(RRClass::IN()),
         origin_("example.org"),
         origin_("example.org"),
-        ns_name_("ns.example.org"),
-        cname_name_("cname.example.org"),
-        dname_name_("dname.example.org"),
-        child_ns_name_("child.example.org"),
-        child_glue_name_("ns.child.example.org"),
-        grandchild_ns_name_("grand.child.example.org"),
-        grandchild_glue_name_("ns.grand.child.example.org"),
-        child_dname_name_("dname.child.example.org"),
-        zone_(class_, origin_),
-        rr_out_(new RRset(Name("example.com"), class_, RRType::A(),
-            RRTTL(300))),
-        rr_ns_(new RRset(origin_, class_, RRType::NS(), RRTTL(300))),
-        rr_ns_a_(new RRset(ns_name_, class_, RRType::A(), RRTTL(300))),
-        rr_ns_aaaa_(new RRset(ns_name_, class_, RRType::AAAA(), RRTTL(300))),
-        rr_a_(new RRset(origin_, class_, RRType::A(), RRTTL(300))),
-        rr_cname_(new RRset(cname_name_, class_, RRType::CNAME(), RRTTL(300))),
-        rr_cname_a_(new RRset(cname_name_, class_, RRType::A(), RRTTL(300))),
-        rr_dname_(new RRset(dname_name_, class_, RRType::DNAME(), RRTTL(300))),
-        rr_dname_a_(new RRset(dname_name_, class_, RRType::A(),
-            RRTTL(300))),
-        rr_dname_ns_(new RRset(dname_name_, class_, RRType::NS(), RRTTL(300))),
-        rr_dname_apex_(new RRset(origin_, class_, RRType::DNAME(),
-            RRTTL(300))),
-        rr_child_ns_(new RRset(child_ns_name_, class_, RRType::NS(),
-                               RRTTL(300))),
-        rr_child_glue_(new RRset(child_glue_name_, class_, RRType::A(),
-                              RRTTL(300))),
-        rr_grandchild_ns_(new RRset(grandchild_ns_name_, class_, RRType::NS(),
-                                    RRTTL(300))),
-        rr_grandchild_glue_(new RRset(grandchild_glue_name_, class_,
-                                      RRType::AAAA(), RRTTL(300))),
-        rr_child_dname_(new RRset(child_dname_name_, class_, RRType::DNAME(),
-            RRTTL(300)))
+        zone_(class_, origin_)
     {
     {
+        // Build test RRsets.  Below, we construct an RRset for
+        // each textual RR(s) of zone_data, and assign it to the corresponding
+        // rr_xxx.
+        const RRsetData zone_data[] = {
+            {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
+            {"example.org. 300 IN A 192.0.2.1", &rr_a_},
+            {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
+            {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
+            {"cname.example.org. 300 IN CNAME canonical.example.org",
+             &rr_cname_},
+            {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
+            {"dname.example.org. 300 IN DNAME target.example.org.",
+             &rr_dname_},
+            {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
+            {"dname.example.org. 300 IN NS ns.dname.example.org.",
+             &rr_dname_ns_},
+            {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
+            {"child.example.org. 300 IN NS ns.child.example.org.",
+             &rr_child_ns_},
+            {"ns.child.example.org. 300 IN A 192.0.2.153",
+             &rr_child_glue_},
+            {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
+             &rr_grandchild_ns_},
+            {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
+             &rr_grandchild_glue_},
+            {"dname.child.example.org. 300 IN DNAME example.com.",
+             &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_},
+            {"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}
+        };
+
+        stringstream zone_data_stream;
+        vector<RRsetPtr*> rrsets;
+        for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
+            zone_data_stream << zone_data[i].text << "\n";
+            rrsets.push_back(zone_data[i].rrset);
+        }
+
+        vector<RRsetPtr*>::iterator it = rrsets.begin();
+        masterLoad(zone_data_stream, Name::ROOT_NAME(), class_,
+                   boost::bind(setRRset, _1, it));
     }
     }
     // Some data to test with
     // Some data to test with
     const RRClass class_;
     const RRClass class_;
-    const Name origin_, ns_name_, cname_name_, dname_name_, child_ns_name_,
-        child_glue_name_, grandchild_ns_name_, grandchild_glue_name_,
-        child_dname_name_;
+    const Name origin_;
     // The zone to torture by tests
     // The zone to torture by tests
     MemoryZone zone_;
     MemoryZone zone_;
 
 
     /*
     /*
      * Some RRsets to put inside the zone.
      * Some RRsets to put inside the zone.
-     * They are empty, but the MemoryZone does not have a reason to look
-     * inside anyway. We will check it finds them and does not change
-     * the pointer.
      */
      */
-    ConstRRsetPtr
+    RRsetPtr
         // Out of zone RRset
         // Out of zone RRset
         rr_out_,
         rr_out_,
         // NS of example.org
         // NS of example.org
@@ -207,16 +238,20 @@ public:
         // A of example.org
         // A of example.org
         rr_a_;
         rr_a_;
     RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
     RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
-    ConstRRsetPtr rr_cname_a_; // for mixed CNAME + A case
+    RRsetPtr rr_cname_a_; // for mixed CNAME + A case
     RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
     RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
-    ConstRRsetPtr rr_dname_a_; // for mixed DNAME + A case
-    ConstRRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
-    ConstRRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
-    ConstRRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
-    ConstRRsetPtr rr_child_glue_; // glue RR of the child domain
-    ConstRRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
-    ConstRRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
-    ConstRRsetPtr rr_child_dname_; // A DNAME under NS
+    RRsetPtr rr_dname_a_; // for mixed DNAME + A case
+    RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
+    RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
+    RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+    RRsetPtr rr_child_glue_; // glue RR of the child domain
+    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_;
+    RRsetPtr rr_emptywild_;
+    RRsetPtr rr_nested_emptywild_;
+    RRsetPtr rr_nswild_, rr_dnamewild_;
 
 
     /**
     /**
      * \brief Test one find query to the zone.
      * \brief Test one find query to the zone.
@@ -292,33 +327,30 @@ TEST_F(MemoryZoneTest, add) {
 }
 }
 
 
 TEST_F(MemoryZoneTest, addMultipleCNAMEs) {
 TEST_F(MemoryZoneTest, addMultipleCNAMEs) {
-    rr_cname_->addRdata(generic::CNAME("canonical1.example.org."));
     rr_cname_->addRdata(generic::CNAME("canonical2.example.org."));
     rr_cname_->addRdata(generic::CNAME("canonical2.example.org."));
     EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
     EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
 }
 }
 
 
 TEST_F(MemoryZoneTest, addCNAMEThenOther) {
 TEST_F(MemoryZoneTest, addCNAMEThenOther) {
-    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
     EXPECT_THROW(zone_.add(rr_cname_a_), MemoryZone::AddError);
     EXPECT_THROW(zone_.add(rr_cname_a_), MemoryZone::AddError);
 }
 }
 
 
 TEST_F(MemoryZoneTest, addOtherThenCNAME) {
 TEST_F(MemoryZoneTest, addOtherThenCNAME) {
-    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_a_));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_a_));
     EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
     EXPECT_THROW(zone_.add(rr_cname_), MemoryZone::AddError);
 }
 }
 
 
 TEST_F(MemoryZoneTest, findCNAME) {
 TEST_F(MemoryZoneTest, findCNAME) {
     // install CNAME RR
     // install CNAME RR
-    rr_cname_->addRdata(generic::CNAME("canonical.example.org."));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
     EXPECT_EQ(SUCCESS, zone_.add(rr_cname_));
 
 
     // Find A RR of the same.  Should match the CNAME
     // Find A RR of the same.  Should match the CNAME
-    findTest(cname_name_, RRType::NS(), Zone::CNAME, true, rr_cname_);
+    findTest(rr_cname_->getName(), RRType::NS(), Zone::CNAME, true, rr_cname_);
 
 
     // Find the CNAME itself.  Should result in normal SUCCESS
     // Find the CNAME itself.  Should result in normal SUCCESS
-    findTest(cname_name_, RRType::CNAME(), Zone::SUCCESS, true, rr_cname_);
+    findTest(rr_cname_->getName(), RRType::CNAME(), Zone::SUCCESS, true,
+             rr_cname_);
 }
 }
 
 
 TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) {
 TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) {
@@ -339,8 +371,7 @@ TEST_F(MemoryZoneTest, findCNAMEUnderZoneCut) {
 // Having a CNAME there is disallowed too, but it is tested by
 // Having a CNAME there is disallowed too, but it is tested by
 // addOtherThenCNAME and addCNAMEThenOther.
 // addOtherThenCNAME and addCNAMEThenOther.
 TEST_F(MemoryZoneTest, addMultipleDNAMEs) {
 TEST_F(MemoryZoneTest, addMultipleDNAMEs) {
-    rr_dname_->addRdata(generic::DNAME("dname1.example.org."));
-    rr_dname_->addRdata(generic::DNAME("dname2.example.org."));
+    rr_dname_->addRdata(generic::DNAME("target2.example.org."));
     EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError);
     EXPECT_THROW(zone_.add(rr_dname_), MemoryZone::AddError);
 }
 }
 
 
@@ -366,7 +397,8 @@ TEST_F(MemoryZoneTest, DNAMEAndNSAtApex) {
     // The NS should be possible to be found, below should be DNAME, not
     // The NS should be possible to be found, below should be DNAME, not
     // delegation
     // delegation
     findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
     findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
-    findTest(child_ns_name_, RRType::A(), Zone::DNAME, true, rr_dname_apex_);
+    findTest(rr_child_ns_->getName(), RRType::A(), Zone::DNAME, true,
+             rr_dname_apex_);
 }
 }
 
 
 TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) {
 TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) {
@@ -379,7 +411,6 @@ TEST_F(MemoryZoneTest, NSAndDNAMEAtApex) {
 
 
 // Search under a DNAME record. It should return the DNAME
 // Search under a DNAME record. It should return the DNAME
 TEST_F(MemoryZoneTest, findBelowDNAME) {
 TEST_F(MemoryZoneTest, findBelowDNAME) {
-    rr_dname_->addRdata(generic::DNAME("target.example.org."));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
     findTest(Name("below.dname.example.org"), RRType::A(), Zone::DNAME, true,
     findTest(Name("below.dname.example.org"), RRType::A(), Zone::DNAME, true,
         rr_dname_);
         rr_dname_);
@@ -388,13 +419,13 @@ TEST_F(MemoryZoneTest, findBelowDNAME) {
 // Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
 // Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
 // influences only the data below (see RFC 2672, section 3)
 // influences only the data below (see RFC 2672, section 3)
 TEST_F(MemoryZoneTest, findAtDNAME) {
 TEST_F(MemoryZoneTest, findAtDNAME) {
-    rr_dname_->addRdata(generic::DNAME("target.example.org."));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_a_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_dname_a_)));
 
 
-    findTest(dname_name_, RRType::A(), Zone::SUCCESS, true, rr_dname_a_);
-    findTest(dname_name_, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_);
-    findTest(dname_name_, RRType::TXT(), Zone::NXRRSET, true);
+    const Name dname_name(rr_dname_->getName());
+    findTest(dname_name, RRType::A(), Zone::SUCCESS, true, rr_dname_a_);
+    findTest(dname_name, RRType::DNAME(), Zone::SUCCESS, true, rr_dname_);
+    findTest(dname_name, RRType::TXT(), Zone::NXRRSET, true);
 }
 }
 
 
 // Try searching something that is both under NS and DNAME, without and with
 // Try searching something that is both under NS and DNAME, without and with
@@ -458,7 +489,7 @@ TEST_F(MemoryZoneTest, findAny) {
     EXPECT_EQ(0, out_rrsets.size());
     EXPECT_EQ(0, out_rrsets.size());
 
 
     RRsetList glue_child_rrsets;
     RRsetList glue_child_rrsets;
-    findTest(child_glue_name_, RRType::ANY(), Zone::SUCCESS, true,
+    findTest(rr_child_glue_->getName(), RRType::ANY(), Zone::SUCCESS, true,
                 ConstRRsetPtr(), &glue_child_rrsets);
                 ConstRRsetPtr(), &glue_child_rrsets);
     EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(),
     EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(),
                                                      RRClass::IN()));
                                                      RRClass::IN()));
@@ -472,13 +503,13 @@ TEST_F(MemoryZoneTest, findAny) {
 
 
     // zone cut
     // zone cut
     RRsetList child_rrsets;
     RRsetList child_rrsets;
-    findTest(child_ns_name_, RRType::ANY(), Zone::DELEGATION, true,
+    findTest(rr_child_ns_->getName(), RRType::ANY(), Zone::DELEGATION, true,
              rr_child_ns_, &child_rrsets);
              rr_child_ns_, &child_rrsets);
     EXPECT_EQ(0, child_rrsets.size());
     EXPECT_EQ(0, child_rrsets.size());
 
 
     // glue for this zone cut
     // glue for this zone cut
     RRsetList new_glue_child_rrsets;
     RRsetList new_glue_child_rrsets;
-    findTest(child_glue_name_, RRType::ANY(), Zone::DELEGATION, true,
+    findTest(rr_child_glue_->getName(), RRType::ANY(), Zone::DELEGATION, true,
                 rr_child_ns_, &new_glue_child_rrsets);
                 rr_child_ns_, &new_glue_child_rrsets);
     EXPECT_EQ(0, new_glue_child_rrsets.size());
     EXPECT_EQ(0, new_glue_child_rrsets.size());
 }
 }
@@ -495,16 +526,16 @@ TEST_F(MemoryZoneTest, glue) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_)));
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_.add(rr_grandchild_glue_)));
 
 
     // by default glue is hidden due to the zone cut
     // by default glue is hidden due to the zone cut
-    findTest(child_glue_name_, RRType::A(), Zone::DELEGATION, true,
+    findTest(rr_child_glue_->getName(), RRType::A(), Zone::DELEGATION, true,
              rr_child_ns_);
              rr_child_ns_);
 
 
 
 
     // If we do it in the "glue OK" mode, we should find the exact match.
     // If we do it in the "glue OK" mode, we should find the exact match.
-    findTest(child_glue_name_, RRType::A(), Zone::SUCCESS, true,
+    findTest(rr_child_glue_->getName(), RRType::A(), Zone::SUCCESS, true,
              rr_child_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
              rr_child_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
 
 
     // glue OK + NXRRSET case
     // glue OK + NXRRSET case
-    findTest(child_glue_name_, RRType::AAAA(), Zone::NXRRSET, true,
+    findTest(rr_child_glue_->getName(), RRType::AAAA(), Zone::NXRRSET, true,
              ConstRRsetPtr(), NULL, NULL, Zone::FIND_GLUE_OK);
              ConstRRsetPtr(), NULL, NULL, Zone::FIND_GLUE_OK);
 
 
     // glue OK + NXDOMAIN case
     // glue OK + NXDOMAIN case
@@ -517,7 +548,8 @@ TEST_F(MemoryZoneTest, glue) {
     // (This case cannot be tested yet)
     // (This case cannot be tested yet)
 
 
     // nested cut case.  The glue should be found.
     // nested cut case.  The glue should be found.
-    findTest(grandchild_glue_name_, RRType::AAAA(), Zone::SUCCESS,
+    findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
+             Zone::SUCCESS,
              true, rr_grandchild_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
              true, rr_grandchild_glue_, NULL, NULL, Zone::FIND_GLUE_OK);
 
 
     // A non-existent name in nested cut.  This should result in delegation
     // A non-existent name in nested cut.  This should result in delegation
@@ -544,11 +576,11 @@ TEST_F(MemoryZoneTest, find) {
 
 
     // These two should be successful
     // These two should be successful
     findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
     findTest(origin_, RRType::NS(), Zone::SUCCESS, true, rr_ns_);
-    findTest(ns_name_, RRType::A(), Zone::SUCCESS, true, rr_ns_a_);
+    findTest(rr_ns_a_->getName(), RRType::A(), Zone::SUCCESS, true, rr_ns_a_);
 
 
     // These domain exist but don't have the provided RRType
     // These domain exist but don't have the provided RRType
     findTest(origin_, RRType::AAAA(), Zone::NXRRSET);
     findTest(origin_, RRType::AAAA(), Zone::NXRRSET);
-    findTest(ns_name_, RRType::NS(), Zone::NXRRSET);
+    findTest(rr_ns_a_->getName(), RRType::NS(), Zone::NXRRSET);
 
 
     // These domains don't exist (and one is out of the zone)
     // These domains don't exist (and one is out of the zone)
     findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN);
     findTest(Name("nothere.example.org"), RRType::A(), Zone::NXDOMAIN);
@@ -616,14 +648,59 @@ TEST_F(MemoryZoneTest, load) {
     findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false,
     findTest(Name("a.root-servers.net."), RRType::A(), Zone::SUCCESS, false,
         ConstRRsetPtr(), NULL, &rootzone);
         ConstRRsetPtr(), NULL, &rootzone);
     // But this should no longer be here
     // But this should no longer be here
-    findTest(ns_name_, RRType::AAAA(), Zone::NXDOMAIN, true, ConstRRsetPtr(),
-        NULL, &rootzone);
+    findTest(rr_ns_a_->getName(), RRType::AAAA(), Zone::NXDOMAIN, true,
+             ConstRRsetPtr(), NULL, &rootzone);
 
 
     // Try loading zone that is wrong in a different way
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
     EXPECT_THROW(zone_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
         MasterLoadError);
         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_));
+    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) {
 TEST_F(MemoryZoneTest, swap) {
     // build one zone with some data
     // build one zone with some data
     MemoryZone zone1(class_, origin_);
     MemoryZone zone1(class_, origin_);

+ 34 - 10
src/lib/datasrc/tests/rbtree_unittest.cc

@@ -187,6 +187,30 @@ TEST_F(RBTreeTest, findError) {
                  BadValue);
                  BadValue);
 }
 }
 
 
+TEST_F(RBTreeTest, flags) {
+    EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("flags.example"),
+                                                  &rbtnode));
+
+    // by default, flags are all off
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // set operation, by default it enables the flag
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // try disable the flag explicitly
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, false);
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // try enable the flag explicitly
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, true);
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+
+    // setting an unknown flag will trigger an exception
+    EXPECT_THROW(rbtnode->setFlag(static_cast<RBNode<int>::Flags>(2), true),
+                 isc::InvalidParameter);
+}
+
 bool
 bool
 testCallback(const RBNode<int>&, bool* callack_checker) {
 testCallback(const RBNode<int>&, bool* callack_checker) {
     *callack_checker = true;
     *callack_checker = true;
@@ -198,16 +222,16 @@ TEST_F(RBTreeTest, callback) {
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("callback.example"),
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("callback.example"),
                                                   &rbtnode));
                                                   &rbtnode));
     rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
     rbtnode->setData(RBNode<int>::NodeDataPtr(new int(1)));
-    EXPECT_FALSE(rbtnode->isCallbackEnabled());
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
 
 
     // enable/re-disable callback
     // enable/re-disable callback
-    rbtnode->enableCallback();
-    EXPECT_TRUE(rbtnode->isCallbackEnabled());
-    rbtnode->disableCallback();
-    EXPECT_FALSE(rbtnode->isCallbackEnabled());
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK, false);
+    EXPECT_FALSE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
 
 
     // enable again for subsequent tests
     // enable again for subsequent tests
-    rbtnode->enableCallback();
+    rbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
     // add more levels below and above the callback node for partial match.
     // add more levels below and above the callback node for partial match.
     RBNode<int>* subrbtnode;
     RBNode<int>* subrbtnode;
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("sub.callback.example"),
     EXPECT_EQ(RBTree<int>::SUCCESS, rbtree.insert(Name("sub.callback.example"),
@@ -221,9 +245,9 @@ TEST_F(RBTreeTest, callback) {
     // it.
     // it.
     EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
     EXPECT_EQ(RBTree<int>::EXACTMATCH, rbtree.find(Name("callback.example"),
                                                    &rbtnode));
                                                    &rbtnode));
-    EXPECT_TRUE(rbtnode->isCallbackEnabled());
-    EXPECT_FALSE(subrbtnode->isCallbackEnabled());
-    EXPECT_FALSE(parentrbtnode->isCallbackEnabled());
+    EXPECT_TRUE(rbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+    EXPECT_FALSE(subrbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
+    EXPECT_FALSE(parentrbtnode->getFlag(RBNode<int>::FLAG_CALLBACK));
 
 
     // check if the callback is called from find()
     // check if the callback is called from find()
     RBTreeNodeChain<int> node_path1;
     RBTreeNodeChain<int> node_path1;
@@ -236,7 +260,7 @@ TEST_F(RBTreeTest, callback) {
     // enable callback at the parent node, but it doesn't have data so
     // enable callback at the parent node, but it doesn't have data so
     // the callback shouldn't be called.
     // the callback shouldn't be called.
     RBTreeNodeChain<int> node_path2;
     RBTreeNodeChain<int> node_path2;
-    parentrbtnode->enableCallback();
+    parentrbtnode->setFlag(RBNode<int>::FLAG_CALLBACK);
     callback_called = false;
     callback_called = false;
     EXPECT_EQ(RBTree<int>::EXACTMATCH,
     EXPECT_EQ(RBTree<int>::EXACTMATCH,
               rbtree.find(Name("callback.example"), &crbtnode, node_path2,
               rbtree.find(Name("callback.example"), &crbtnode, node_path2,