Michal 'vorner' Vaner 13 years ago
parent
commit
a47f01b6c8

+ 144 - 140
src/bin/auth/query.cc

@@ -15,6 +15,8 @@
 #include <algorithm>            // for std::max
 #include <vector>
 #include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
 
 #include <dns/message.h>
 #include <dns/rcode.h>
@@ -67,7 +69,7 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 
     // Find A rrset
     if (qname_ != qname || qtype_ != RRType::A()) {
-        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL,
+        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
                                                     options | dnssec_opt_);
         if (a_result.code == ZoneFinder::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
@@ -78,7 +80,7 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
     // Find AAAA rrset
     if (qname_ != qname || qtype_ != RRType::AAAA()) {
         ZoneFinder::FindResult aaaa_result =
-            zone.find(qname, RRType::AAAA(), NULL, options | dnssec_opt_);
+            zone.find(qname, RRType::AAAA(), options | dnssec_opt_);
         if (aaaa_result.code == ZoneFinder::SUCCESS) {
             response_.addRRset(Message::SECTION_ADDITIONAL,
                     boost::const_pointer_cast<RRset>(aaaa_result.rrset),
@@ -90,7 +92,7 @@ Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
 void
 Query::addSOA(ZoneFinder& finder) {
     ZoneFinder::FindResult soa_result(finder.find(finder.getOrigin(),
-        RRType::SOA(), NULL, dnssec_opt_));
+        RRType::SOA(), dnssec_opt_));
     if (soa_result.code != ZoneFinder::SUCCESS) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
@@ -146,7 +148,7 @@ Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // otherwise we shouldn't have got NXDOMAIN for the original query in
     // the first place).
     const ZoneFinder::FindResult fresult = finder.find(wildname,
-                                                       RRType::NSEC(), NULL,
+                                                       RRType::NSEC(),
                                                        dnssec_opt_);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
@@ -171,7 +173,7 @@ Query::addWildcardProof(ZoneFinder& finder) {
     // substitution.  Confirm that by specifying NO_WILDCARD.  It should result
     // in NXDOMAIN and an NSEC RR that proves it should be returned.
     const ZoneFinder::FindResult fresult =
-        finder.find(qname_, RRType::NSEC(), NULL,
+        finder.find(qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
@@ -194,7 +196,7 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
                       boost::const_pointer_cast<RRset>(nsec), dnssec_);
     
     const ZoneFinder::FindResult fresult =
-        finder.find(qname_, RRType::NSEC(), NULL,
+        finder.find(qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
         fresult.rrset->getRdataCount() == 0) {
@@ -213,8 +215,7 @@ void
 Query::addAuthAdditional(ZoneFinder& finder) {
     // Fill in authority and addtional sections.
     ZoneFinder::FindResult ns_result = finder.find(finder.getOrigin(),
-                                                   RRType::NS(), NULL,
-                                                   dnssec_opt_);
+                                                   RRType::NS(), dnssec_opt_);
     // zone origin name should have NS records
     if (ns_result.code != ZoneFinder::SUCCESS) {
         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
@@ -229,7 +230,6 @@ Query::addAuthAdditional(ZoneFinder& finder) {
 
 void
 Query::process() {
-    bool keep_doing = true;
     const bool qtype_is_any = (qtype_ == RRType::ANY());
 
     response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
@@ -251,147 +251,151 @@ Query::process() {
     // Found a zone which is the nearest ancestor to QNAME, set the AA bit
     response_.setHeaderFlag(Message::HEADERFLAG_AA);
     response_.setRcode(Rcode::NOERROR());
-    while (keep_doing) {
-        keep_doing = false;
-        std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
-        const ZoneFinder::FindResult db_result(
-            zfinder.find(qname_, qtype_, target.get(), dnssec_opt_));
-        switch (db_result.code) {
-            case ZoneFinder::DNAME: {
-                // First, put the dname into the answer
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<RRset>(db_result.rrset),
-                    dnssec_);
+    std::vector<ConstRRsetPtr> target;
+    boost::function0<ZoneFinder::FindResult> find;
+    if (qtype_is_any) {
+        find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
+                           boost::ref(target), dnssec_opt_);
+    } else {
+        find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
+                           dnssec_opt_);
+    }
+    ZoneFinder::FindResult db_result(find());
+    switch (db_result.code) {
+        case ZoneFinder::DNAME: {
+            // First, put the dname into the answer
+            response_.addRRset(Message::SECTION_ANSWER,
+                boost::const_pointer_cast<RRset>(db_result.rrset),
+                dnssec_);
+            /*
+             * Empty DNAME should never get in, as it is impossible to
+             * create one in master file.
+             *
+             * FIXME: Other way to prevent this should be done
+             */
+            assert(db_result.rrset->getRdataCount() > 0);
+            // Get the data of DNAME
+            const rdata::generic::DNAME& dname(
+                dynamic_cast<const rdata::generic::DNAME&>(
+                db_result.rrset->getRdataIterator()->getCurrent()));
+            // The yet unmatched prefix dname
+            const Name prefix(qname_.split(0, qname_.getLabelCount() -
+                db_result.rrset->getName().getLabelCount()));
+            // If we put it together, will it be too long?
+            // (The prefix contains trailing ., which will be removed
+            if (prefix.getLength() - Name::ROOT_NAME().getLength() +
+                dname.getDname().getLength() > Name::MAX_WIRE) {
                 /*
-                 * Empty DNAME should never get in, as it is impossible to
-                 * create one in master file.
-                 *
-                 * FIXME: Other way to prevent this should be done
+                 * In case the synthesized name is too long, section 4.1
+                 * of RFC 2672 mandates we return YXDOMAIN.
                  */
-                assert(db_result.rrset->getRdataCount() > 0);
-                // Get the data of DNAME
-                const rdata::generic::DNAME& dname(
-                    dynamic_cast<const rdata::generic::DNAME&>(
-                    db_result.rrset->getRdataIterator()->getCurrent()));
-                // The yet unmatched prefix dname
-                const Name prefix(qname_.split(0, qname_.getLabelCount() -
-                    db_result.rrset->getName().getLabelCount()));
-                // If we put it together, will it be too long?
-                // (The prefix contains trailing ., which will be removed
-                if (prefix.getLength() - Name::ROOT_NAME().getLength() +
-                    dname.getDname().getLength() > Name::MAX_WIRE) {
-                    /*
-                     * In case the synthesized name is too long, section 4.1
-                     * of RFC 2672 mandates we return YXDOMAIN.
-                     */
-                    response_.setRcode(Rcode::YXDOMAIN());
-                    return;
-                }
-                // The new CNAME we are creating (it will be unsigned even
-                // with DNSSEC, the DNAME is signed and it can be validated
-                // by that)
-                RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
-                    RRType::CNAME(), db_result.rrset->getTTL()));
-                // Construct the new target by replacing the end
-                cname->addRdata(rdata::generic::CNAME(qname_.split(0,
-                    qname_.getLabelCount() -
-                    db_result.rrset->getName().getLabelCount()).
-                    concatenate(dname.getDname())));
-                response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
-                break;
+                response_.setRcode(Rcode::YXDOMAIN());
+                return;
             }
-            case ZoneFinder::CNAME:
-            case ZoneFinder::WILDCARD_CNAME:
-                /*
-                 * We don't do chaining yet. Therefore handling a CNAME is
-                 * mostly the same as handling SUCCESS, but we didn't get
-                 * what we expected. It means no exceptions in ANY or NS
-                 * on the origin (though CNAME in origin is probably
-                 * forbidden anyway).
-                 *
-                 * So, just put it there.
-                 */
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<RRset>(db_result.rrset),
-                    dnssec_);
+            // The new CNAME we are creating (it will be unsigned even
+            // with DNSSEC, the DNAME is signed and it can be validated
+            // by that)
+            RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
+                RRType::CNAME(), db_result.rrset->getTTL()));
+            // Construct the new target by replacing the end
+            cname->addRdata(rdata::generic::CNAME(qname_.split(0,
+                qname_.getLabelCount() -
+                db_result.rrset->getName().getLabelCount()).
+                concatenate(dname.getDname())));
+            response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
+            break;
+        }
+        case ZoneFinder::CNAME:
+        case ZoneFinder::WILDCARD_CNAME:
+            /*
+             * We don't do chaining yet. Therefore handling a CNAME is
+             * mostly the same as handling SUCCESS, but we didn't get
+             * what we expected. It means no exceptions in ANY or NS
+             * on the origin (though CNAME in origin is probably
+             * forbidden anyway).
+             *
+             * So, just put it there.
+             */
+            response_.addRRset(Message::SECTION_ANSWER,
+                boost::const_pointer_cast<RRset>(db_result.rrset),
+                dnssec_);
 
-                // If the answer is a result of wildcard substitution,
-                // add a proof that there's no closer name.
-                if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
-                    addWildcardProof(*result.zone_finder);
-                }
-                break;
-            case ZoneFinder::SUCCESS:
-            case ZoneFinder::WILDCARD:
-                if (qtype_is_any) {
-                    // If quety type is ANY, insert all RRs under the domain
-                    // into answer section.
-                    BOOST_FOREACH(RRsetPtr rrset, *target) {
-                        response_.addRRset(Message::SECTION_ANSWER, rrset,
-                                           dnssec_);
-                        // Handle additional for answer section
-                        addAdditional(*result.zone_finder, *rrset.get());
-                    }
-                } else {
+            // If the answer is a result of wildcard substitution,
+            // add a proof that there's no closer name.
+            if (dnssec_ && db_result.code == ZoneFinder::WILDCARD_CNAME) {
+                addWildcardProof(*result.zone_finder);
+            }
+            break;
+        case ZoneFinder::SUCCESS:
+        case ZoneFinder::WILDCARD:
+            if (qtype_is_any) {
+                // If quety type is ANY, insert all RRs under the domain
+                // into answer section.
+                BOOST_FOREACH(ConstRRsetPtr rrset, target) {
                     response_.addRRset(Message::SECTION_ANSWER,
-                        boost::const_pointer_cast<RRset>(db_result.rrset),
-                        dnssec_);
+                        boost::const_pointer_cast<RRset>(rrset), dnssec_);
                     // Handle additional for answer section
-                    addAdditional(*result.zone_finder, *db_result.rrset);
+                    addAdditional(*result.zone_finder, *rrset.get());
                 }
-                // If apex NS records haven't been provided in the answer
-                // section, insert apex NS records into the authority section
-                // and AAAA/A RRS of each of the NS RDATA into the additional
-                // section.
-                if (qname_ != result.zone_finder->getOrigin() ||
-                    db_result.code != ZoneFinder::SUCCESS ||
-                    (qtype_ != RRType::NS() && !qtype_is_any))
-                {
-                    addAuthAdditional(*result.zone_finder);
-                }
-
-                // If the answer is a result of wildcard substitution,
-                // add a proof that there's no closer name.
-                if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
-                    addWildcardProof(*result.zone_finder);
-                }
-                break;
-            case ZoneFinder::DELEGATION:
-                response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-                response_.addRRset(Message::SECTION_AUTHORITY,
+            } else {
+                response_.addRRset(Message::SECTION_ANSWER,
                     boost::const_pointer_cast<RRset>(db_result.rrset),
                     dnssec_);
+                // Handle additional for answer section
                 addAdditional(*result.zone_finder, *db_result.rrset);
-                break;
-            case ZoneFinder::NXDOMAIN:
-                response_.setRcode(Rcode::NXDOMAIN());
-                addSOA(*result.zone_finder);
-                if (dnssec_ && db_result.rrset) {
-                    addNXDOMAINProof(zfinder, db_result.rrset);
-                }
-                break;
-            case ZoneFinder::NXRRSET:
-                addSOA(*result.zone_finder);
-                if (dnssec_ && db_result.rrset) {
-                    response_.addRRset(Message::SECTION_AUTHORITY,
-                                       boost::const_pointer_cast<RRset>(
-                                           db_result.rrset),
-                                       dnssec_);
-                }
-                break;
-            case ZoneFinder::WILDCARD_NXRRSET:
-                addSOA(*result.zone_finder);
-                if (dnssec_ && db_result.rrset) {
-                    addWildcardNXRRSETProof(zfinder, db_result.rrset);
-                }
-                break;
-            default:
-                // This is basically a bug of the data source implementation,
-                // but could also happen in the middle of development where
-                // we try to add a new result code.
-                isc_throw(isc::NotImplemented, "Unknown result code");
-                break;
-        }
+            }
+            // If apex NS records haven't been provided in the answer
+            // section, insert apex NS records into the authority section
+            // and AAAA/A RRS of each of the NS RDATA into the additional
+            // section.
+            if (qname_ != result.zone_finder->getOrigin() ||
+                db_result.code != ZoneFinder::SUCCESS ||
+                (qtype_ != RRType::NS() && !qtype_is_any))
+            {
+                addAuthAdditional(*result.zone_finder);
+            }
+
+            // If the answer is a result of wildcard substitution,
+            // add a proof that there's no closer name.
+            if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
+                addWildcardProof(*result.zone_finder);
+            }
+            break;
+        case ZoneFinder::DELEGATION:
+            response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
+            response_.addRRset(Message::SECTION_AUTHORITY,
+                boost::const_pointer_cast<RRset>(db_result.rrset),
+                dnssec_);
+            addAdditional(*result.zone_finder, *db_result.rrset);
+            break;
+        case ZoneFinder::NXDOMAIN:
+            response_.setRcode(Rcode::NXDOMAIN());
+            addSOA(*result.zone_finder);
+            if (dnssec_ && db_result.rrset) {
+                addNXDOMAINProof(zfinder, db_result.rrset);
+            }
+            break;
+        case ZoneFinder::NXRRSET:
+            addSOA(*result.zone_finder);
+            if (dnssec_ && db_result.rrset) {
+                response_.addRRset(Message::SECTION_AUTHORITY,
+                                   boost::const_pointer_cast<RRset>(
+                                       db_result.rrset),
+                                   dnssec_);
+            }
+            break;
+        case ZoneFinder::WILDCARD_NXRRSET:
+            addSOA(*result.zone_finder);
+            if (dnssec_ && db_result.rrset) {
+                addWildcardNXRRSETProof(zfinder, db_result.rrset);
+            }
+            break;
+        default:
+            // This is basically a bug of the data source implementation,
+            // but could also happen in the middle of development where
+            // we try to add a new result code.
+            isc_throw(isc::NotImplemented, "Unknown result code");
+            break;
     }
 }
 

+ 25 - 13
src/bin/auth/tests/query_unittest.cc

@@ -211,8 +211,10 @@ public:
     virtual isc::dns::RRClass getClass() const { return (rrclass_); }
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
-                            RRsetList* target = NULL,
                             const FindOptions options = FIND_DEFAULT);
+    virtual FindResult findAll(const isc::dns::Name& name,
+                               std::vector<ConstRRsetPtr>& target,
+                               const FindOptions options = FIND_DEFAULT);
 
     // If false is passed, it makes the zone broken as if it didn't have the
     // SOA.
@@ -305,8 +307,29 @@ substituteWild(const RRset& wild_rrset, const Name& real_name) {
 }
 
 ZoneFinder::FindResult
+MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
+                        const FindOptions options)
+{
+    ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
+    if (result.code == NXRRSET) {
+        const Domains::const_iterator found_domain = domains_.find(name);
+        if (!found_domain->second.empty()) {
+            for (RRsetStore::const_iterator found_rrset =
+                 found_domain->second.begin();
+                 found_rrset != found_domain->second.end(); ++found_rrset) {
+                // Insert RRs under the domain name into target
+                target.push_back(found_rrset->second);
+            }
+            return (FindResult(SUCCESS, RRsetPtr()));
+        }
+    }
+
+    return (result);
+}
+
+ZoneFinder::FindResult
 MockZoneFinder::find(const Name& name, const RRType& type,
-                     RRsetList* target, const FindOptions options)
+                     const FindOptions options)
 {
     // Emulating a broken zone: mandatory apex RRs are missing if specifically
     // configured so (which are rare cases).
@@ -358,17 +381,6 @@ MockZoneFinder::find(const Name& name, const RRType& type,
             return (FindResult(SUCCESS, rrset));
         }
 
-        // If not found but we have a target, fill it with all RRsets here
-        if (!found_domain->second.empty() && target != NULL) {
-            for (found_rrset = found_domain->second.begin();
-                 found_rrset != found_domain->second.end(); ++found_rrset) {
-                // Insert RRs under the domain name into target
-                target->addRRset(
-                    boost::const_pointer_cast<RRset>(found_rrset->second));
-            }
-            return (FindResult(SUCCESS, found_domain->second.begin()->second));
-        }
-
         // Otherwise, if this domain name has CNAME, return it.
         found_rrset = found_domain->second.find(RRType::CNAME());
         if (found_rrset != found_domain->second.end()) {

+ 64 - 16
src/lib/datasrc/database.cc

@@ -176,7 +176,8 @@ private:
 
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
-                                  bool check_ns, const string* construct_name)
+                                  bool check_ns, const string* construct_name,
+                                  bool any)
 {
     RRsigStore sig_store;
     bool records_found = false;
@@ -221,7 +222,7 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
                      columns[DatabaseAccessor::RDATA_COLUMN]));
             }
 
-            if (types.find(cur_type) != types.end()) {
+            if (types.find(cur_type) != types.end() || any) {
                 // This type is requested, so put it into result
                 const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
                 // Ths sigtype column was an optimization for finding the
@@ -286,6 +287,12 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
         sig_store.appendSignatures(i->second);
     }
 
+    if (records_found && any) {
+        result[RRType::ANY()] = RRsetPtr();
+        // These will be sitting on the other RRsets.
+        result.erase(RRType::RRSIG());
+    }
+
     return (FoundRRsets(records_found, result));
 }
 
@@ -389,6 +396,25 @@ DatabaseClient::Finder::findNSECCover(const Name& name) {
     return (ConstRRsetPtr());
 }
 
+ZoneFinder::FindResult
+DatabaseClient::Finder::findAll(const isc::dns::Name& name,
+                                std::vector<isc::dns::ConstRRsetPtr>& target,
+                                const FindOptions options)
+{
+    return (findInternal(name, RRType::ANY(), &target, options));
+}
+
+ZoneFinder::FindResult
+DatabaseClient::Finder::find(const isc::dns::Name& name,
+                             const isc::dns::RRType& type,
+                             const FindOptions options)
+{
+    if (type == RRType::ANY()) {
+        isc_throw(isc::Unexpected, "Use findAll to answer ANY");
+    }
+    return (findInternal(name, type, NULL, options));
+}
+
 DatabaseClient::Finder::DelegationSearchResult
 DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
                                             const FindOptions options)
@@ -550,7 +576,8 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
 ZoneFinder::FindResult
 DatabaseClient::Finder::findWildcardMatch(
     const isc::dns::Name& name, const isc::dns::RRType& type,
-    const FindOptions options, const DelegationSearchResult& dresult)
+    const FindOptions options, const DelegationSearchResult& dresult,
+    std::vector<isc::dns::ConstRRsetPtr>* target)
 {
     // Note that during the search we are going to search not only for the
     // requested type, but also for types that indicate a delegation -
@@ -569,7 +596,7 @@ DatabaseClient::Finder::findWildcardMatch(
         // RFC 4592 section 4.4).
         // Search for a match.  The types are the same as with original query.
         FoundRRsets found = getRRsets(wildcard, final_types, true,
-                                      &construct_name);
+                                      &construct_name, type == RRType::ANY());
         if (found.first) {
             // Found something - but what?
 
@@ -594,7 +621,7 @@ DatabaseClient::Finder::findWildcardMatch(
                 // The wildcard match is the best one, find the final result
                 // at it.  Note that wildcard should never be the zone origin.
                 return (findOnNameResult(name, type, options, false,
-                                         found, &wildcard));
+                                         found, &wildcard, target));
             } else {
 
                 // more specified match found, cancel wildcard match
@@ -660,7 +687,9 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                          const FindOptions options,
                                          const bool is_origin,
                                          const FoundRRsets& found,
-                                         const string* wildname)
+                                         const string* wildname,
+                                         std::vector<isc::dns::ConstRRsetPtr>*
+                                         target)
 {
     const bool wild = (wildname != NULL);
 
@@ -698,14 +727,29 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                    DATASRC_DATABASE_FOUND_CNAME));
 
     } else if (wti != found.second.end()) {
+        bool any(type == RRType::ANY());
+        isc::log::MessageID lid(wild ? DATASRC_DATABASE_WILDCARD_MATCH :
+                                DATASRC_DATABASE_FOUND_RRSET);
+        if (any) {
+            // An ANY query, copy everything to the target instead of returning
+            // directly.
+            for (FoundIterator it(found.second.begin());
+                 it != found.second.end(); ++it) {
+                if (it->second) {
+                    // Skip over the empty ANY
+                    target->push_back(it->second);
+                }
+            }
+            lid = wild ? DATASRC_DATABASE_WILDCARD_ANY :
+                DATASRC_DATABASE_FOUND_ANY;
+        }
         // Found an RR matching the query, so return it.  (Note that this
         // includes the case where we were explicitly querying for a CNAME and
         // found it.  It also includes the case where we were querying for an
         // NS RRset and found it at the apex of the zone.)
         return (logAndCreateResult(name, wildname, type,
                                    wild ? WILDCARD : SUCCESS, wti->second,
-                                   wild ? DATASRC_DATABASE_WILDCARD_MATCH :
-                                   DATASRC_DATABASE_FOUND_RRSET));
+                                   lid));
     }
 
     // If we get here, we have found something at the requested name but not
@@ -747,7 +791,9 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
 ZoneFinder::FindResult
 DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
                                          FindOptions options,
-                                         const DelegationSearchResult& dresult)
+                                         const DelegationSearchResult& dresult,
+                                         std::vector<isc::dns::ConstRRsetPtr>*
+                                         target)
 {
     const bool dnssec_data = ((options & FIND_DNSSEC) != 0);
 
@@ -771,7 +817,7 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
         // (i.e. all results except NXDOMAIN) return it; otherwise fall
         // through to the NXDOMAIN case below.
         const ZoneFinder::FindResult wresult =
-            findWildcardMatch(name, type, options, dresult);
+            findWildcardMatch(name, type, options, dresult, target);
         if (wresult.code != NXDOMAIN) {
             return (FindResult(wresult.code, wresult.rrset));
         }
@@ -786,9 +832,9 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
 }
 
 ZoneFinder::FindResult
-DatabaseClient::Finder::find(const isc::dns::Name& name,
+DatabaseClient::Finder::findInternal(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
-                             isc::dns::RRsetList*,
+                             std::vector<isc::dns::ConstRRsetPtr>* target,
                              const FindOptions options)
 {
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
@@ -820,16 +866,18 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     WantedTypes final_types(FINAL_TYPES());
     final_types.insert(type);
     const FoundRRsets found = getRRsets(name.toText(), final_types,
-                                        !is_origin);
+                                        !is_origin, NULL,
+                                        type == RRType::ANY());
 
     if (found.first) {
         // Something found at the domain name.  Look into it further to get
         // the final result.
-        return (findOnNameResult(name, type, options, is_origin, found, NULL));
+        return (findOnNameResult(name, type, options, is_origin, found, NULL,
+                                 target));
     } else {
         // Did not find anything at all at the domain name, so check for
         // subdomains or wildcards.
-        return (findNoNameResult(name, type, options, dresult));
+        return (findNoNameResult(name, type, options, dresult, target));
     }
 }
 
@@ -897,7 +945,7 @@ public:
         // Find the SOA of the zone (may or may not succeed).  Note that
         // this must be done before starting the iteration context.
         soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
-            find(zone_name, RRType::SOA(), NULL).rrset;
+            find(zone_name, RRType::SOA()).rrset;
 
         // Request the context
         context_ = accessor_->getAllRecords(zone.second);

+ 101 - 71
src/lib/datasrc/database.h

@@ -714,11 +714,6 @@ public:
         /// (this implementation is not complete, and currently only
         /// does full matches, CNAMES, and the signatures for matches and
         /// CNAMEs)
-        /// \note target was used in the original design to handle ANY
-        ///       queries. This is not implemented yet, and may use
-        ///       target again for that, but it might also use something
-        ///       different. It is left in for compatibility at the moment.
-        /// \note options are ignored at this moment
         ///
         /// \note Maybe counter intuitively, this method is not a const member
         /// function.  This is intentional; some of the underlying
@@ -741,13 +736,19 @@ public:
         ///
         /// \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 ZoneFinder::FindOptions.
         virtual FindResult find(const isc::dns::Name& name,
                                 const isc::dns::RRType& type,
-                                isc::dns::RRsetList* target = NULL,
                                 const FindOptions options = FIND_DEFAULT);
+        /// \brief Implementation of the ZoneFinder::findAll method.
+        ///
+        /// In short, it is mostly the same thing as find, but it returns all
+        /// RRsets in the named node through the target parameter in successful
+        /// case. It acts the same in the unsuccessful one.
+        virtual FindResult findAll(const isc::dns::Name& name,
+                                   std::vector<isc::dns::ConstRRsetPtr>& target,
+                                   const FindOptions options = FIND_DEFAULT);
 
         /// \brief Implementation of ZoneFinder::findPreviousName method.
         virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
@@ -773,12 +774,57 @@ public:
         boost::shared_ptr<DatabaseAccessor> accessor_;
         const int zone_id_;
         const isc::dns::Name origin_;
-        //
         /// \brief Shortcut name for the result of getRRsets
         typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
             FoundRRsets;
         /// \brief Just shortcut for set of types
         typedef std::set<dns::RRType> WantedTypes;
+        /// \brief Internal logit of find and findAll methods.
+        ///
+        /// Most of their handling is in the "error" cases and delegations
+        /// and so on. So they share the logic here and find and findAll provide
+        /// just an interface for it.
+        ///
+        /// Parameters and behaviour is like of those combined together.
+        /// Unexpected parameters, like type != ANY and having the target, are
+        /// just that - unexpected and not checked.
+        FindResult findInternal(const isc::dns::Name& name,
+                                const isc::dns::RRType& type,
+                                std::vector<isc::dns::ConstRRsetPtr>* target,
+                                const FindOptions options = FIND_DEFAULT);
+        /// \brief Searches database for RRsets of one domain.
+        ///
+        /// This method scans RRs of single domain specified by name and
+        /// extracts any RRsets found and requested by parameters.
+        ///
+        /// It is used internally by find(), because it is called multiple
+        /// times (usually with different domains).
+        ///
+        /// \param name Which domain name should be scanned.
+        /// \param types List of types the caller is interested in.
+        /// \param check_ns If this is set to true, it checks nothing lives
+        ///     together with NS record (with few little exceptions, like RRSIG
+        ///     or NSEC). This check is meant for non-apex NS records.
+        /// \param construct_name If this is NULL, the resulting RRsets have
+        ///     their name set to name. If it is not NULL, it overrides the name
+        ///     and uses this one (this can be used for wildcard synthesized
+        ///     records).
+        /// \param any If this is true, it records all the types, not only the
+        ///     ones requested by types. It also puts a NULL pointer under the
+        ///     ANY type into the result, if it finds any RRs at all, to easy the
+        ///     identification of success.
+        /// \return A pair, where the first element indicates if the domain
+        ///     contains any RRs at all (not only the requested, it may happen
+        ///     this is set to true, but the second part is empty). The second
+        ///     part is map from RRtypes to RRsets of the corresponding types.
+        ///     If the RRset is not present in DB, the RRtype is not there at
+        ///     all (so you'll not find NULL pointer in the result).
+        /// \throw DataSourceError If there's a low-level error with the
+        ///     database or the database contains bad data.
+        FoundRRsets getRRsets(const std::string& name,
+                              const WantedTypes& types, bool check_ns,
+                              const std::string* construct_name = NULL,
+                              bool any = false);
 
         /// \brief Search result of \c findDelegationPoint().
         ///
@@ -813,35 +859,6 @@ public:
             const size_t last_known; ///< No. labels in last non-empty domain
         };
 
-        /// \brief Searches database for RRsets of one domain.
-        ///
-        /// This method scans RRs of single domain specified by name and
-        /// extracts any RRsets found and requested by parameters.
-        ///
-        /// It is used internally by find(), because it is called multiple
-        /// times (usually with different domains).
-        ///
-        /// \param name Which domain name should be scanned.
-        /// \param types List of types the caller is interested in.
-        /// \param check_ns If this is set to true, it checks nothing lives
-        ///     together with NS record (with few little exceptions, like RRSIG
-        ///     or NSEC). This check is meant for non-apex NS records.
-        /// \param construct_name If this is NULL, the resulting RRsets have
-        ///     their name set to name. If it is not NULL, it overrides the name
-        ///     and uses this one (this can be used for wildcard synthesized
-        ///     records).
-        /// \return A pair, where the first element indicates if the domain
-        ///     contains any RRs at all (not only the requested, it may happen
-        ///     this is set to true, but the second part is empty). The second
-        ///     part is map from RRtypes to RRsets of the corresponding types.
-        ///     If the RRset is not present in DB, the RRtype is not there at
-        ///     all (so you'll not find NULL pointer in the result).
-        /// \throw DataSourceError If there's a low-level error with the
-        ///     database or the database contains bad data.
-        FoundRRsets getRRsets(const std::string& name,
-                              const WantedTypes& types, bool check_ns,
-                              const std::string* construct_name = NULL);
-
         /// \brief Find delegation point
         ///
         /// Given a name, searches through the superdomains from the origin
@@ -908,6 +925,9 @@ public:
         ///        for ZoneFinder::FindOptions.
         /// \param dresult Result of the search through the zone for a
         ///        delegation.
+        /// \param target If the type happens to be ANY, it will insert all
+        ///        the RRsets of the found name (if any is found) here instead
+        ///        of being returned by the result.
         ///
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
@@ -919,49 +939,54 @@ public:
         FindResult findWildcardMatch(
             const isc::dns::Name& name,
             const isc::dns::RRType& type, const FindOptions options,
-            const DelegationSearchResult& dresult);
+            const DelegationSearchResult& dresult,
+            std::vector<isc::dns::ConstRRsetPtr>* target);
 
-	/// \brief Handle matching results for name
-	///
-	/// This is called when something is found in the underlying database
-	/// whose domain name is an exact match of the name to be searched for.
-	/// It explores four possible cases to decide the final lookup result:
-	/// - The name is a zone cut due to an NS RR.
-	/// - CNAME is found (while the requested RR type is not CNAME).
-	///   In this case multiple CNAMEs are checked and rejected with
-	///   a \c DataSourceError exception.
-	/// - Requested type is not found at that name.
-	/// - A record of the requested type is found.
-	/// and returns a corresponding find result.
-	///
-	/// This method is commonly used for normal (non wildcard) and wildcard
-	/// matches.
-	///
+        /// \brief Handle matching results for name
+        ///
+        /// This is called when something is found in the underlying database
+        /// whose domain name is an exact match of the name to be searched for.
+        /// It explores four possible cases to decide the final lookup result:
+        /// - The name is a zone cut due to an NS RR.
+        /// - CNAME is found (while the requested RR type is not CNAME).
+        ///   In this case multiple CNAMEs are checked and rejected with
+        ///   a \c DataSourceError exception.
+        /// - Requested type is not found at that name.
+        /// - A record of the requested type is found, or the query is ANY and
+        ///   some records were found.
+        /// and returns a corresponding find result.
+        ///
+        /// This method is commonly used for normal (non wildcard) and wildcard
+        /// matches.
+        ///
         /// \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 is_origin If name is the zone's origin name.
-	/// \param found A set of found RRsets in the search for the name
-	///        and type.  It could contain one or more of the requested
-	///        type, CNAME, NS, and NSEC RRsets of the name.
-	/// \param wildname If non NULL, the method is called on a wildcard
-	///                 match, and points to a string object representing
-	///                 a textual form of the matched wildcard name;
-	///                 it's NULL in the case of non wildcard match.
-	///
+        /// \param is_origin If name is the zone's origin name.
+        /// \param found A set of found RRsets in the search for the name
+        ///        and type.  It could contain one or more of the requested
+        ///        type, CNAME, NS, and NSEC RRsets of the name.
+        /// \param wildname If non NULL, the method is called on a wildcard
+        ///                 match, and points to a string object representing
+        ///                 a textual form of the matched wildcard name;
+        ///                 it's NULL in the case of non wildcard match.
+        /// \param target When the query is any, this must be set to a vector
+        ///    where the result will be stored.
+        ///
         /// \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 (corresponding to the each of
-	///         the above 4 cases).  The return value is intended to be
-	///         usable as a return value of the caller of this helper
-	///         method.
+        ///         indicating the match type (corresponding to the each of
+        ///         the above 4 cases).  The return value is intended to be
+        ///         usable as a return value of the caller of this helper
+        ///         method.
         FindResult findOnNameResult(const isc::dns::Name& name,
 				    const isc::dns::RRType& type,
 				    const FindOptions options,
 				    const bool is_origin,
 				    const FoundRRsets& found,
-				    const std::string* wildname);
+				    const std::string* wildname,
+                    std::vector<isc::dns::ConstRRsetPtr>* target);
 
         /// \brief Handle no match for name
         ///
@@ -983,6 +1008,9 @@ public:
         ///        for ZoneFinder::FindOptions.
         /// \param dresult Result of the search through the zone for a
         ///        delegation.
+        /// \param target If the query is for type ANY, the successfull result,
+        ///        if there happens to be one, will be returned through the
+        ///        parameter, as it doesn't fit into the result.
         ///
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
@@ -992,17 +1020,19 @@ public:
         FindResult findNoNameResult(const isc::dns::Name& name,
                                     const isc::dns::RRType& type,
                                     FindOptions options,
-                                    const DelegationSearchResult& dresult);
+                                    const DelegationSearchResult& dresult,
+                                    std::vector<isc::dns::ConstRRsetPtr>*
+                                    target);
 
         /// Logs condition and creates result
         ///
         /// A convenience function used by findOnNameResult(), it both creates
-	/// the FindResult object that find() will return to its caller as well
+        /// the FindResult object that find() will return to its caller as well
         /// as logging a debug message for the information being returned.
         ///
         /// \param name Domain name of the RR that was being sought.
         /// \param wildname Domain name string of a matched wildcard name or
-	/// NULL for non wildcard match.
+        /// NULL for non wildcard match.
         /// \param type Type of RR being sought.
         /// \param code Result of the find operation
         /// \param rrset RRset found as a result of the find (which may be

+ 9 - 0
src/lib/datasrc/datasrc_messages.mes

@@ -78,6 +78,10 @@ different TTL values. This isn't allowed on the wire and is considered
 an error, so we set it to the lowest value we found (but we don't modify the
 database). The data in database should be checked and fixed.
 
+% DATASRC_DATABASE_FOUND_ANY search in datasource %1 resulted in returning all records of %2
+The data returned by the database backend contained data for the given domain
+name, so all the RRsets of the domain are returned.
+
 % DATASRC_DATABASE_FOUND_CNAME search in datasource %1 for %2/%3/%4 found CNAME, resulting in %5
 When searching the domain for a name a CNAME was found at that name.
 Even though it was not the RR type being sought, it is returned.  (The
@@ -178,6 +182,11 @@ whether the data is still valid.  The zone name, its class, and the
 underlying database name as well as the error message thrown from the
 database module are shown in the log message.
 
+% DATASRC_DATABASE_WILDCARD_ANY search in datasource %1 resulted in wildcard match type ANY on %2
+The database doesn't contain directly matching name.  When searching
+for a wildcard match, a wildcard record matching the name of the query
+containing some RRsets was found. All the RRsets of the node are returned.
+
 % DATASRC_DATABASE_WILDCARD_CANCEL_NS canceled wildcard match on %3 because %2 contains NS (data source %1)
 The database was queried to provide glue data and it didn't find direct match.
 It could create it from given wildcard, but matching wildcards is forbidden

+ 13 - 6
src/lib/datasrc/memory_datasrc.cc

@@ -422,7 +422,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
 
     // Implementation of InMemoryZoneFinder::find
     FindResult find(const Name& name, RRType type,
-                    RRsetList* target, const FindOptions options) const
+                    std::vector<ConstRRsetPtr> *target,
+                    const FindOptions options) const
     {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
             arg(type);
@@ -572,9 +573,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             for (found = node->getData()->begin();
                  found != node->getData()->end(); ++found)
             {
-                target->addRRset(
-                    boost::const_pointer_cast<RRset>(prepareRRset(name,
-                    found->second, rename)));
+                target->push_back(prepareRRset(name, found->second, rename));
             }
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
                 arg(name);
@@ -629,9 +628,17 @@ InMemoryZoneFinder::getClass() const {
 
 ZoneFinder::FindResult
 InMemoryZoneFinder::find(const Name& name, const RRType& type,
-                 RRsetList* target, const FindOptions options)
+                 const FindOptions options)
+{
+    return (impl_->find(name, type, NULL, options));
+}
+
+ZoneFinder::FindResult
+InMemoryZoneFinder::findAll(const Name& name,
+                            std::vector<ConstRRsetPtr>& target,
+                            const FindOptions options)
 {
-    return (impl_->find(name, type, target, options));
+    return (impl_->find(name, RRType::ANY(), &target, options));
 }
 
 result::Result

+ 10 - 4
src/lib/datasrc/memory_datasrc.h

@@ -69,14 +69,20 @@ public:
     ///
     /// See documentation in \c Zone.
     ///
-    /// It returns NULL pointer in case of NXDOMAIN and NXRRSET,
-    /// and also SUCCESS if target is not NULL(TYPE_ANY query).
-    /// (the base class documentation does not seem to require that).
+    /// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
-                            isc::dns::RRsetList* target = NULL,
                             const FindOptions options = FIND_DEFAULT);
 
+    /// \brief Version of find that returns all types at once
+    ///
+    /// It acts the same as find, just that when the correct node is found,
+    /// all the RRsets are filled into the target parameter instead of being
+    /// returned by the result.
+    virtual FindResult findAll(const isc::dns::Name& name,
+                               std::vector<isc::dns::ConstRRsetPtr>& target,
+                               const FindOptions options = FIND_DEFAULT);
+
     /// \brief Imelementation of the ZoneFinder::findPreviousName method
     ///
     /// This one throws NotImplemented exception, as InMemory doesn't

+ 144 - 18
src/lib/datasrc/tests/database_unittest.cc

@@ -1419,8 +1419,7 @@ doFindTest(ZoneFinder& finder,
            const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
 {
     SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
-    const ZoneFinder::FindResult result = finder.find(name, type, NULL,
-                                                      options);
+    const ZoneFinder::FindResult result = finder.find(name, type, options);
     ASSERT_EQ(expected_result, result.code) << name << " " << type;
     if (!expected_rdatas.empty() && result.rrset) {
         checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
@@ -1444,6 +1443,39 @@ doFindTest(ZoneFinder& finder,
     }
 }
 
+void
+doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
+                    ZoneFinder::Result expected_result,
+                    const isc::dns::RRType expected_type,
+                    std::vector<std::string> expected_rdata,
+                    const isc::dns::Name& expected_name =
+                    isc::dns::Name::ROOT_NAME(),
+                    const ZoneFinder::FindOptions options =
+                    ZoneFinder::FIND_DEFAULT)
+{
+    SCOPED_TRACE("All test for " + name.toText());
+    std::vector<ConstRRsetPtr> target;
+    ZoneFinder::FindResult result(finder.findAll(name, target, options));
+    EXPECT_TRUE(target.empty());
+    EXPECT_EQ(expected_result, result.code);
+    EXPECT_EQ(expected_type, result.rrset->getType());
+    RdataIteratorPtr it(result.rrset->getRdataIterator());
+    std::vector<std::string> rdata;
+    while (!it->isLast()) {
+        rdata.push_back(it->getCurrent().toText());
+        it->next();
+    }
+    std::sort(rdata.begin(), rdata.end());
+    std::sort(expected_rdata.begin(), expected_rdata.end());
+    ASSERT_EQ(expected_rdata.size(), rdata.size());
+    for (size_t i(0); i < expected_rdata.size(); ++ i) {
+        EXPECT_EQ(expected_rdata[i], rdata[i]);
+    }
+    EXPECT_TRUE(expected_rdata == rdata);
+    EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
+              expected_name, result.rrset->getName());
+}
+
 // When asking for an RRset where RRs somehow have different TTLs, it should 
 // convert to the lowest one.
 TEST_F(MockDatabaseClientTest, ttldiff) {
@@ -1670,58 +1702,58 @@ TYPED_TEST(DatabaseClientTest, find) {
 
     EXPECT_THROW(finder->find(isc::dns::Name("badcname1.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badcname2.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badcname3.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badrdata.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badtype.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badttl.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("badsig.example.org."),
                                               this->qtype_,
-                                              NULL, ZoneFinder::FIND_DEFAULT),
+                                              ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
 
     // Trigger the hardcoded exceptions and see if find() has cleaned up
     if (this->is_mock_) {
         EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.search."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      DataSourceError);
         EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.search."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      isc::Exception);
         EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.search."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      std::exception);
         EXPECT_THROW(finder->find(isc::dns::Name("dsexception.in.getnext."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      DataSourceError);
         EXPECT_THROW(finder->find(isc::dns::Name("iscexception.in.getnext."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      isc::Exception);
         EXPECT_THROW(finder->find(isc::dns::Name("basicexception.in.getnext."),
                                   this->qtype_,
-                                  NULL, ZoneFinder::FIND_DEFAULT),
+                                  ZoneFinder::FIND_DEFAULT),
                      std::exception);
     }
 
@@ -1840,17 +1872,17 @@ TYPED_TEST(DatabaseClientTest, findDelegation) {
 
     // This is broken dname, it contains two targets
     EXPECT_THROW(finder->find(isc::dns::Name("below.baddname.example.org."),
-                              this->qtype_, NULL,
+                              this->qtype_,
                               ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
 
     // Broken NS - it lives together with something else
     EXPECT_THROW(finder->find(isc::dns::Name("brokenns1.example.org."),
-                              this->qtype_, NULL,
+                              this->qtype_,
                               ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
     EXPECT_THROW(finder->find(isc::dns::Name("brokenns2.example.org."),
-                              this->qtype_, NULL,
+                              this->qtype_,
                               ZoneFinder::FIND_DEFAULT),
                  DataSourceError);
 }
@@ -2255,6 +2287,100 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
                                Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
 }
 
+TYPED_TEST(DatabaseClientTest, anyFromFind) {
+    // Find will reject answering an ANY query
+    EXPECT_THROW(this->getFinder()->find(isc::dns::Name("www2.example.org."),
+                                         RRType::ANY()), isc::Unexpected);
+}
+
+// Test the findAll method.
+TYPED_TEST(DatabaseClientTest, getAll) {
+    // The domain doesn't exist, so we must get the right NSEC
+    boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
+
+    // It should act the same on the "failures"
+    std::vector<ConstRRsetPtr> target;
+    EXPECT_EQ(ZoneFinder::NXDOMAIN,
+              finder->findAll(isc::dns::Name("nothere.example.org."),
+                              target).code);
+    EXPECT_TRUE(target.empty());
+    EXPECT_EQ(ZoneFinder::NXRRSET,
+              finder->findAll(isc::dns::Name("here.wild.example.org."),
+                              target).code);
+    this->expected_rdatas_.push_back("ns.delegation.example.org.");
+    this->expected_rdatas_.push_back("ns.example.com.");
+    doFindAllTestResult(*finder, isc::dns::Name("xx.delegation.example.org."),
+                        ZoneFinder::DELEGATION, RRType::NS(),
+                        this->expected_rdatas_,
+                        isc::dns::Name("delegation.example.org."));
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("www.example.org.");
+    doFindAllTestResult(*finder, isc::dns::Name("cname.example.org"),
+                        ZoneFinder::CNAME, RRType::CNAME(),
+                        this->expected_rdatas_);
+    this->expected_rdatas_.clear();
+    this->expected_rdatas_.push_back("dname.example.com.");
+    doFindAllTestResult(*finder, isc::dns::Name("a.dname.example.org"),
+                        ZoneFinder::DNAME, RRType::DNAME(),
+                        this->expected_rdatas_,
+                        isc::dns::Name("dname.example.org."));
+    // It should get the data on success
+    EXPECT_EQ(ZoneFinder::SUCCESS,
+              finder->findAll(isc::dns::Name("www2.example.org."),
+                              target).code);
+    ASSERT_EQ(2, target.size());
+    size_t a_idx(target[1]->getType() == RRType::A());
+    EXPECT_EQ(RRType::A(), target[a_idx]->getType());
+    std::string previous;
+    size_t count(0);
+    for (RdataIteratorPtr it(target[a_idx]->getRdataIterator());
+         !it->isLast(); it->next()) {
+        count ++;
+        EXPECT_NE(previous, it->getCurrent().toText());
+        EXPECT_TRUE(it->getCurrent().toText() == "192.0.2.1" ||
+                    it->getCurrent().toText() == "192.0.2.2");
+        previous = it->getCurrent().toText();
+    }
+    EXPECT_EQ(2, count);
+    EXPECT_EQ(RRType::AAAA(), target[1 - a_idx]->getType());
+    RdataIteratorPtr it(target[1 - a_idx]->getRdataIterator());
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("2001:db8::1", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+
+    // And on wildcard. Check the signatures as well.
+    target.clear();
+    EXPECT_EQ(ZoneFinder::WILDCARD,
+              finder->findAll(isc::dns::Name("a.wild.example.org"),
+                              target, ZoneFinder::FIND_DNSSEC).code);
+    ASSERT_EQ(2, target.size());
+    a_idx = target[1]->getType() == RRType::A();
+    EXPECT_EQ(RRType::A(), target[a_idx]->getType());
+    it = target[a_idx]->getRdataIterator();
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("192.0.2.5", it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+    ConstRRsetPtr sig(target[a_idx]->getRRsig());
+    ASSERT_TRUE(sig);
+    EXPECT_EQ(RRType::RRSIG(), sig->getType());
+    EXPECT_EQ("A 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE",
+              sig->getRdataIterator()->getCurrent().toText());
+    EXPECT_EQ(RRType::NSEC(), target[1 - a_idx]->getType());
+    it = target[1 - a_idx]->getRdataIterator();
+    ASSERT_FALSE(it->isLast());
+    EXPECT_EQ("cancel.here.wild.example.org. A RRSIG NSEC",
+              it->getCurrent().toText());
+    it->next();
+    EXPECT_TRUE(it->isLast());
+    sig = target[1 - a_idx]->getRRsig();
+    ASSERT_TRUE(sig);
+    EXPECT_EQ(RRType::RRSIG(), sig->getType());
+    EXPECT_EQ("NSEC 5 3 3600 20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE",
+              sig->getRdataIterator()->getCurrent().toText());
+}
+
 TYPED_TEST(DatabaseClientTest, getOrigin) {
     DataSourceClient::FindResult
         zone(this->client_->findZone(Name("example.org")));

+ 72 - 66
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -16,6 +16,7 @@
 #include <vector>
 
 #include <boost/bind.hpp>
+#include <boost/foreach.hpp>
 
 #include <exceptions/exceptions.h>
 
@@ -389,7 +390,6 @@ public:
                   ZoneFinder::Result result,
                   bool check_answer = true,
                   const ConstRRsetPtr& answer = ConstRRsetPtr(),
-                  RRsetList* target = NULL,
                   InMemoryZoneFinder* zone_finder = NULL,
                   ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
                   bool check_wild_answer = false)
@@ -402,7 +402,7 @@ public:
         EXPECT_NO_THROW({
                 ZoneFinder::FindResult find_result(zone_finder->find(
                                                        name, rrtype,
-                                                       target, options));
+                                                       options));
                 // Check it returns correct answers
                 EXPECT_EQ(result, find_result.code);
                 if (check_answer) {
@@ -438,6 +438,32 @@ public:
                 }
             });
     }
+    /**
+     * \brief Calls the findAll on the finder and checks the result.
+     */
+    std::vector<ConstRRsetPtr> findAllTest(const Name& name,
+                                           ZoneFinder::Result result,
+                                           size_t expected_size,
+                                           InMemoryZoneFinder* finder = NULL,
+                                           const ConstRRsetPtr &rrset_result =
+                                           ConstRRsetPtr(),
+                                           ZoneFinder::FindOptions options =
+                                           ZoneFinder::FIND_DEFAULT)
+    {
+        if (finder == NULL) {
+            finder = &zone_finder_;
+        }
+        std::vector<ConstRRsetPtr> target;
+        ZoneFinder::FindResult findResult(finder->findAll(name, target,
+                                                          options));
+        EXPECT_EQ(result, findResult.code);
+        EXPECT_EQ(rrset_result, findResult.rrset);
+        BOOST_FOREACH(const ConstRRsetPtr& rrset, target) {
+            EXPECT_EQ(name, rrset->getName());
+        }
+        EXPECT_EQ(expected_size, target.size());
+        return (target);
+    }
     // Internal part of the cancelWildcard test that is multiple times
     void doCancelWildcardTest();
 };
@@ -522,7 +548,7 @@ TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
                                            RRTTL(300)));
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cname_under_cut_));
     findTest(Name("cname.child.example.org"), RRType::AAAA(),
-             ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL, NULL,
+             ZoneFinder::CNAME, true, rr_cname_under_cut_, NULL,
              ZoneFinder::FIND_GLUE_OK);
 }
 
@@ -598,7 +624,7 @@ TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
 
     findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
     findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
-             NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             NULL, ZoneFinder::FIND_GLUE_OK);
 }
 
 // Test adding child zones and zone cut handling
@@ -636,25 +662,19 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_glue_)));
 
     // origin
-    RRsetList origin_rrsets;
-    findTest(origin_, RRType::ANY(), ZoneFinder::SUCCESS, true,
-             ConstRRsetPtr(), &origin_rrsets);
-    EXPECT_EQ(2, origin_rrsets.size());
-    EXPECT_EQ(rr_a_, origin_rrsets.findRRset(RRType::A(), RRClass::IN()));
-    EXPECT_EQ(rr_ns_, origin_rrsets.findRRset(RRType::NS(), RRClass::IN()));
+    std::vector<ConstRRsetPtr> rrsets(findAllTest(origin_, ZoneFinder::SUCCESS,
+                                                  2));
+    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
+                                           rr_a_));
+    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
+                                           rr_ns_));
 
     // out zone name
-    RRsetList out_rrsets;
-    findTest(Name("example.com"), RRType::ANY(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), &out_rrsets);
-    EXPECT_EQ(0, out_rrsets.size());
-
-    RRsetList glue_child_rrsets;
-    findTest(rr_child_glue_->getName(), RRType::ANY(), ZoneFinder::SUCCESS,
-             true, ConstRRsetPtr(), &glue_child_rrsets);
-    EXPECT_EQ(rr_child_glue_, glue_child_rrsets.findRRset(RRType::A(),
-                                                     RRClass::IN()));
-    EXPECT_EQ(1, glue_child_rrsets.size());
+    findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN, 0);
+
+    rrsets = findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, 1);
+    EXPECT_FALSE(rrsets.end() == std::find(rrsets.begin(), rrsets.end(),
+                                           rr_child_glue_));
 
     // TODO: test NXRRSET case after rbtree non-terminal logic has
     // been implemented
@@ -663,16 +683,12 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
     EXPECT_NO_THROW(EXPECT_EQ(SUCCESS, zone_finder_.add(rr_child_ns_)));
 
     // zone cut
-    RRsetList child_rrsets;
-    findTest(rr_child_ns_->getName(), RRType::ANY(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_, &child_rrsets);
-    EXPECT_EQ(0, child_rrsets.size());
+    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION, 0, NULL,
+                rr_child_ns_);
 
     // glue for this zone cut
-    RRsetList new_glue_child_rrsets;
-    findTest(rr_child_glue_->getName(), RRType::ANY(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_, &new_glue_child_rrsets);
-    EXPECT_EQ(0, new_glue_child_rrsets.size());
+    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION, 0, NULL,
+                rr_child_ns_);
 }
 
 TEST_F(InMemoryZoneFinderTest, glue) {
@@ -693,26 +709,26 @@ TEST_F(InMemoryZoneFinderTest, glue) {
 
     // If we do it in the "glue OK" mode, we should find the exact match.
     findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
-             rr_child_glue_, NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             rr_child_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
 
     // glue OK + NXRRSET case
     findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
-             true, ConstRRsetPtr(), NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             true, ConstRRsetPtr(), NULL, ZoneFinder::FIND_GLUE_OK);
 
     // glue OK + NXDOMAIN case
     findTest(Name("www.child.example.org"), RRType::A(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL,
+             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
              ZoneFinder::FIND_GLUE_OK);
 
     // nested cut case.  The glue should be found.
     findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
              ZoneFinder::SUCCESS,
-             true, rr_grandchild_glue_, NULL, NULL, ZoneFinder::FIND_GLUE_OK);
+             true, rr_grandchild_glue_, NULL, ZoneFinder::FIND_GLUE_OK);
 
     // A non-existent name in nested cut.  This should result in delegation
     // at the highest zone cut.
     findTest(Name("www.grand.child.example.org"), RRType::TXT(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL,
+             ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
              ZoneFinder::FIND_GLUE_OK);
 }
 
@@ -801,14 +817,14 @@ TEST_F(InMemoryZoneFinderTest, load) {
 
     // Now see there are some rrsets (we don't look inside, though)
     findTest(Name("."), RRType::SOA(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &rootzone);
+             ConstRRsetPtr(), &rootzone);
     findTest(Name("."), RRType::NS(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &rootzone);
+             ConstRRsetPtr(), &rootzone);
     findTest(Name("a.root-servers.net."), RRType::A(), ZoneFinder::SUCCESS,
-             false, ConstRRsetPtr(), NULL, &rootzone);
+             false, ConstRRsetPtr(), &rootzone);
     // But this should no longer be here
     findTest(rr_ns_a_->getName(), RRType::AAAA(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), NULL, &rootzone);
+             ConstRRsetPtr(), &rootzone);
 
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
@@ -846,14 +862,14 @@ TEST_F(InMemoryZoneFinderTest, wildcard) {
     {
         SCOPED_TRACE("Search at created child");
         findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
-                 false, rr_wild_, NULL, NULL, ZoneFinder::FIND_DEFAULT, true);
+                 false, rr_wild_, NULL, ZoneFinder::FIND_DEFAULT, true);
     }
 
     // Search another created name, this time little bit lower
     {
         SCOPED_TRACE("Search at created grand-child");
         findTest(Name("a.b.wild.example.org"), RRType::A(),
-                 ZoneFinder::SUCCESS, false, rr_wild_, NULL, NULL,
+                 ZoneFinder::SUCCESS, false, rr_wild_, NULL,
                  ZoneFinder::FIND_DEFAULT, true);
     }
 
@@ -885,7 +901,7 @@ TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
     {
         SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
         findTest(Name("a.child.example.org"), RRType::A(),
-                 ZoneFinder::DELEGATION, true, rr_child_ns_, NULL, NULL,
+                 ZoneFinder::DELEGATION, true, rr_child_ns_, NULL,
                  ZoneFinder::FIND_GLUE_OK);
     }
 }
@@ -897,9 +913,9 @@ TEST_F(InMemoryZoneFinderTest, anyWildcard) {
     // First try directly the name (normal match)
     {
         SCOPED_TRACE("Asking direcly for *");
-        RRsetList target;
-        findTest(Name("*.wild.example.org"), RRType::ANY(),
-                 ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &target);
+        const std::vector<ConstRRsetPtr>
+            target(findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
+                               1));
         ASSERT_EQ(1, target.size());
         EXPECT_EQ(RRType::A(), (*target.begin())->getType());
         EXPECT_EQ(Name("*.wild.example.org"), (*target.begin())->getName());
@@ -908,10 +924,9 @@ TEST_F(InMemoryZoneFinderTest, anyWildcard) {
     // Then a wildcard match
     {
         SCOPED_TRACE("Asking in the wild way");
-        RRsetList target;
-        findTest(Name("a.wild.example.org"), RRType::ANY(),
-                 ZoneFinder::SUCCESS, true, ConstRRsetPtr(), &target);
-        ASSERT_EQ(1, target.size());
+        const std::vector<ConstRRsetPtr>
+            target(findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
+                               1));
         EXPECT_EQ(RRType::A(), (*target.begin())->getType());
         EXPECT_EQ(Name("a.wild.example.org"), (*target.begin())->getName());
     }
@@ -943,15 +958,9 @@ TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
 
     {
         SCOPED_TRACE("Asking for ANY record");
-        RRsetList normalTarget;
-        findTest(Name("*.foo.example.org"), RRType::ANY(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(), &normalTarget);
-        EXPECT_EQ(0, normalTarget.size());
-
-        RRsetList wildTarget;
-        findTest(Name("a.foo.example.org"), RRType::ANY(),
-                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(), &wildTarget);
-        EXPECT_EQ(0, wildTarget.size());
+        findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET, 0);
+
+        findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET, 0);
     }
 
     {
@@ -1011,10 +1020,7 @@ TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
         for (const char** name = names; *name != NULL; ++ name) {
             SCOPED_TRACE(string("Node ") + *name);
 
-            RRsetList target;
-            findTest(Name(*name), RRType::ANY(), ZoneFinder::NXRRSET, true,
-                ConstRRsetPtr(), &target);
-            EXPECT_EQ(0, target.size());
+            findAllTest(Name(*name), ZoneFinder::NXRRSET, 0);
         }
     }
 }
@@ -1054,7 +1060,7 @@ InMemoryZoneFinderTest::doCancelWildcardTest() {
             SCOPED_TRACE(string("Node ") + *name);
 
             findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
-                     rr_wild_, NULL, NULL, ZoneFinder::FIND_DEFAULT, true);
+                     rr_wild_, NULL, ZoneFinder::FIND_DEFAULT, true);
         }
     }
 
@@ -1125,13 +1131,13 @@ TEST_F(InMemoryZoneFinderTest, swap) {
     EXPECT_EQ(RRClass::IN(), finder2.getClass());
     // make sure the zone data is swapped, too
     findTest(origin_, RRType::NS(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), NULL, &finder1);
+             ConstRRsetPtr(), &finder1);
     findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &finder1);
+             ConstRRsetPtr(), &finder1);
     findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false,
-             ConstRRsetPtr(), NULL, &finder2);
+             ConstRRsetPtr(), &finder2);
     findTest(other_origin, RRType::TXT(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), NULL, &finder2);
+             ConstRRsetPtr(), &finder2);
 }
 
 TEST_F(InMemoryZoneFinderTest, getFileName) {

+ 20 - 6
src/lib/datasrc/zone.h

@@ -237,9 +237,6 @@ public:
     ///   a successful match, and the code of \c SUCCESS will be returned.
     /// - If the search name matches a delegation point of DNAME, it returns
     ///   the code of \c DNAME and that DNAME RR.
-    /// - If the target isn't NULL, all RRsets under the domain are inserted
-    ///   there and SUCCESS (or NXDOMAIN, in case of empty domain) is returned
-    ///   instead of normall processing. This is intended to handle ANY query.
     ///
     /// \note This behavior is controversial as we discussed in
     /// https://lists.isc.org/pipermail/bind10-dev/2011-January/001918.html
@@ -276,16 +273,33 @@ public:
     ///
     /// \param name The domain name to be searched for.
     /// \param type The RR type to be searched for.
-    /// \param target If target is not NULL, insert all RRs under the domain
-    /// into it.
     /// \param options The search options.
     /// \return A \c FindResult object enclosing the search result (see above).
     virtual FindResult find(const isc::dns::Name& name,
                             const isc::dns::RRType& type,
-                            isc::dns::RRsetList* target = NULL,
                             const FindOptions options
                             = FIND_DEFAULT) = 0;
 
+    ///
+    /// \brief Finds all RRsets in the given name.
+    ///
+    /// This function works almost exactly in the same way as the find one. The
+    /// only difference is, when the lookup is successful (eg. the code is
+    /// SUCCESS or WILDCARD), all the RRsets residing in the named node are
+    /// copied into the \c target parameter and the rrset member of the result
+    /// is NULL. All the other (unsuccessful) cases are handled the same,
+    /// including returning delegations, NSEC/NSEC3 proofs, etc. The options
+    /// parameter works the same way and it should conform to the same exception
+    /// restrictions.
+    ///
+    /// \param name \see find, parameter name
+    /// \param target the successfull result is returned through this
+    /// \param options \see find, parameter options
+    /// \return \see find and it's result
+    virtual FindResult findAll(const isc::dns::Name& name,
+                               std::vector<isc::dns::ConstRRsetPtr> &target,
+                               const FindOptions options = FIND_DEFAULT) = 0;
+
     /// \brief Get previous name in the zone
     ///
     /// Gets the previous name in the DNSSEC order. This can be used

+ 1 - 1
src/lib/python/isc/datasrc/finder_python.cc

@@ -74,7 +74,7 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
                 static_cast<ZoneFinder::FindOptions>(options_int);
             const ZoneFinder::FindResult find_result(
                 finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype),
-                             NULL, options));
+                             options));
             const ZoneFinder::Result r = find_result.code;
             isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
             if (rrsp) {