Browse Source

[master] Merge branch 'trac1608'

JINMEI Tatuya 13 years ago
parent
commit
1af6565d9d

+ 452 - 50
src/lib/datasrc/memory_datasrc.cc

@@ -12,16 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <algorithm>
-#include <map>
-#include <utility>
-#include <cctype>
-#include <cassert>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/bind.hpp>
-
 #include <exceptions/exceptions.h>
 
 #include <dns/name.h>
@@ -39,6 +29,17 @@
 #include <datasrc/data_source.h>
 #include <datasrc/factory.h>
 
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+#include <cctype>
+#include <cassert>
+
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
@@ -47,8 +48,15 @@ using boost::scoped_ptr;
 namespace isc {
 namespace datasrc {
 
+using namespace internal;
+
 namespace {
 // Some type aliases
+
+// RRset specified for this implementation
+typedef boost::shared_ptr<internal::RBNodeRRset> RBNodeRRsetPtr;
+typedef boost::shared_ptr<const internal::RBNodeRRset> ConstRBNodeRRsetPtr;
+
 /*
  * Each domain consists of some RRsets. They will be looked up by the
  * RRType.
@@ -60,19 +68,44 @@ namespace {
  * critical place and map has better interface for the lookups, so we use
  * that.
  */
-typedef map<RRType, ConstRRsetPtr> Domain;
+typedef map<RRType, ConstRBNodeRRsetPtr> Domain;
 typedef Domain::value_type DomainPair;
 typedef boost::shared_ptr<Domain> DomainPtr;
 // The tree stores domains
 typedef RBTree<Domain> DomainTree;
 typedef RBNode<Domain> DomainNode;
 
+// In the following dedicated namespace we define a few application-specific
+// RBNode flags.  We use a separate namespace so we can consolidate the
+// definition in a single place, which would hopefully reduce the risk of
+// collisions.
+// (Note: it's within an unnamed namespace, so effectively private.)
+namespace domain_flag {
+// This flag indicates the node is at a "wildcard level" (in short, it means
+// one of the node's immediate child is a wildcard).  See addWildcards()
+// for more details.
+const DomainNode::Flags WILD = DomainNode::FLAG_USER1;
+
+// This flag is used for additional record shortcut.  If a node has this
+// flag, it's under a zone cut for a delegation to a child zone.
+// Note: for a statically built zone this information is stable, but if we
+// change the implementation to be dynamically modifiable, it may not be
+// realistic to keep this flag update for all affected nodes, and we may
+// have to reconsider the mechanism.
+const DomainNode::Flags GLUE = DomainNode::FLAG_USER2;
+};
+
 // Separate storage for NSEC3 RRs (and their RRSIGs).  It's an STL map
 // from string to the NSEC3 RRset.  The map key is the first label
 // (upper cased) of the owner name of the corresponding NSEC3 (i.e., map
 // value).  We can use  the standard string comparison (if the comparison
 // target is also upper cased) due to the nature of NSEC3 owner names.
-typedef map<string, ConstRRsetPtr> NSEC3Map;
+//
+// Note: We maintain the RRsets in the form of RBNodeRRset even if they are
+// not stored in the RB tree.  The reason is because comparison can be
+// more efficient if we make sure all RRsets returned from this module are
+// of the same type.
+typedef map<string, ConstRBNodeRRsetPtr> NSEC3Map;
 typedef NSEC3Map::value_type NSEC3Pair;
 
 // Actual zone data: Essentially a set of zone's RRs.  This is defined as
@@ -106,6 +139,275 @@ struct ZoneData {
 };
 }
 
+namespace internal {
+
+/// \brief An encapsulation type for a pointer of an additional node
+/// associated with an \c RBNodeRRset object.
+///
+/// Currently this is defined as a structure only so that it can declared
+/// in rbnode_rrset.h; this is essentially a pointer to \c DomainNode.
+/// In future, however, this structure may have other attributes.
+struct AdditionalNodeInfo {
+    AdditionalNodeInfo(DomainNode* node) : node_(node) {}
+    DomainNode* node_;
+};
+
+//
+// RBNodeRRset details
+//
+struct RBNodeRRsetImpl {
+public:
+    RBNodeRRsetImpl(const ConstRRsetPtr& rrset) : rrset_(rrset)
+    {}
+
+    ConstRRsetPtr rrset_;     ///< Underlying RRset
+    scoped_ptr<vector<AdditionalNodeInfo> > additionals_;
+};
+
+RBNodeRRset::RBNodeRRset(const ConstRRsetPtr& rrset) :
+    impl_(new RBNodeRRsetImpl(rrset))
+{
+}
+
+RBNodeRRset::~RBNodeRRset() {
+    delete impl_;
+}
+
+unsigned int
+RBNodeRRset::getRdataCount() const {
+    return (impl_->rrset_->getRdataCount());
+}
+
+const Name&
+RBNodeRRset::getName() const {
+    return (impl_->rrset_->getName());
+}
+
+const RRClass&
+RBNodeRRset::getClass() const {
+    return (impl_->rrset_->getClass());
+}
+
+const RRType&
+RBNodeRRset::getType() const {
+    return (impl_->rrset_->getType());
+}
+
+const RRTTL&
+RBNodeRRset::getTTL() const {
+    return (impl_->rrset_->getTTL());
+}
+
+void
+RBNodeRRset::setName(const Name&) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
+}
+
+void
+RBNodeRRset::setTTL(const RRTTL&) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
+}
+
+string
+RBNodeRRset::toText() const {
+    return (impl_->rrset_->toText());
+}
+
+unsigned int
+RBNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
+    return (impl_->rrset_->toWire(renderer));
+}
+
+unsigned int
+RBNodeRRset::toWire(isc::util::OutputBuffer& buffer) const {
+    return (impl_->rrset_->toWire(buffer));
+}
+
+void
+RBNodeRRset::addRdata(ConstRdataPtr) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
+}
+
+void
+RBNodeRRset::addRdata(const Rdata&) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
+}
+
+RdataIteratorPtr
+RBNodeRRset::getRdataIterator() const {
+    return (impl_->rrset_->getRdataIterator());
+}
+
+RRsetPtr
+RBNodeRRset::getRRsig() const {
+    return (impl_->rrset_->getRRsig());
+}
+
+void
+RBNodeRRset::addRRsig(const ConstRdataPtr& rdata) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(rdata);
+}
+
+void
+RBNodeRRset::addRRsig(const RdataPtr& rdata) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(rdata);
+}
+
+void
+RBNodeRRset::addRRsig(const AbstractRRset& sigs) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::addRRsig(const ConstRRsetPtr& sigs) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::addRRsig(const RRsetPtr& sigs) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::removeRRsig() {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->removeRRsig();
+}
+
+ConstRRsetPtr
+RBNodeRRset::getUnderlyingRRset() const {
+    return (impl_->rrset_);
+}
+
+void
+RBNodeRRset::addAdditionalNode(const AdditionalNodeInfo& additional) {
+    // Lazy initialization
+    if (!impl_->additionals_) {
+        impl_->additionals_.reset(new vector<AdditionalNodeInfo>);
+    }
+    impl_->additionals_->push_back(additional);
+}
+
+const vector<AdditionalNodeInfo>*
+RBNodeRRset::getAdditionalNodes() const {
+    return (impl_->additionals_.get());
+}
+
+void
+RBNodeRRset::copyAdditionalNodes(RBNodeRRset& dst) const {
+    if (impl_->additionals_) {
+        dst.impl_->additionals_.reset(
+            new vector<AdditionalNodeInfo>(impl_->additionals_->begin(),
+                                           impl_->additionals_->end()));
+    }
+}
+
+} // end of internal
+
+namespace {
+// Specialized version of ZoneFinder::ResultContext, which specifically
+// holds rrset in the form of RBNodeRRset.
+struct RBNodeResultContext {
+    /// \brief Constructor
+    ///
+    /// The first three parameters correspond to those of
+    /// ZoneFinder::ResultContext.  If node is non NULL, it specifies the
+    /// found RBNode in the search.
+    RBNodeResultContext(ZoneFinder::Result code_param,
+                        ConstRBNodeRRsetPtr rrset_param,
+                        ZoneFinder::FindResultFlags flags_param,
+                        const DomainNode* node) :
+        code(code_param), rrset(rrset_param), flags(flags_param),
+        found_node(node)
+    {}
+
+    const ZoneFinder::Result code;
+    const ConstRBNodeRRsetPtr rrset;
+    const ZoneFinder::FindResultFlags flags;
+    const DomainNode* const found_node;
+};
+}
+
+class InMemoryZoneFinder::Context : public ZoneFinder::Context {
+public:
+    /// \brief Constructor.
+    ///
+    /// Note that we don't have a specific constructor for the findAll() case.
+    /// For (successful) type ANY query, found_node points to the
+    /// corresponding RB node, which is recorded within this specialized
+    /// context.
+    Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
+            const RBNodeResultContext& result) :
+        ZoneFinder::Context(finder, options,
+                            ResultContext(result.code, result.rrset,
+                                          result.flags)),
+        rrset_(result.rrset), found_node_(result.found_node)
+    {}
+
+protected:
+    virtual void getAdditionalImpl(const vector<RRType>& requested_types,
+                                   vector<ConstRRsetPtr>& result)
+    {
+        if (!rrset_) {
+            // In this case this context should encapsulate the result of
+            // findAll() and found_node_ should point to a valid answer node.
+            if (found_node_ == NULL || found_node_->isEmpty()) {
+                isc_throw(isc::Unexpected,
+                          "Invalid call to in-memory getAdditional: caller's "
+                          "bug or broken zone");
+            }
+            BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
+                getAdditionalForRRset(*dom_it.second, requested_types,
+                                      result);
+            }
+        } else {
+            getAdditionalForRRset(*rrset_, requested_types, result);
+        }
+    }
+
+private:
+    // Retrieve additional RRsets for a given RRset associated in the context.
+    // The process is straightforward: it examines the link to
+    // AdditionalNodeInfo vector (if set), and find RRsets of the requested
+    // type for each node.
+    static void getAdditionalForRRset(const RBNodeRRset& rrset,
+                                      const vector<RRType>& requested_types,
+                                      vector<ConstRRsetPtr>& result)
+    {
+        const vector<AdditionalNodeInfo>* additionals_ =
+            rrset.getAdditionalNodes();
+        if (additionals_ == NULL) {
+            return;
+        }
+        const bool glue_ok = (rrset.getType() == RRType::NS());
+        BOOST_FOREACH(const AdditionalNodeInfo& additional, *additionals_) {
+            assert(additional.node_ != NULL);
+            if (additional.node_->isEmpty()) {
+                continue;
+            }
+            if (!glue_ok && additional.node_->getFlag(domain_flag::GLUE)) {
+                continue;
+            }
+            BOOST_FOREACH(const RRType& rrtype, requested_types) {
+                Domain::const_iterator found =
+                    additional.node_->getData()->find(rrtype);
+                if (found != additional.node_->getData()->end()) {
+                    // TODO: wildcard consideration
+                    result.push_back(found->second);
+                }
+            }
+        }
+    }
+
+    const ConstRBNodeRRsetPtr rrset_;
+    const DomainNode* const found_node_;
+};
+
 // Private data and hidden methods of InMemoryZoneFinder
 struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
     // Constructor
@@ -114,8 +416,6 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         zone_data_(new ZoneData(origin_))
     {}
 
-    static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
-
     // Information about the zone
     RRClass zone_class_;
     Name origin_;
@@ -154,7 +454,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                                                          &node));
                 assert(result == DomainTree::SUCCESS ||
                        result == DomainTree::ALREADYEXISTS);
-                node->setFlag(DOMAINFLAG_WILD);
+                node->setFlag(domain_flag::WILD);
 
                 // Ensure a separate level exists for the wildcard name.
                 // Note: for 'name' itself we do this later anyway, but the
@@ -407,7 +707,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             return (result::EXIST);
         }
 
-        zone_data.nsec3_data_->map_.insert(NSEC3Pair(fst_label, rrset));
+        zone_data.nsec3_data_->map_.insert(
+            NSEC3Pair(fst_label, ConstRBNodeRRsetPtr(new RBNodeRRset(rrset))));
         return (result::SUCCESS);
     }
 
@@ -416,7 +717,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * access is without the impl_-> and it will get inlined anyway.
      */
     // Implementation of InMemoryZoneFinder::add
-    result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data) {
+    result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data,
+                       vector<RBNodeRRset*>* need_additionals)
+    {
         // Sanitize input.  This will cause an exception to be thrown
         // if the input RRset is empty.
         addValidation(rawrrset);
@@ -428,7 +731,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         // ... although instead of loading the RRset directly, we encapsulate
         // it within an RBNodeRRset.  This contains additional information that
         // speeds up queries.
-        ConstRRsetPtr rrset(new internal::RBNodeRRset(rawrrset));
+        RBNodeRRsetPtr rrset(new RBNodeRRset(rawrrset));
 
         if (rrset->getType() == RRType::NSEC3()) {
             return (addNSEC3(rrset, zone_data));
@@ -485,6 +788,12 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 node->setFlag(DomainNode::FLAG_CALLBACK);
             }
 
+            if (need_additionals != NULL &&
+                (rrset->getType() == RRType::NS() ||
+                 rrset->getType() == RRType::MX())) {
+                need_additionals->push_back(rrset.get());
+            }
+
             // If we've added NSEC3PARAM at zone origin, set up NSEC3 specific
             // data or check consistency with already set up parameters.
             if (rrset->getType() == RRType::NSEC3PARAM() &&
@@ -513,8 +822,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * Same as above, but it checks the return value and if it already exists,
      * it throws.
      */
-    void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data) {
-        switch (add(set, *zone_data)) {
+    void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data,
+                     vector<RBNodeRRset*>* need_additionals)
+    {
+        switch (add(set, *zone_data, need_additionals)) {
         case result::EXIST:
             LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
                 arg(set->getName()).arg(set->getType());
@@ -540,7 +851,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         {}
         const DomainNode* zonecut_node_;
         const DomainNode* dname_node_;
-        ConstRRsetPtr rrset_;
+        ConstRBNodeRRsetPtr rrset_;
         const FindOptions options_;
     };
 
@@ -613,18 +924,19 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * It is designed for wildcard case, where we create the rrsets
      * dynamically.
      */
-    static ConstRRsetPtr prepareRRset(const Name& name,
-                                      const ConstRRsetPtr& rrset,
-                                      bool rename, FindOptions options)
+    static ConstRBNodeRRsetPtr prepareRRset(const Name& name,
+                                            const ConstRBNodeRRsetPtr& rrset,
+                                            bool rename, FindOptions options)
     {
         if (rename) {
             LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
                 arg(rrset->getName()).arg(name);
-            RRsetPtr result(new RRset(name, rrset->getClass(),
-                                      rrset->getType(), rrset->getTTL()));
+            RRsetPtr result_base(new RRset(name, rrset->getClass(),
+                                           rrset->getType(),
+                                           rrset->getTTL()));
             for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
                  i->next()) {
-                result->addRdata(i->getCurrent());
+                result_base->addRdata(i->getCurrent());
             }
             if ((options & FIND_DNSSEC) != 0) {
                 ConstRRsetPtr sig_rrset = rrset->getRRsig();
@@ -638,9 +950,11 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                     {
                         result_sig->addRdata(i->getCurrent());
                     }
-                    result->addRRsig(result_sig);
+                    result_base->addRRsig(result_sig);
                 }
             }
+            RBNodeRRsetPtr result(new RBNodeRRset(result_base));
+            rrset->copyAdditionalNodes(*result);
             return (result);
         } else {
             return (rrset);
@@ -651,8 +965,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
     // account wildcard matches and DNSSEC information.  We set the NSEC/NSEC3
     // flag when applicable regardless of the find option; the caller would
     // simply ignore these when they didn't request DNSSEC related results.
-    ResultContext createFindResult(Result code, ConstRRsetPtr rrset,
-                                   bool wild = false) const
+    // When the optional parameter 'node' is given (in which case it should be
+    // non NULL), it means it's a result of ANY query and the context should
+    // remember the matched node.
+    RBNodeResultContext createFindResult(Result code,
+                                         ConstRBNodeRRsetPtr rrset,
+                                         bool wild = false,
+                                         const DomainNode* node = NULL) const
     {
         FindResultFlags flags = RESULT_DEFAULT;
         if (wild) {
@@ -662,13 +981,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             zone_data_->nsec3_data_) {
             flags = flags | RESULT_NSEC3_SIGNED;
         }
-        return (ZoneFinder::ResultContext(code, rrset, flags));
+        return (RBNodeResultContext(code, rrset, flags, node));
     }
 
     // Implementation of InMemoryZoneFinder::find
-    ZoneFinder::ResultContext find(const Name& name, RRType type,
-                                   std::vector<ConstRRsetPtr>* target,
-                                   const FindOptions options) const
+    RBNodeResultContext find(const Name& name, RRType type,
+                             std::vector<ConstRRsetPtr>* target,
+                             const FindOptions options) const
     {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
             arg(type);
@@ -722,7 +1041,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                     NameComparisonResult::SUPERDOMAIN) {
                     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
                         arg(name);
-                    return (createFindResult(NXRRSET, ConstRRsetPtr()));
+                    return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr()));
                 }
 
                 /*
@@ -733,7 +1052,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                  * not match according to 4.3.3 of RFC 1034 (the query name
                  * is known to exist).
                  */
-                if (node->getFlag(DOMAINFLAG_WILD)) {
+                if (node->getFlag(domain_flag::WILD)) {
                     /* Should we cancel this match?
                      *
                      * If we compare with some node and get a common ancestor,
@@ -761,7 +1080,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                         getLastComparisonResult().getCommonLabels() > 1) {
                         LOG_DEBUG(logger, DBG_TRACE_DATA,
                                      DATASRC_MEM_WILDCARD_CANCEL).arg(name);
-                        return (createFindResult(NXDOMAIN, ConstRRsetPtr(),
+                        return (createFindResult(NXDOMAIN,
+                                                 ConstRBNodeRRsetPtr(),
                                                  false));
                     }
                     const Name wildcard(Name("*").concatenate(
@@ -769,7 +1089,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                     DomainTree::Result result =
                         zone_data_->domains_.find(wildcard, &node);
                     /*
-                     * Otherwise, why would the DOMAINFLAG_WILD be there if
+                     * Otherwise, why would the domain_flag::WILD be there if
                      * there was no wildcard under it?
                      */
                     assert(result == DomainTree::EXACTMATCH);
@@ -787,7 +1107,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             case DomainTree::NOTFOUND:
                 LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
                     arg(name);
-                return (createFindResult(NXDOMAIN, ConstRRsetPtr(), false));
+                return (createFindResult(NXDOMAIN, ConstRBNodeRRsetPtr(),
+                                         false));
             case DomainTree::EXACTMATCH: // This one is OK, handle it
                 break;
             default:
@@ -800,7 +1121,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         if (node->isEmpty()) {
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
                 arg(name);
-            return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
+            return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
         }
 
         Domain::const_iterator found;
@@ -832,7 +1153,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             }
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
                 arg(name);
-            return (createFindResult(SUCCESS, ConstRRsetPtr(), rename));
+            return (createFindResult(SUCCESS, ConstRBNodeRRsetPtr(), rename,
+                                     node));
         }
 
         found = node->getData()->find(type);
@@ -858,7 +1180,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         // No exact match or CNAME.  Return NXRRSET.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
             arg(name);
-        return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
+        return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
     }
 };
 
@@ -890,8 +1212,8 @@ InMemoryZoneFinder::find(const Name& name, const RRType& type,
                          const FindOptions options)
 {
     return (ZoneFinderContextPtr(
-                new Context(*this, options,
-                            impl_->find(name, type, NULL, options))));
+                new Context(*this, options, impl_->find(name, type, NULL,
+                                                        options))));
 }
 
 ZoneFinderContextPtr
@@ -901,8 +1223,7 @@ InMemoryZoneFinder::findAll(const Name& name,
 {
     return (ZoneFinderContextPtr(
                 new Context(*this, options, impl_->find(name, RRType::ANY(),
-                                                        &target, options),
-                            target)));
+                                                        &target, options))));
 }
 
 ZoneFinder::FindNSEC3Result
@@ -934,7 +1255,7 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
     const unsigned int olabels = impl_->origin_.getLabelCount();
     const unsigned int qlabels = name.getLabelCount();
 
-    ConstRRsetPtr covering_proof; // placeholder of the next closer proof
+    ConstRBNodeRRsetPtr covering_proof; // placeholder of the next closer proof
     // Examine all names from the query name to the origin name, stripping
     // the deepest label one by one, until we find a name that has a matching
     // NSEC3 hash.
@@ -984,20 +1305,101 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
 
 result::Result
 InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
-    return (impl_->add(rrset, *impl_->zone_data_));
+    return (impl_->add(rrset, *impl_->zone_data_, NULL));
+}
+
+namespace {
+// This should eventually be more generalized.
+const Name
+getAdditionalName(RRType rrtype, const rdata::Rdata& rdata) {
+    if (rrtype == RRType::NS()) {
+        const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+        return (ns.getNSName());
+    } else if (rrtype == RRType::MX()) {
+        const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+        return (mx.getMXName());
+    }
+    // In our usage this shouldn't happen.
+    assert(false);
+}
+
+bool
+checkZoneCut(const DomainNode& node, pair<bool, bool>* arg) {
+    // We are only interested in the highest zone cut information.
+    // Ignore others and continue the search.
+    if (arg->first) {
+        return (false);
+    }
+    // Once we encounter a delegation point due to a DNAME, anything under it
+    // should be hidden.
+    if (node.getData()->find(RRType::DNAME()) != node.getData()->end()) {
+        return (true);
+    } else if (node.getData()->find(RRType::NS()) != node.getData()->end()) {
+        arg->first = true;
+        arg->second = true;
+        return (false);
+    }
+    return (false);
 }
 
+void
+addAdditional(RBNodeRRset* rrset, ZoneData* zone_data) {
+    RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
+    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+        // For each domain name that requires additional section processing
+        // in each RDATA, search the tree for the name and remember it if
+        // found.  If the name is under a zone cut (for a delegation to a
+        // child zone), mark the node as "GLUE", so we can selectively
+        // include/exclude them when we use it.
+
+        // TODO: wildcard
+        RBTreeNodeChain<Domain> node_path;
+        DomainNode* node = NULL;
+        // The callback argument is a pair of bools: the first is a flag to
+        // only check the highest cut; the second one records whether the
+        // search goes under a zone cut.
+        pair<bool, bool> callback_arg(false, false);
+        const DomainTree::Result result =
+            zone_data->domains_.find(
+                getAdditionalName(rrset->getType(),
+                                  rdata_iterator->getCurrent()),
+                &node, node_path, checkZoneCut, &callback_arg);
+        if (result == DomainTree::EXACTMATCH) {
+            assert(node != NULL);
+            if (callback_arg.second ||
+                (node->getFlag(DomainNode::FLAG_CALLBACK) &&
+                 node->getData()->find(RRType::NS()) !=
+                 node->getData()->end())) {
+                // The node is under or at a zone cut; mark it as a glue.
+                node->setFlag(domain_flag::GLUE);
+            }
+            // Note that node may be empty.  We should keep it in the list
+            // in case we dynamically update the tree and it becomes non empty
+            // (which is not supported yet)
+            rrset->addAdditionalNode(node);
+        }
+    }
+}
+}
 
 void
 InMemoryZoneFinder::load(const string& filename) {
     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
         arg(filename);
-    // Load it into temporary zone data
+    // Load it into temporary zone data.  As we build the zone, we record
+    // the (RBNode)RRsets that needs to be associated with additional
+    // information in 'need_additionals'.
+    vector<RBNodeRRset*> need_additionals;
     scoped_ptr<ZoneData> tmp(new ZoneData(getOrigin()));
 
     masterLoad(filename.c_str(), getOrigin(), getClass(),
                boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_,
-                           _1, tmp.get()));
+                           _1, tmp.get(), &need_additionals));
+
+    // For each RRset in need_additionals, identify the corresponding
+    // RBnode for additional processing and associate it in the RRset.
+    for_each(need_additionals.begin(), need_additionals.end(),
+             boost::bind(addAdditional, _1, tmp.get()));
 
     // If the zone is NSEC3-signed, check if it has NSEC3PARAM
     if (tmp->nsec3_data_) {

+ 6 - 0
src/lib/datasrc/memory_datasrc.h

@@ -218,6 +218,12 @@ private:
     // extracts the pointer to data and puts it into the iterator.
     // The access is read only.
     friend class InMemoryClient;
+
+    /// \brief In-memory version of finder context.
+    ///
+    /// The implementation (and any specialized interface) is completely local
+    /// to the InMemoryZoneFinder class, so it's defined as private
+    class Context;
 };
 
 /// \brief A data source client that holds all necessary data in memory.

+ 80 - 75
src/lib/datasrc/rbnode_rrset.h

@@ -24,11 +24,27 @@
 #include <util/buffer.h>
 
 #include <string>
+#include <vector>
 
 namespace isc {
 namespace datasrc {
 namespace internal {
 
+/// \brief The actual content of \c RBNodeRRset
+///
+///  This is defined in the namespace-scope (not hidden in the main class)
+/// so that the In-memory data source implementation can refer to it.
+struct RBNodeRRsetImpl;
+
+// Forward declaration of an opaque data type defined and used within the
+// implementation.  This is public only because it needs to be used within
+// the in-memory data source implementation, but conceptually this is a
+// private type for the in-memory data source implementation.
+// Note that the definition of the structure is still hidden within the
+// implementation, so, basically, a normal application should never be able
+// to use it directly even if it peeks into the "internal" namespace.
+struct AdditionalNodeInfo;
+
 /// \brief Special RRset for optimizing memory datasource requirement
 ///
 /// To speed up the performance of the in-memory data source, at load time
@@ -84,11 +100,10 @@ public:
     /// Creates an RBNodeRRset from the pointer to the RRset passed to it.
     ///
     /// \param rrset Pointer to underlying RRset encapsulated by this object.
-    explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset) : rrset_(rrset)
-    {}
+    explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset);
 
     /// \brief Destructor
-    virtual ~RBNodeRRset() {}
+    virtual ~RBNodeRRset();
 
     // Getter and Setter Methods
     //
@@ -96,64 +111,34 @@ public:
     // setter methods thrown an exception - this specialisation of the RRset
     // object does not expect the underlying RRset to be modified.
 
-    virtual unsigned int getRdataCount() const {
-        return (rrset_->getRdataCount());
-    }
+    virtual unsigned int getRdataCount() const;
 
-    virtual const isc::dns::Name& getName() const {
-        return (rrset_->getName());
-    }
+    virtual const isc::dns::Name& getName() const;
 
-    virtual const isc::dns::RRClass& getClass() const {
-        return (rrset_->getClass());
-    }
+    virtual const isc::dns::RRClass& getClass() const;
 
-    virtual const isc::dns::RRType& getType() const {
-        return (rrset_->getType());
-    }
+    virtual const isc::dns::RRType& getType() const;
 
-    virtual const isc::dns::RRTTL& getTTL() const {
-        return (rrset_->getTTL());
-    }
+    virtual const isc::dns::RRTTL& getTTL() const;
 
-    virtual void setName(const isc::dns::Name&) {
-        isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
-    }
+    virtual void setName(const isc::dns::Name&);
 
-    virtual void setTTL(const isc::dns::RRTTL&) {
-        isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
-    }
+    virtual void setTTL(const isc::dns::RRTTL&);
 
-    virtual std::string toText() const {
-        return (rrset_->toText());
-    }
+    virtual std::string toText() const;
 
     virtual unsigned int toWire(
-            isc::dns::AbstractMessageRenderer& renderer) const {
-        return (rrset_->toWire(renderer));
-    }
+        isc::dns::AbstractMessageRenderer& renderer) const;
 
-    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const {
-        return (rrset_->toWire(buffer));
-    }
+    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
 
-    virtual void addRdata(isc::dns::rdata::ConstRdataPtr) {
-        isc_throw(isc::NotImplemented,
-                  "RBNodeRRset::addRdata() not supported");
-    }
+    virtual void addRdata(isc::dns::rdata::ConstRdataPtr);
 
-    virtual void addRdata(const isc::dns::rdata::Rdata&) {
-        isc_throw(isc::NotImplemented,
-                  "RBNodeRRset::addRdata() not supported");
-    }
+    virtual void addRdata(const isc::dns::rdata::Rdata&);
 
-    virtual isc::dns::RdataIteratorPtr getRdataIterator() const {
-        return (rrset_->getRdataIterator());
-    }
+    virtual isc::dns::RdataIteratorPtr getRdataIterator() const;
 
-    virtual isc::dns::RRsetPtr getRRsig() const {
-        return (rrset_->getRRsig());
-    }
+    virtual isc::dns::RRsetPtr getRRsig() const;
 
     // With all the RRsig methods, we have the problem that we store the
     // underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
@@ -161,45 +146,65 @@ public:
     // this by temporarily violating the "const" nature of the RRset to add the
     // data.
 
-    virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata) {
-        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
-        p->addRRsig(rdata);
-    }
+    virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata);
+
+    virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata);
+
+    virtual void addRRsig(const AbstractRRset& sigs);
 
-    virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata) {
-        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
-        p->addRRsig(rdata);
-    }
+    virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs);
 
-    virtual void addRRsig(const AbstractRRset& sigs) {
-        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
-        p->addRRsig(sigs);
-    }
+    virtual void addRRsig(const isc::dns::RRsetPtr& sigs);
 
-    virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs) {
-        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
-        p->addRRsig(sigs);
-    }
+    virtual void removeRRsig();
 
-    virtual void addRRsig(const isc::dns::RRsetPtr& sigs) {
-        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
-        p->addRRsig(sigs);
-    }
+    /// \brief Associate a link to an RB node of the additional record.
+    ///
+    /// This method adds a given opaque object that holds a link to an RB node
+    /// of the underlying in-memory data source that is corresponding to an
+    /// RDATA of this RRset.
+    ///
+    /// This method is exposed as public so it can be used within the in-memory
+    /// data source implementation, and only for that purpose.
+    ///
+    /// \param additional An opaque \c AdditionalNodeInfo object to be
+    /// associated with this RRset.
+    void addAdditionalNode(const AdditionalNodeInfo& additional);
 
-    virtual void removeRRsig() {
-        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
-        p->removeRRsig();
-    }
+    /// \brief Return a pointer to the list (vector) of additional RB nodes.
+    ///
+    /// This method returns a pointer to a vector storing the opaque
+    /// \c AdditionalNodeInfo object that may be possibly set in this RRset.
+    /// Not all RRsets are associated with additional nodes; if no
+    /// such node is stored, this method returns NULL.
+    ///
+    /// Like \c addAdditionalNode(), this method is exposed as public only for
+    /// the in-memory data source implementation.
+    ///
+    /// \return A pointer to the associated vector of \c AdditionalNodeInfo;
+    /// NULL if no additional nodes are associated to this RRset.
+    const std::vector<AdditionalNodeInfo>* getAdditionalNodes() const;
+
+    /// \brief Copy the list of additional RB nodes to another RRset.
+    ///
+    /// This method copies the internal list (an STL vector in the actual
+    /// implementation) of additional RB nodes for this RRset to another
+    /// \c RBNodeRRset object.  The copy destination is generally expected to
+    /// be newly created and have an empty list, but this method does not
+    /// check the condition.  If the destination already has a non empty list,
+    /// the existing entries will be lost.
+    ///
+    /// \param dst The \c RBNodeRRset object to which the additional
+    /// RB node list is to be copied.
+    void copyAdditionalNodes(RBNodeRRset& dst) const;
 
     /// \brief Return underlying RRset pointer
     ///
     /// ... mainly for testing.
-    isc::dns::ConstRRsetPtr getUnderlyingRRset() const {
-        return (rrset_);
-    }
+    isc::dns::ConstRRsetPtr getUnderlyingRRset() const;
 
 private:
-    isc::dns::ConstRRsetPtr rrset_;     ///< Underlying RRset
+    RBNodeRRsetImpl* impl_;
 };
 
 }   // namespace internal

+ 4 - 2
src/lib/datasrc/rbtree.h

@@ -123,7 +123,8 @@ public:
     /// set to on by the \c setFlag() method.
     enum Flags {
         FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
-        FLAG_USER1 = 0x80000000U ///< Application specific flag
+        FLAG_USER1 = 0x80000000U, ///< Application specific flag
+        FLAG_USER2 = 0x40000000U  ///< Application specific flag
     };
 private:
     // Some flag values are expected to be used for internal purposes
@@ -131,7 +132,8 @@ private:
     // 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);
+    static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1 |
+                                            FLAG_USER2);
 
 public:
 

+ 41 - 1
src/lib/datasrc/tests/testdata/contexttest.zone

@@ -1,7 +1,7 @@
 ;; test zone file used for ZoneFinderContext tests.
 ;; RRSIGs are (obviouslly) faked ones for testing.
 
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 22 3600 300 3600000 3600
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 56 3600 300 3600000 3600
 example.org.			      3600 IN NS	ns1.example.org.
 example.org.			      3600 IN NS	ns2.example.org.
 example.org.			      3600 IN MX	1 mx1.example.org.
@@ -28,8 +28,48 @@ ns2.a.example.org.		      3600 IN A		192.0.2.6
 ns2.a.example.org.		      3600 IN AAAA	2001:db8::6
 mx.a.example.org.		      3600 IN A		192.0.2.7
 
+;; delegation, one of its NS names is at zone cut.
+b.example.org.			      3600 IN NS	ns.b.example.org.
+b.example.org.			      3600 IN NS	b.example.org.
+b.example.org.			      3600 IN AAAA	2001:db8::8
+
+ns.b.example.org.		      3600 IN A		192.0.2.9
+
+;; The MX name is at a zone cut.  shouldn't be included in the
+;; additional section.
+mxatcut.example.org.		      3600 IN MX	1 b.example.org.
+
+;; delegation, one of its NS names is under a DNAME delegation point;
+;; another is at that point; and yet another is under DNAME below a
+;; zone cut.
+c.example.org. 	      	      3600 IN NS	ns.dname.example.org.
+c.example.org. 	      	      3600 IN NS	dname.example.org.
+c.example.org.      	      3600 IN NS	ns.deepdname.example.org.
+ns.dname.example.org.		      3600 IN A		192.0.2.11
+dname.example.org.		      3600 IN A		192.0.2.12
+ns.deepdname.example.org.	      3600 IN AAAA	2001:db8::9
+
+;; delegation, one of its NS name is at an empty non terminal.
+d.example.org. 	      	      3600 IN NS	ns.empty.example.org.
+d.example.org. 	      	      3600 IN NS	ns1.example.org.
+;; by adding these two we can create an empty RB node for
+;; ns.empty.example.org in the in-memory zone
+foo.ns.empty.example.org.     3600 IN A		192.0.2.13
+bar.ns.empty.example.org.     3600 IN A		192.0.2.14
+
+;; delegation; the NS name matches a wildcard (and there's no exact match)
+e.example.org. 	      	      3600 IN NS	ns.wild.example.org.
+*.wild.example.org.	      3600 IN A		192.0.2.15
+
+;; additional for an answer RRset (MX) as a result of wildcard
+;; expansion
+*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
+
 ;; CNAME
 alias.example.org. 3600 IN CNAME cname.example.org.
 
 ;; DNAME
 dname.example.org. 3600 IN DNAME dname.example.com.
+
+;; DNAME under a NS (strange one)
+deepdname.c.example.org. 3600 IN DNAME deepdname.example.com.

+ 90 - 0
src/lib/datasrc/tests/zone_finder_context_unittest.cc

@@ -214,6 +214,81 @@ TEST_P(ZoneFinderContextTest, getAdditionalDelegation) {
                 result_sets_.begin(), result_sets_.end());
 }
 
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationAtZoneCut) {
+    // Similar to the previous case, but one of the NS addresses is at the
+    // zone cut.
+
+    // XXX: the current database-based data source incorrectly rejects this
+    // setup (see #1771)
+    if (GetParam() == createSQLite3Client) {
+        return;
+    }
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.b.example.org"),
+                                             RRType::SOA());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("b.example.org. 3600 IN AAAA 2001:db8::8\n"
+                "ns.b.example.org. 3600 IN A 192.0.2.9\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithDname) {
+    // Delegation: One of the NS names under a DNAME delegation; another
+    // is at the delegation point; yet another is under DNAME below a zone cut.
+    // The first should be hidden.
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.c.example.org"),
+                                             RRType::TXT());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("dname.example.org. 3600 IN A 192.0.2.12\n"
+                "ns.deepdname.example.org. 3600 IN AAAA 2001:db8::9\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithEmptyName) {
+    // One of NS names is at an empty non terminal node.  It shouldn't cause
+    // any disruption.
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.d.example.org"),
+                                             RRType::A());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithWild) {
+    // The NS name needs to be expanded by a wildcard.  Currently it doesn't
+    // work for the optimized in-memory version.
+    if (GetParam() == createInMemoryClient) {
+        return;
+    }
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.e.example.org"),
+                                             RRType::AAAA());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns.wild.example.org. 3600 IN A 192.0.2.15\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationForWild) {
+    // additional for an answer RRset (MX) as a result of wildcard expansion.
+    // note the difference from the previous test.  in this case wildcard
+    // applies to the owner name of the answer, not the owner name of the
+    // additional.
+    ZoneFinderContextPtr ctx = finder_->find(Name("mx.wildmx.example.org"),
+                                             RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
 TEST_P(ZoneFinderContextTest, getAdditionalMX) {
     // Similar to the previous cases, but for MX addresses.  The test zone
     // contains MX name under a zone cut.  Its address shouldn't be returned.
@@ -239,6 +314,21 @@ TEST_P(ZoneFinderContextTest, getAdditionalMX) {
                 result_sets_.begin(), result_sets_.end());
 }
 
+TEST_P(ZoneFinderContextTest, getAdditionalMXAtZoneCut) {
+    // XXX: the current database-based data source incorrectly rejects this
+    // setup (see #1771)
+    if (GetParam() == createSQLite3Client) {
+        return;
+    }
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("mxatcut.example.org."),
+                                             RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+}
+
 TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
     // Similar to the AuthNS test, but the original find() requested DNSSEC
     // RRSIGs.  Then additional records will also have RRSIGs.

+ 7 - 0
src/lib/datasrc/zone.h

@@ -252,6 +252,13 @@ public:
         /// call resulted in SUCCESS or DELEGATION.  Otherwise this method
         /// does nothing.
         ///
+        /// \note The additional RRsets returned via method are limited to
+        /// ones contained in the zone which the corresponding find/findAll
+        /// call searched (possibly including glues under a zone cut where
+        /// they are applicable).  If the caller needs to get out-of-zone
+        /// additional RRsets, it needs to explicitly finds them by
+        /// identifying the corresponding zone and calls \c find() for it.
+        ///
         /// \param requested_types A vector of RR types for desired additional
         ///  RRsets.
         /// \param result A vector to which any found additional RRsets are