Browse Source

[1198] Extract out the wildcard matching into a separate method

Stephen Morris 13 years ago
parent
commit
63f318aa44
2 changed files with 232 additions and 118 deletions
  1. 141 111
      src/lib/datasrc/database.cc
  2. 91 7
      src/lib/datasrc/database.h

+ 141 - 111
src/lib/datasrc/database.cc

@@ -469,34 +469,141 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
                                    last_known));
 }
 
+DatabaseClient::Finder::WildcardSearchResult
+DatabaseClient::Finder::findWildcardMatch(const isc::dns::Name& name,
+                                          const isc::dns::RRType& type,
+                                          const FindOptions options,
+                                          isc::dns::RRsetPtr& first_ns,
+                                          size_t last_known) {
+    // Result of search
+    isc::dns::RRsetPtr result_rrset;
+    ZoneFinder::Result result_status = SUCCESS;
+
+    // Search options
+    const bool dnssec_data((options & FIND_DNSSEC) != 0);
+
+    // Other
+    bool records_found = false;     // Distinguish between NXDOMAIN and NXRRSET
+    WantedTypes final_types(FINAL_TYPES());
+    final_types.insert(type);
+
+    // We know that the name is a non-empty terminal, so check for wildcards.
+    // We can start at the last known non-empty domain and work up.  We remove
+    // labels one by one and look for the wildcard there, up to the
+    // first non-empty domain.
+    for (size_t i = 1; i <= name.getLabelCount() - last_known; ++i) {
+
+        // Construct the name with *
+        const Name superdomain(name.split(i));
+        const string wildcard("*." + superdomain.toText());
+        const string construct_name(name.toText());
+
+        // TODO What do we do about DNAME here?
+        // The types are the same as with original query
+        FoundRRsets found = getRRsets(wildcard, final_types, true,
+                                      &construct_name);
+        if (found.first) {
+            if (first_ns) {
+                // In case we are under NS, we don't wildcard-match, but return
+                // delegation
+                result_rrset = first_ns;
+                result_status = DELEGATION;
+                records_found = true;
+
+                LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                          DATASRC_DATABASE_WILDCARD_CANCEL_NS).
+                    arg(accessor_->getDBName()).arg(wildcard).
+                    arg(first_ns->getName());
+
+            } else if (!hasSubdomains(name.split(i - 1).toText())) {
+
+                // Nothing we added as part of the * can exist directly, as we
+                // go up only to first existing domain, but it could be an empty
+                // non-terminal. In that case, we need to cancel the match.
+
+                records_found = true;
+                const FoundIterator cni(found.second.find(RRType::CNAME()));
+                const FoundIterator nsi(found.second.find(RRType::NS()));
+                const FoundIterator nci(found.second.find(RRType::NSEC()));
+                const FoundIterator wti(found.second.find(type));
+                if (cni != found.second.end() && type != RRType::CNAME()) {
+                    result_rrset = cni->second;
+                    result_status = WILDCARD_CNAME;
+
+                } else if (nsi != found.second.end()) {
+                    result_rrset = nsi->second;
+                    result_status = DELEGATION;
+
+                } else if (wti != found.second.end()) {
+                    result_rrset = wti->second;
+                    result_status = WILDCARD;
+
+                } else {
+                    // NXRRSET case in the wildcard
+                    result_status = WILDCARD_NXRRSET;
+                    if (dnssec_data &&
+                        nci != found.second.end()) {
+                        // User wants a proof the wildcard doesn't contain it
+                        //
+                        // However, we need to get the RRset in the name of the
+                        // wildcard, not the constructed one, so we walk it
+                        // again
+                        found = getRRsets(wildcard, NSEC_TYPES(), true);
+                        result_rrset =
+                            found.second.find(RRType::NSEC())->second;
+                    }
+                }
+
+                LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                          DATASRC_DATABASE_WILDCARD).
+                    arg(accessor_->getDBName()).arg(wildcard).
+                    arg(name);
+            } else {
+                LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                          DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
+                    arg(accessor_->getDBName()).arg(wildcard).
+                    arg(name).arg(superdomain);
+            }
+            break;
+        } else if (hasSubdomains(wildcard)) {
+            // Empty non-terminal asterisk
+            records_found = true;
+            LOG_DEBUG(logger, DBG_TRACE_DETAILED,
+                      DATASRC_DATABASE_WILDCARD_EMPTY).
+                arg(accessor_->getDBName()).arg(wildcard).
+                arg(name);
+            if (dnssec_data) {
+                result_rrset = findNSECCover(Name(wildcard));
+                if (result_rrset) {
+                    result_status = WILDCARD_NXRRSET;
+                }
+            }
+            break;
+        }
+    }
+    return (WildcardSearchResult(result_status, result_rrset, records_found));
+}
+
 ZoneFinder::FindResult
 DatabaseClient::Finder::find(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
                              isc::dns::RRsetList*,
                              const FindOptions options)
 {
-    // This variable is used to determine the difference between
-    // NXDOMAIN and NXRRSET
-    bool records_found = false;
+    LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
+              .arg(accessor_->getDBName()).arg(name).arg(type);
+
     bool glue_ok((options & FIND_GLUE_OK) != 0);
     const bool dnssec_data((options & FIND_DNSSEC) != 0);
-    bool get_cover(false);
+
+    bool records_found = false; // Distinguish between NXDOMAIN and NXRRSET
+    bool get_cover = false;
     isc::dns::RRsetPtr result_rrset;
     ZoneFinder::Result result_status = SUCCESS;
-    FoundRRsets found;
-    logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
-        .arg(accessor_->getDBName()).arg(name).arg(type);
-
-    // First, do we have any kind of delegation (NS/DNAME) here?
     const Name origin(getOrigin());
-    // Number of labels in the last known non-empty domain
-    const size_t current_label_count(name.getLabelCount());
-    // This is how many labels we remove to get origin
-    //const size_t remove_labels(current_label_count - origin_label_count);
 
     // First stage: go throught all superdomains from the origin down,
     // searching for nodes that indicate a delegation (NS or DNAME).
-
     DelegationSearchResult dresult = findDelegationPoint(name, options);
     result_status = dresult.code;
     result_rrset = dresult.rrset;
@@ -507,19 +614,21 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     size_t last_known = dresult.last_known;
 
     if (!result_rrset) { // Only if we didn't find a redirect already
+
         // Try getting the final result and extract it
         // It is special if there's a CNAME or NS, DNAME is ignored here
         // And we don't consider the NS in origin
-
         WantedTypes final_types(FINAL_TYPES());
         final_types.insert(type);
-        found = getRRsets(name.toText(), final_types, name != origin);
+        FoundRRsets found = getRRsets(name.toText(), final_types,
+                                      name != origin);
         records_found = found.first;
 
         // NS records, CNAME record and Wanted Type records
         const FoundIterator nsi(found.second.find(RRType::NS()));
         const FoundIterator cni(found.second.find(RRType::CNAME()));
         const FoundIterator wti(found.second.find(type));
+
         if (name != origin && !glue_ok && nsi != found.second.end()) {
             // There's a delegation at the exact node.
             LOG_DEBUG(logger, DBG_TRACE_DETAILED,
@@ -527,6 +636,7 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                 arg(accessor_->getDBName()).arg(name);
             result_status = DELEGATION;
             result_rrset = nsi->second;
+
         } else if (type != isc::dns::RRType::CNAME() &&
                    cni != found.second.end()) {
             // A CNAME here
@@ -537,19 +647,22 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                           result_rrset->getRdataCount() <<
                           " rdata at " << name << ", expected 1");
             }
+
         } else if (wti != found.second.end()) {
             // Just get the answer
             result_rrset = wti->second;
+
         } else if (!records_found) {
             // Nothing lives here.
-            // But check if something lives below this
-            // domain and if so, pretend something is here as well.
+            // But check if something lives below this domain and if so,
+            // pretend something is here as well.
             if (hasSubdomains(name.toText())) {
                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                           DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
                     arg(accessor_->getDBName()).arg(name);
                 records_found = true;
                 get_cover = dnssec_data;
+
             } else if ((options & NO_WILDCARD) != 0) {
                 // If wildcard check is disabled, the search will ultimately
                 // terminate with NXDOMAIN. If DNSSEC is enabled, flag that
@@ -557,102 +670,19 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
                 if (dnssec_data) {
                     get_cover = true;
                 }
+
             } else {
-                // It's not empty non-terminal. So check for wildcards.
+                // It's not an empty non-terminal so check for wildcards.
                 // We remove labels one by one and look for the wildcard there.
                 // Go up to first non-empty domain.
-                for (size_t i(1); i <= current_label_count - last_known; ++i) {
-                    // Construct the name with *
-                    const Name superdomain(name.split(i));
-                    const string wildcard("*." + superdomain.toText());
-                    const string construct_name(name.toText());
-                    // TODO What do we do about DNAME here?
-                    // The types are the same as with original query
-                    found = getRRsets(wildcard, final_types, true,
-                                      &construct_name);
-                    if (found.first) {
-                        if (first_ns) {
-                            // In case we are under NS, we don't
-                            // wildcard-match, but return delegation
-                            result_rrset = first_ns;
-                            result_status = DELEGATION;
-                            records_found = true;
-                            // We pretend to switch to non-glue_ok mode
-                            glue_ok = false;
-                            LOG_DEBUG(logger, DBG_TRACE_DETAILED,
-                                      DATASRC_DATABASE_WILDCARD_CANCEL_NS).
-                                arg(accessor_->getDBName()).arg(wildcard).
-                                arg(first_ns->getName());
-                        } else if (!hasSubdomains(name.split(i - 1).toText()))
-                        {
-                            // Nothing we added as part of the * can exist
-                            // directly, as we go up only to first existing
-                            // domain, but it could be empty non-terminal. In
-                            // that case, we need to cancel the match.
-                            records_found = true;
-                            const FoundIterator
-                                cni(found.second.find(RRType::CNAME()));
-                            const FoundIterator
-                                nsi(found.second.find(RRType::NS()));
-                            const FoundIterator
-                                nci(found.second.find(RRType::NSEC()));
-                            const FoundIterator wti(found.second.find(type));
-                            if (cni != found.second.end() &&
-                                type != RRType::CNAME()) {
-                                result_rrset = cni->second;
-                                result_status = WILDCARD_CNAME;
-                            } else if (nsi != found.second.end()) {
-                                result_rrset = nsi->second;
-                                result_status = DELEGATION;
-                            } else if (wti != found.second.end()) {
-                                result_rrset = wti->second;
-                                result_status = WILDCARD;
-                            } else {
-                                // NXRRSET case in the wildcard
-                                result_status = WILDCARD_NXRRSET;
-                                if (dnssec_data &&
-                                    nci != found.second.end()) {
-                                    // User wants a proof the wildcard doesn't
-                                    // contain it
-                                    //
-                                    // However, we need to get the RRset in the
-                                    // name of the wildcard, not the constructed
-                                    // one, so we walk it again
-                                    found = getRRsets(wildcard, NSEC_TYPES(),
-                                                      true);
-                                    result_rrset =
-                                        found.second.find(RRType::NSEC())->
-                                        second;
-                                }
-                            }
-
-                            LOG_DEBUG(logger, DBG_TRACE_DETAILED,
-                                      DATASRC_DATABASE_WILDCARD).
-                                arg(accessor_->getDBName()).arg(wildcard).
-                                arg(name);
-                        } else {
-                            LOG_DEBUG(logger, DBG_TRACE_DETAILED,
-                                      DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
-                                arg(accessor_->getDBName()).arg(wildcard).
-                                arg(name).arg(superdomain);
-                        }
-                        break;
-                    } else if (hasSubdomains(wildcard)) {
-                        // Empty non-terminal asterisk
-                        records_found = true;
-                        LOG_DEBUG(logger, DBG_TRACE_DETAILED,
-                                  DATASRC_DATABASE_WILDCARD_EMPTY).
-                            arg(accessor_->getDBName()).arg(wildcard).
-                            arg(name);
-                        if (dnssec_data) {
-                            result_rrset = findNSECCover(Name(wildcard));
-                            if (result_rrset) {
-                                result_status = WILDCARD_NXRRSET;
-                            }
-                        }
-                        break;
-                    }
-                }
+                WildcardSearchResult wresult = findWildcardMatch(name, type,
+                                                                 options,
+                                                                 first_ns,
+                                                                 last_known);
+                result_status = wresult.code;
+                result_rrset = wresult.rrset;
+                records_found = wresult.records_found;
+
                 // This is the NXDOMAIN case (nothing found anywhere). If
                 // they want DNSSEC data, try getting the NSEC record
                 if (dnssec_data && !records_found) {

+ 91 - 7
src/lib/datasrc/database.h

@@ -21,8 +21,8 @@
 #include <boost/tuple/tuple.hpp>
 
 #include <dns/rrclass.h>
-#include <dns/rrclass.h>
 #include <dns/rrset.h>
+#include <dns/rrtype.h>
 
 #include <datasrc/client.h>
 
@@ -815,7 +815,7 @@ public:
         const DatabaseAccessor& getAccessor() const {
             return (*accessor_);
         }
-        
+
         /// \brief Search result of \c findDelegationPoint().
         ///
         /// This is a tuple combining the result of the search - a status code
@@ -849,6 +849,32 @@ public:
             const size_t last_known; ///< No. labels in last non-empty domain
         };
 
+        /// \brief Search result of \c findWildcard().
+        ///
+        /// This is a tuple combining the result of the search - a status code
+        /// and a pointer to the RRset found - together with additional
+        /// information needed for subsequent processing: if there is not a
+        /// match for the data sought, then whether there is no match for the
+        /// wildcard or where there is a match, but no RRs of the type
+        /// requested.
+        ///
+        /// Note that the code and rrset elements are the same as that in
+        /// the \c ZoneFinder::FindResult struct: this structure could be
+        /// derived from that one, but as it is used just once in the code and
+        /// will never be treated as a \c FindResult, the obscurity involved in
+        /// deriving it from a parent class was deemed not worthwhile.
+        struct WildcardSearchResult {
+            WildcardSearchResult(const ZoneFinder::Result param_code,
+                                 const isc::dns::RRsetPtr param_rrset,
+                                 const bool param_found) :
+                                 code(param_code), rrset(param_rrset),
+                                 records_found(param_found)
+            {}
+            const ZoneFinder::Result code;      ///< Result code
+            const isc::dns::RRsetPtr rrset;     ///< Pointer to RRset found
+            const bool records_found;           ///< true => NXRRSET
+        };
+
     private:
         boost::shared_ptr<DatabaseAccessor> accessor_;
         const int zone_id_;
@@ -898,10 +924,23 @@ public:
          * down, searching for a point that indicates a delegation (i.e. an
          * NS record or a DNAME).
          *
+         * The method operates in two modes, non-glue-ok and glue-ok modes:
+         *
+         * In non-glue-ok mode, the search is made purely for the NS or DNAME
+         * RR.  The zone is searched from the origin down looking  for one
+         * of these RRTypes (and ignoring the NS records at the zone origin).
+         * A status is returned indicating what is found: DNAME, DELEGATION
+         * of SUCCESS, the last indicating that nothing was found, together
+         * with a pointer to the relevant RR.
+         *
+         * In glue-ok mode, the first NS encountered in the search (apart from
+         * the NS at the zone apex) is remembered but otherwise NS records are
+         * ignored and the search attempts to find a DNAME.  The result is
+         * returned in the same format, along with a pointer to the first non-
+         * apex NS (if found).
+         *
          * \param name The name to find
-         * \param type The RRType to find
-         * \param target Unused at this moment
-         * \param options Options about how to search. See
+         * \param options Options about how to search. See the documentation for
          *        ZoneFinder::FindOptions.
          *
          * \return Tuple holding the result of the search - the RRset of the
@@ -912,11 +951,56 @@ public:
          *         non-empty domain.  The associated information is found as
          *         a natural part of the search for the delegation point and
          *         is used later in the find() processing; it is passed back
-         *         to avoid the need to perform a second search toi obtain it.
+         *         to avoid the need to perform a second search to obtain it.
          */
         DelegationSearchResult
         findDelegationPoint(const isc::dns::Name& name,
-                            const FindOptions options = FIND_DEFAULT);
+                            const FindOptions options);
+
+        /**
+         * \brief Find wildcard match
+         *
+         * Having found that the name is not an empty non-terminal, this
+         * searches the zone for for wildcards that match the name.
+         *
+         * It searches superdomains of the name from the zone origin down
+         * looking for a wildcard in the zone that matches the name.  There
+         * are several cases to consider:
+         *
+         * - If the previous search for a delegation point has found that
+         *   there is an NS at the superdomain of the point at which the
+         *   wildcard is found, the delegation is returned.
+         * - If there is a match to the name, an appropriate status is
+         *   returned (match on requested type, delegation, cname, or just
+         *   the indication of a match but no RRs relevant to the query).
+         * - If the match is to an non-empty non-terminal wildcard, a
+         *   wildcard NXRRSET is returned.
+         *
+         * Note that if DNSSEC is enabled for the search and the zone uses
+         * NSEC for authenticated denial of existence, the search may
+         * return NSEC records.
+         *
+         * \param name The name to find
+         * \param type The RRType to find
+         * \param options Options about how to search. See the documentation
+         *        for ZoneFinder::FindOptions.
+         * \param first_ns A pointer to the first NS found in a search for
+         *        the name (will only be non-null in glue-ok mode).
+         * \param last_known the number of labels in the last known non-empty
+         *        domain in the name.
+         *
+         * \return Tuple holding the result of the search - the RRset of the
+         *         wildcard records matching the name, together with a status
+         *         indicating the match type (e.g. CNAME at the wildcard
+         *         match, no RRs of the requested type at the wildcard,
+         *         success due to an exact match).  Also returned if there
+         *         is no match is an indication as to whether there was an
+         *         NXDOMAIN or an NXRRSET.
+         */
+        WildcardSearchResult
+        findWildcardMatch(const isc::dns::Name& name,
+                     const isc::dns::RRType& type, const FindOptions options,
+                     isc::dns::RRsetPtr& first_ns, size_t last_known);
 
         /**
          * \brief Checks if something lives below this domain.