Browse Source

[master] Merge branch 'trac1747'

Jelte Jansen 13 years ago
parent
commit
12f57073eb

+ 3 - 2
src/bin/auth/auth_srv.cc

@@ -163,6 +163,8 @@ private:
 
     // validateStatistics
     bool validateStatistics(isc::data::ConstElementPtr data) const;
+
+    auth::Query query_;
 };
 
 AuthSrvImpl::AuthSrvImpl(const bool use_cache,
@@ -554,8 +556,7 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
         if (memory_client_ && memory_client_class_ == question->getClass()) {
             const RRType& qtype = question->getType();
             const Name& qname = question->getName();
-            auth::Query(*memory_client_, qname, qtype, message,
-                        dnssec_ok).process();
+            query_.process(*memory_client_, qname, qtype, message, dnssec_ok);
         } else {
             datasrc::Query query(message, cache_, dnssec_ok);
             data_sources_.doQuery(query);

+ 115 - 112
src/bin/auth/query.cc

@@ -42,6 +42,11 @@ public:
         msg_(msg), section_(section), dnssec_(dnssec)
     {}
     void operator()(const ConstRRsetPtr& rrset) {
+        /*
+         * FIXME:
+         * The const-cast is wrong, but the Message interface seems
+         * to insist.
+         */
         msg_.addRRset(section_,
                       boost::const_pointer_cast<AbstractRRset>(rrset),
                       dnssec_);
@@ -85,6 +90,7 @@ getAdditional(const Name& qname, RRType qtype,
         results.push_back(*it);
     }
 }
+
 }
 
 namespace isc {
@@ -98,13 +104,7 @@ Query::addSOA(ZoneFinder& finder) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
     } else {
-        /*
-         * FIXME:
-         * The const-cast is wrong, but the Message interface seems
-         * to insist.
-         */
-        response_.addRRset(Message::SECTION_AUTHORITY,
-            boost::const_pointer_cast<AbstractRRset>(soa_ctx->rrset), dnssec_);
+        authorities_.push_back(soa_ctx->rrset);
     }
 }
 
@@ -123,8 +123,7 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     }
 
     // Add the NSEC proving NXDOMAIN to the authority section.
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(nsec), dnssec_);
+    authorities_.push_back(nsec);
 
     // Next, identify the best possible wildcard name that would match
     // the query name.  It's the longer common suffix with the qname
@@ -135,14 +134,14 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // and the best possible wildcard is *.b.example.com.  If the NXDOMAIN
     // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
     // is the next domain of the NSEC, and we get the same wildcard name.
-    const int qlabels = qname_.getLabelCount();
-    const int olabels = qname_.compare(nsec->getName()).getCommonLabels();
-    const int nlabels = qname_.compare(
+    const int qlabels = qname_->getLabelCount();
+    const int olabels = qname_->compare(nsec->getName()).getCommonLabels();
+    const int nlabels = qname_->compare(
         dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
                                            getCurrent()).
         getNextName()).getCommonLabels();
     const int common_labels = std::max(olabels, nlabels);
-    const Name wildname(Name("*").concatenate(qname_.split(qlabels -
+    const Name wildname(Name("*").concatenate(qname_->split(qlabels -
                                                            common_labels)));
 
     // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
@@ -161,9 +160,7 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // stage of performance optimization, we should consider optimizing this
     // for some optimized data source implementations.
     if (nsec->getName() != fcontext->rrset->getName()) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
-                           dnssec_);
+        authorities_.push_back(fcontext->rrset);
     }
 }
 
@@ -183,16 +180,10 @@ Query::addClosestEncloserProof(ZoneFinder& finder, const Name& name,
     }
 
     if (add_closest) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               result.closest_proof),
-                           dnssec_);
+        authorities_.push_back(result.closest_proof);
     }
     if (result.next_proof) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               result.next_proof),
-                           dnssec_);
+        authorities_.push_back(result.next_proof);
     }
     return (result.closest_labels);
 }
@@ -208,10 +199,7 @@ Query::addNSEC3ForName(ZoneFinder& finder, const Name& name, bool match) {
                   << (result.matched ? "matching" : "covering")
                   << " NSEC3 found for " << name);
     }
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(
-                           result.closest_proof),
-                       dnssec_);
+    authorities_.push_back(result.closest_proof);
 }
 
 void
@@ -219,12 +207,12 @@ Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
     // Firstly get the NSEC3 proves for Closest Encloser Proof
     // See Section 7.2.1 of RFC 5155.
     const uint8_t closest_labels =
-        addClosestEncloserProof(finder, qname_, false);
+        addClosestEncloserProof(finder, *qname_, false);
 
     // Next, construct the wildcard name at the closest encloser, i.e.,
     // '*' followed by the closest encloser, and add NSEC3 for it.
     const Name wildname(Name("*").concatenate(
-               qname_.split(qname_.getLabelCount() - closest_labels)));
+               qname_->split(qname_->getLabelCount() - closest_labels)));
     addNSEC3ForName(finder, wildname, false);
 }
 
@@ -239,17 +227,14 @@ 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.
         ConstZoneFinderContextPtr fcontext =
-            finder.find(qname_, RRType::NSEC(),
+            finder.find(*qname_, RRType::NSEC(),
                         dnssec_opt_ | ZoneFinder::NO_WILDCARD);
         if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
             fcontext->rrset->getRdataCount() == 0) {
             isc_throw(BadNSEC,
                       "Unexpected NSEC result for wildcard proof");
         }
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               fcontext->rrset),
-                           dnssec_);
+        authorities_.push_back(fcontext->rrset);
     } else if (db_context.isNSEC3Signed()) {
         // Case for RFC 5155 Section 7.2.6.
         //
@@ -257,7 +242,7 @@ Query::addWildcardProof(ZoneFinder& finder,
         // of the matching wildcard, so NSEC3 for its next closer (and only
         // that NSEC3) is what we are expected to provided per the RFC (if
         // this assumption isn't met the zone is broken anyway).
-        addClosestEncloserProof(finder, qname_, false, false);
+        addClosestEncloserProof(finder, *qname_, false, false);
     }
 }
 
@@ -270,7 +255,7 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
     }
     
     ConstZoneFinderContextPtr fcontext =
-        finder.find(qname_, RRType::NSEC(),
+        finder.find(*qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
     if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
         fcontext->rrset->getRdataCount() == 0) {
@@ -279,9 +264,7 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
    
     if (nsec->getName() != fcontext->rrset->getName()) {
         // one NSEC RR proves wildcard_nxrrset that no matched QNAME.
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(fcontext->rrset),
-                           dnssec_);
+        authorities_.push_back(fcontext->rrset);
     }
 }
 
@@ -290,10 +273,7 @@ Query::addDS(ZoneFinder& finder, const Name& dname) {
     ConstZoneFinderContextPtr ds_context =
         finder.find(dname, RRType::DS(), dnssec_opt_);
     if (ds_context->code == ZoneFinder::SUCCESS) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               ds_context->rrset),
-                           dnssec_);
+        authorities_.push_back(ds_context->rrset);
     } else if (ds_context->code == ZoneFinder::NXRRSET &&
                ds_context->isNSECSigned()) {
         addNXRRsetProof(finder, *ds_context);
@@ -312,29 +292,26 @@ Query::addNXRRsetProof(ZoneFinder& finder,
                        const ZoneFinder::Context& db_context)
 {
     if (db_context.isNSECSigned() && db_context.rrset) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               db_context.rrset),
-                           dnssec_);
+        authorities_.push_back(db_context.rrset);
         if (db_context.isWildcard()) {
             addWildcardNXRRSETProof(finder, db_context.rrset);
         }
     } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
-        if (qtype_ == RRType::DS()) {
+        if (*qtype_ == RRType::DS()) {
             // RFC 5155, Section 7.2.4.  Add either NSEC3 for the qname or
             // closest (provable) encloser proof in case of optout.
-            addClosestEncloserProof(finder, qname_, true);
+            addClosestEncloserProof(finder, *qname_, true);
         } else {
             // RFC 5155, Section 7.2.3.  Just add NSEC3 for the qname.
-            addNSEC3ForName(finder, qname_, true);
+            addNSEC3ForName(finder, *qname_, true);
         }
     } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
         // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
         // qname, construct the matched wildcard name and add NSEC3 for it.
         const uint8_t closest_labels =
-            addClosestEncloserProof(finder, qname_, false);
+            addClosestEncloserProof(finder, *qname_, false);
         const Name wname = Name("*").concatenate(
-            qname_.split(qname_.getLabelCount() - closest_labels));
+            qname_->split(qname_->getLabelCount() - closest_labels));
         addNSEC3ForName(finder, wname, true);
     }
 }
@@ -354,10 +331,8 @@ Query::addAuthAdditional(ZoneFinder& finder,
         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
                   finder.getOrigin().toText());
     }
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(
-                           ns_context->rrset), dnssec_);
-    getAdditional(qname_, qtype_, *ns_context, additionals);
+    authorities_.push_back(ns_context->rrset);
+    getAdditional(*qname_, *qtype_, *ns_context, additionals);
 }
 
 namespace {
@@ -377,10 +352,20 @@ findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
 }
 
 void
-Query::process() {
+Query::process(datasrc::DataSourceClient& datasrc_client,
+               const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+               isc::dns::Message& response, bool dnssec)
+{
+    // Set up the cleaner object so internal pointers and vectors are
+    // always reset after scope leaves this method
+    QueryCleaner cleaner(*this);
+
+    // Set up query parameters for the rest of the (internal) methods
+    initialize(datasrc_client, qname, qtype, response, dnssec);
+
     // Found a zone which is the nearest ancestor to QNAME
-    const DataSourceClient::FindResult result = findZone(datasrc_client_,
-                                                         qname_, qtype_);
+    const DataSourceClient::FindResult result = findZone(*datasrc_client_,
+                                                         *qname_, *qtype_);
 
     // If we have no matching authoritative zone for the query name, return
     // REFUSED.  In short, this is to be compatible with BIND 9, but the
@@ -392,38 +377,34 @@ Query::process() {
         // If we tried to find a "parent zone" for a DS query and failed,
         // we may still have authority at the child side.  If we do, the query
         // has to be handled there.
-        if (qtype_ == RRType::DS() && qname_.getLabelCount() > 1 &&
+        if (*qtype_ == RRType::DS() && qname_->getLabelCount() > 1 &&
             processDSAtChild()) {
             return;
         }
-        response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-        response_.setRcode(Rcode::REFUSED());
+        response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
+        response_->setRcode(Rcode::REFUSED());
         return;
     }
     ZoneFinder& zfinder = *result.zone_finder;
 
     // We have authority for a zone that contain the query name (possibly
     // indirectly via delegation).  Look into the zone.
-    response_.setHeaderFlag(Message::HEADERFLAG_AA);
-    response_.setRcode(Rcode::NOERROR());
-    vector<ConstRRsetPtr> target;
-    vector<ConstRRsetPtr> additionals;
+    response_->setHeaderFlag(Message::HEADERFLAG_AA);
+    response_->setRcode(Rcode::NOERROR());
     boost::function0<ZoneFinderContextPtr> find;
-    const bool qtype_is_any = (qtype_ == RRType::ANY());
+    const bool qtype_is_any = (*qtype_ == RRType::ANY());
     if (qtype_is_any) {
-        find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
-                           boost::ref(target), dnssec_opt_);
+        find = boost::bind(&ZoneFinder::findAll, &zfinder, *qname_,
+                           boost::ref(answers_), dnssec_opt_);
     } else {
-        find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
+        find = boost::bind(&ZoneFinder::find, &zfinder, *qname_, *qtype_,
                            dnssec_opt_);
     }
     ZoneFinderContextPtr db_context(find());
     switch (db_context->code) {
         case ZoneFinder::DNAME: {
             // First, put the dname into the answer
-            response_.addRRset(Message::SECTION_ANSWER,
-                boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
-                dnssec_);
+            answers_.push_back(db_context->rrset);
             /*
              * Empty DNAME should never get in, as it is impossible to
              * create one in master file.
@@ -436,7 +417,7 @@ Query::process() {
                 dynamic_cast<const rdata::generic::DNAME&>(
                 db_context->rrset->getRdataIterator()->getCurrent()));
             // The yet unmatched prefix dname
-            const Name prefix(qname_.split(0, qname_.getLabelCount() -
+            const Name prefix(qname_->split(0, qname_->getLabelCount() -
                 db_context->rrset->getName().getLabelCount()));
             // If we put it together, will it be too long?
             // (The prefix contains trailing ., which will be removed
@@ -446,20 +427,20 @@ Query::process() {
                  * In case the synthesized name is too long, section 4.1
                  * of RFC 2672 mandates we return YXDOMAIN.
                  */
-                response_.setRcode(Rcode::YXDOMAIN());
-                return;
+                response_->setRcode(Rcode::YXDOMAIN());
+                break;
             }
             // 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_context->rrset->getClass(),
+            RRsetPtr cname(new RRset(*qname_, db_context->rrset->getClass(),
                 RRType::CNAME(), db_context->rrset->getTTL()));
             // Construct the new target by replacing the end
-            cname->addRdata(rdata::generic::CNAME(qname_.split(0,
-                qname_.getLabelCount() -
+            cname->addRdata(rdata::generic::CNAME(qname_->split(0,
+                qname_->getLabelCount() -
                 db_context->rrset->getName().getLabelCount()).
                 concatenate(dname.getDname())));
-            response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
+            answers_.push_back(cname);
             break;
         }
         case ZoneFinder::CNAME:
@@ -472,9 +453,7 @@ Query::process() {
              *
              * So, just put it there.
              */
-            response_.addRRset(Message::SECTION_ANSWER,
-                boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
-                dnssec_);
+            answers_.push_back(db_context->rrset);
 
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
@@ -483,21 +462,13 @@ Query::process() {
             }
             break;
         case ZoneFinder::SUCCESS:
-            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<AbstractRRset>(rrset), dnssec_);
-                }
-            } else {
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
-                    dnssec_);
+            // If query type is ANY, the rrs have already been added
+            if (!qtype_is_any) {
+                answers_.push_back(db_context->rrset);
             }
 
             // Retrieve additional records for the answer
-            getAdditional(qname_, qtype_, *db_context, additionals);
+            getAdditional(*qname_, *qtype_, *db_context, additionals_);
 
             // If apex NS records haven't been provided in the answer
             // section, insert apex NS records into the authority section
@@ -507,9 +478,9 @@ Query::process() {
             // qname is the zone origin.
             if (result.code != result::SUCCESS ||
                 db_context->code != ZoneFinder::SUCCESS ||
-                (qtype_ != RRType::NS() && !qtype_is_any))
+                (*qtype_ != RRType::NS() && !qtype_is_any))
             {
-                addAuthAdditional(*result.zone_finder, additionals);
+                addAuthAdditional(*result.zone_finder, additionals_);
             }
 
             // If the answer is a result of wildcard substitution,
@@ -523,16 +494,14 @@ Query::process() {
             // if we are an authority of the child, too.  If so, we need to
             // complete the process in the child as specified in Section
             // 2.2.1.2. of RFC3658.
-            if (qtype_ == RRType::DS() && processDSAtChild()) {
+            if (*qtype_ == RRType::DS() && processDSAtChild()) {
                 return;
             }
 
-            response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-            response_.addRRset(Message::SECTION_AUTHORITY,
-                boost::const_pointer_cast<AbstractRRset>(db_context->rrset),
-                dnssec_);
+            response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
+            authorities_.push_back(db_context->rrset);
             // Retrieve additional records for the name servers
-            db_context->getAdditional(A_AND_AAAA(), additionals);
+            db_context->getAdditional(A_AND_AAAA(), additionals_);
 
             // If DNSSEC is requested, see whether there is a DS
             // record for this delegation.
@@ -541,7 +510,7 @@ Query::process() {
             }
             break;
         case ZoneFinder::NXDOMAIN:
-            response_.setRcode(Rcode::NXDOMAIN());
+            response_->setRcode(Rcode::NXDOMAIN());
             addSOA(*result.zone_finder);
             if (dnssec_) {
                 if (db_context->isNSECSigned() && db_context->rrset) {
@@ -565,15 +534,48 @@ Query::process() {
             break;
     }
 
-    for_each(additionals.begin(), additionals.end(),
-             RRsetInserter(response_, Message::SECTION_ADDITIONAL,
-                           dnssec_));
+    createResponse();
+}
+
+void
+Query::initialize(datasrc::DataSourceClient& datasrc_client,
+                  const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+                  isc::dns::Message& response, bool dnssec)
+{
+    datasrc_client_ = &datasrc_client;
+    qname_ = &qname;
+    qtype_ = &qtype;
+    response_ = &response;
+    dnssec_ = dnssec;
+    dnssec_opt_ = (dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC :
+                   isc::datasrc::ZoneFinder::FIND_DEFAULT);
+}
+
+void
+Query::createResponse() {
+    for_each(answers_.begin(), answers_.end(),
+             RRsetInserter(*response_, Message::SECTION_ANSWER, dnssec_));
+    for_each(authorities_.begin(), authorities_.end(),
+             RRsetInserter(*response_, Message::SECTION_AUTHORITY, dnssec_));
+    for_each(additionals_.begin(), additionals_.end(),
+             RRsetInserter(*response_, Message::SECTION_ADDITIONAL, dnssec_));
+}
+
+void
+Query::reset() {
+    datasrc_client_ = NULL;
+    qname_ = NULL;
+    qtype_ = NULL;
+    response_ = NULL;
+    answers_.clear();
+    authorities_.clear();
+    additionals_.clear();
 }
 
 bool
 Query::processDSAtChild() {
     const DataSourceClient::FindResult zresult =
-        datasrc_client_.findZone(qname_);
+        datasrc_client_->findZone(*qname_);
 
     if (zresult.code != result::SUCCESS) {
         return (false);
@@ -588,17 +590,18 @@ Query::processDSAtChild() {
     // The important point in this case is to return SOA so that the resolver
     // that happens to contact us can hunt for the appropriate parent zone
     // by seeing the SOA.
-    response_.setHeaderFlag(Message::HEADERFLAG_AA);
-    response_.setRcode(Rcode::NOERROR());
+    response_->setHeaderFlag(Message::HEADERFLAG_AA);
+    response_->setRcode(Rcode::NOERROR());
     addSOA(*zresult.zone_finder);
     ConstZoneFinderContextPtr ds_context =
-        zresult.zone_finder->find(qname_, RRType::DS(), dnssec_opt_);
+        zresult.zone_finder->find(*qname_, RRType::DS(), dnssec_opt_);
     if (ds_context->code == ZoneFinder::NXRRSET) {
         if (dnssec_) {
             addNXRRsetProof(*zresult.zone_finder, *ds_context);
         }
     }
 
+    createResponse();
     return (true);
 }
 

+ 84 - 19
src/bin/auth/query.h

@@ -18,6 +18,8 @@
 #include <dns/rrset.h>
 #include <datasrc/zone.h>
 
+#include <boost/noncopyable.hpp>
+
 #include <vector>
 
 namespace isc {
@@ -34,6 +36,13 @@ class DataSourceClient;
 
 namespace auth {
 
+/// \brief Initial reserved size for the vectors in Query
+///
+/// The value is larger than we expect the vectors to even become, and
+/// has been chosen arbitrarily. The reason to set them quite high is
+/// to prevent reallocation on addition.
+const size_t RESERVE_RRSETS = 64;
+
 /// The \c Query class represents a standard DNS query that encapsulates
 /// processing logic to answer the query.
 ///
@@ -64,7 +73,7 @@ namespace auth {
 /// likely to misuse one of the classes instead of the other
 /// accidentally, and since it's considered a temporary development state,
 /// we keep this name at the moment.
-class Query {
+class Query : boost::noncopyable {
 private:
 
     /// \brief Adds a SOA.
@@ -226,10 +235,10 @@ private:
     void addNSEC3ForName(isc::datasrc::ZoneFinder& finder,
                          const isc::dns::Name& name, bool match);
 
-public:
-    /// Constructor from query parameters.
+    /// Set up the Query object for a new query lookup
     ///
-    /// This constructor never throws an exception.
+    /// This is the first step of the process() method, and initializes
+    /// the member data
     ///
     /// \param datasrc_client The datasource wherein the answer to the query is
     /// to be found.
@@ -238,14 +247,56 @@ public:
     /// \param response The response message to store the answer to the query.
     /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
     ///     possible.
-    Query(const isc::datasrc::DataSourceClient& datasrc_client,
-          const isc::dns::Name& qname, const isc::dns::RRType& qtype,
-          isc::dns::Message& response, bool dnssec = false) :
-        datasrc_client_(datasrc_client), qname_(qname), qtype_(qtype),
-        response_(response), dnssec_(dnssec),
-        dnssec_opt_(dnssec ?  isc::datasrc::ZoneFinder::FIND_DNSSEC :
-                    isc::datasrc::ZoneFinder::FIND_DEFAULT)
-    {}
+    void initialize(datasrc::DataSourceClient& datasrc_client,
+                    const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+                    isc::dns::Message& response, bool dnssec = false);
+
+    /// \brief Fill in the response sections
+    ///
+    /// This is the final step of the process() method, and within
+    /// that method, it should be called before it returns (if any
+    /// response data is to be added)
+    ///
+    /// This will take each RRset collected in answers_, authorities_, and
+    /// additionals_, and add them to their corresponding sections in
+    /// the response message.
+    ///
+    /// After they are added, the vectors are cleared.
+    void createResponse();
+
+    /// \brief Resets any partly built response data, and internal pointers
+    ///
+    /// Called by the QueryCleaner object upon its destruction
+    void reset();
+
+    /// \brief Internal class used for cleanup of Query members
+    ///
+    /// The process() call creates an object of this class, which
+    /// upon its destruction, calls Query::reset(), so that outside
+    /// of single calls to process(), the query state is always clean.
+    class QueryCleaner {
+    public:
+        QueryCleaner(isc::auth::Query& query) : query_(query) {}
+        ~QueryCleaner() { query_.reset(); }
+    private:
+        isc::auth::Query& query_;
+    };
+
+public:
+    /// Default constructor.
+    ///
+    /// Query parameters will be set by the call to process()
+    ///
+    Query() :
+        datasrc_client_(NULL), qname_(NULL), qtype_(NULL),
+        response_(NULL), dnssec_(false),
+        dnssec_opt_(isc::datasrc::ZoneFinder::FIND_DEFAULT)
+    {
+        answers_.reserve(RESERVE_RRSETS);
+        authorities_.reserve(RESERVE_RRSETS);
+        additionals_.reserve(RESERVE_RRSETS);
+    }
+
 
     /// Process the query.
     ///
@@ -273,7 +324,17 @@ public:
     /// This might throw BadZone or any of its specific subclasses, but that
     /// shouldn't happen in real-life (as BadZone means wrong data, it should
     /// have been rejected upon loading).
-    void process();
+    ///
+    /// \param datasrc_client The datasource wherein the answer to the query is
+    /// to be found.
+    /// \param qname The query name
+    /// \param qtype The RR type of the query
+    /// \param response The response message to store the answer to the query.
+    /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
+    ///     possible.
+    void process(datasrc::DataSourceClient& datasrc_client,
+                 const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+                 isc::dns::Message& response, bool dnssec = false);
 
     /// \short Bad zone data encountered.
     ///
@@ -342,12 +403,16 @@ public:
     };
 
 private:
-    const isc::datasrc::DataSourceClient& datasrc_client_;
-    const isc::dns::Name& qname_;
-    const isc::dns::RRType& qtype_;
-    isc::dns::Message& response_;
-    const bool dnssec_;
-    const isc::datasrc::ZoneFinder::FindOptions dnssec_opt_;
+    const isc::datasrc::DataSourceClient* datasrc_client_;
+    const isc::dns::Name* qname_;
+    const isc::dns::RRType* qtype_;
+    isc::dns::Message* response_;
+    bool dnssec_;
+    isc::datasrc::ZoneFinder::FindOptions dnssec_opt_;
+
+    std::vector<isc::dns::ConstRRsetPtr> answers_;
+    std::vector<isc::dns::ConstRRsetPtr> authorities_;
+    std::vector<isc::dns::ConstRRsetPtr> additionals_;
 };
 
 }

+ 193 - 167
src/bin/auth/tests/query_unittest.cc

@@ -903,6 +903,7 @@ protected:
     const qid_t qid;
     const uint16_t query_code;
     const string ns_addrs_and_sig_txt; // convenient shortcut
+    Query query;
 };
 
 // A wrapper to check resulting response message commonly used in
@@ -946,25 +947,40 @@ TEST_F(QueryTest, noZone) {
     // There's no zone in the memory datasource.  So the response should have
     // REFUSED.
     InMemoryClient empty_memory_client;
-    Query nozone_query(empty_memory_client, qname, qtype, response);
-    EXPECT_NO_THROW(nozone_query.process());
+    EXPECT_NO_THROW(query.process(empty_memory_client, qname, qtype,
+                                  response));
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
 TEST_F(QueryTest, exactMatch) {
-    Query query(memory_client, qname, qtype, response);
-    EXPECT_NO_THROW(query.process());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
 }
 
+TEST_F(QueryTest, exactMatchMultipleQueries) {
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    // find match rrset
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+                  www_a_txt, zone_ns_txt, ns_addrs_txt);
+
+    // clean up response for second query
+    response.clear(isc::dns::Message::RENDER);
+    response.setRcode(Rcode::NOERROR());
+    response.setOpcode(Opcode::QUERY());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    // find match rrset
+    SCOPED_TRACE("Second query");
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+                  www_a_txt, zone_ns_txt, ns_addrs_txt);
+}
+
 TEST_F(QueryTest, exactMatchIgnoreSIG) {
     // Check that we do not include the RRSIG when not requested even when
     // we receive it from the data source.
     mock_finder->setIncludeRRSIGAnyway(true);
-    Query query(memory_client, qname, qtype, response);
-    EXPECT_NO_THROW(query.process());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -972,8 +988,8 @@ TEST_F(QueryTest, exactMatchIgnoreSIG) {
 
 TEST_F(QueryTest, dnssecPositive) {
     // Just like exactMatch, but the signatures should be included as well
-    Query query(memory_client, qname, qtype, response, true);
-    EXPECT_NO_THROW(query.process());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response,
+                                  true));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
                   (www_a_txt + std::string("www.example.com. 3600 IN RRSIG "
@@ -991,8 +1007,9 @@ TEST_F(QueryTest, dnssecPositive) {
 TEST_F(QueryTest, exactAddrMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
-                          response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("noglue.example.com"),
+                                  qtype, response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
                   "noglue.example.com. 3600 IN A 192.0.2.53\n", zone_ns_txt,
@@ -1003,8 +1020,8 @@ TEST_F(QueryTest, exactAddrMatch) {
 TEST_F(QueryTest, apexNSMatch) {
     // find match rrset, omit authority data which has already been provided
     // in the answer section from the authority section.
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"), RRType::NS(),
-                          response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::NS(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
                   zone_ns_txt, NULL, ns_addrs_txt);
@@ -1014,8 +1031,8 @@ TEST_F(QueryTest, apexNSMatch) {
 TEST_F(QueryTest, exactAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("noglue.example.com"),
+                                  RRType::ANY(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 3, 2,
                   (string("noglue.example.com. 3600 IN A 192.0.2.53\n") +
@@ -1028,8 +1045,8 @@ TEST_F(QueryTest, exactAnyMatch) {
 TEST_F(QueryTest, apexAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 5, 0, 3,
                   (string(soa_txt) + string(zone_ns_txt) +
                    string(nsec_apex_txt)).c_str(),
@@ -1037,23 +1054,23 @@ TEST_F(QueryTest, apexAnyMatch) {
 }
 
 TEST_F(QueryTest, mxANYMatch) {
-    EXPECT_NO_THROW(Query(memory_client, Name("mx.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("mx.example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 3, 4,
                   (string(mx_txt) + string(nsec_mx_txt)).c_str(), zone_ns_txt,
                   (string(ns_addrs_txt) + string(www_a_txt)).c_str());
 }
 
 TEST_F(QueryTest, glueANYMatch) {
-    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("delegation.example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                   NULL, delegation_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, nodomainANY) {
-    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
@@ -1065,23 +1082,24 @@ TEST_F(QueryTest, noApexNS) {
     // Disable apex NS record
     mock_finder->setApexNSFlag(false);
 
-    EXPECT_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
-                       response).process(), Query::NoApexNS);
+    EXPECT_THROW(query.process(memory_client, Name("noglue.example.com"), qtype,
+                               response), Query::NoApexNS);
     // We don't look into the response, as it threw
 }
 
 TEST_F(QueryTest, delegation) {
-    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
-                          qtype, response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("delegation.example.com"),
+                                  qtype, response));
 
     responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                   NULL, delegation_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, secureDelegation) {
-    EXPECT_NO_THROW(Query(memory_client,
-                          Name("foo.signed-delegation.example.com"),
-                          qtype, response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("foo.signed-delegation.example.com"),
+                                  qtype, response, true));
 
     // Should now contain RRSIG and DS record as well.
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
@@ -1094,9 +1112,9 @@ TEST_F(QueryTest, secureDelegation) {
 }
 
 TEST_F(QueryTest, secureUnsignedDelegation) {
-    EXPECT_NO_THROW(Query(memory_client,
-                          Name("foo.unsigned-delegation.example.com"),
-                          qtype, response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("foo.unsigned-delegation.example.com"),
+                                  qtype, response, true));
 
     // Should now contain RRSIG and NSEC record as well.
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
@@ -1115,8 +1133,9 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3) {
     mock_finder->setNSEC3Flag(true);
     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
 
-    Query(memory_client, Name("foo.unsigned-delegation.example.com"),
-          qtype, response, true).process();
+    query.process(memory_client,
+                  Name("foo.unsigned-delegation.example.com"),
+                  qtype, response, true);
 
     // The response should contain the NS and matching NSEC3 with its RRSIG
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
@@ -1133,8 +1152,9 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
     // Similar to the previous case, but the delegation is an optout.
     mock_finder->setNSEC3Flag(true);
 
-    Query(memory_client, Name("foo.unsigned-delegation.example.com"),
-          qtype, response, true).process();
+    query.process(memory_client,
+                  Name("foo.unsigned-delegation.example.com"),
+                  qtype, response, true);
 
     // The response should contain the NS and the closest provable encloser
     // proof (and their RRSIGs).  The closest encloser is the apex (origin),
@@ -1157,19 +1177,22 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
 TEST_F(QueryTest, badSecureDelegation) {
     // Test whether exception is raised if DS query at delegation results in
     // something different than SUCCESS or NXRRSET
-    EXPECT_THROW(Query(memory_client, Name("bad-delegation.example.com"),
-                       qtype, response, true).process(), Query::BadDS);
+    EXPECT_THROW(query.process(memory_client,
+                               Name("bad-delegation.example.com"),
+                               qtype, response, true), Query::BadDS);
 
     // But only if DNSSEC is requested (it shouldn't even try to look for
     // the DS otherwise)
-    EXPECT_NO_THROW(Query(memory_client, Name("bad-delegation.example.com"),
-                          qtype, response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("bad-delegation.example.com"),
+                                  qtype, response));
 }
 
 
 TEST_F(QueryTest, nxdomain) {
-    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                          response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("nxdomain.example.com"), qtype,
+                                  response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
@@ -1178,8 +1201,9 @@ TEST_F(QueryTest, nxdomainWithNSEC) {
     // NXDOMAIN with DNSSEC proof.  We should have SOA, NSEC that proves
     // NXDOMAIN and NSEC that proves nonexistence of matching wildcard,
     // as well as their RRSIGs.
-    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                          response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("nxdomain.example.com"), qtype,
+                                  response, true));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1198,8 +1222,8 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
     // is derived from the next domain of the NSEC that proves NXDOMAIN, and
     // the NSEC to provide the non existence of wildcard is different from
     // the first NSEC.
-    Query(memory_client, Name("(.no.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("(.no.example.com"), qtype, response,
+                  true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1216,8 +1240,8 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
 TEST_F(QueryTest, nxdomainWithNSECDuplicate) {
     // See comments about nz_txt.  In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence of wildcard.
-    Query(memory_client, Name("nx.no.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("nx.no.example.com"), qtype, response,
+                  true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 4, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1233,8 +1257,8 @@ TEST_F(QueryTest, nxdomainBadNSEC1) {
     mock_finder->setNSECResult(Name("badnsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("badnsec.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("badnsec.example.com"),
+                               qtype, response, true),
                  std::bad_cast);
 }
 
@@ -1243,8 +1267,8 @@ TEST_F(QueryTest, nxdomainBadNSEC2) {
     mock_finder->setNSECResult(Name("emptynsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("emptynsec.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("emptynsec.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
@@ -1253,8 +1277,8 @@ TEST_F(QueryTest, nxdomainBadNSEC3) {
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
@@ -1262,8 +1286,8 @@ TEST_F(QueryTest, nxdomainBadNSEC4) {
     // "no-wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
@@ -1273,8 +1297,8 @@ TEST_F(QueryTest, nxdomainBadNSEC5) {
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
     // This is a bit odd, but we'll simply include the returned RRset.
-    Query(memory_client, Name("nxdomain.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("nxdomain.example.com"), qtype,
+                  response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1293,14 +1317,14 @@ TEST_F(QueryTest, nxdomainBadNSEC6) {
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
 TEST_F(QueryTest, nxrrset) {
-    EXPECT_NO_THROW(Query(memory_client, Name("www.example.com"),
-                          RRType::TXT(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("www.example.com"),
+                                  RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
@@ -1309,8 +1333,8 @@ TEST_F(QueryTest, nxrrset) {
 TEST_F(QueryTest, nxrrsetWithNSEC) {
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC that proves the
     // NXRRSET and their RRSIGs.
-    Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("www.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1330,8 +1354,8 @@ TEST_F(QueryTest, emptyNameWithNSEC) {
     // exact match), so we only need one NSEC.
     // From the point of the Query::process(), this is actually no different
     // from the other NXRRSET case, but we check that explicitly just in case.
-    Query(memory_client, Name("no.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("no.example.com"), RRType::A(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1346,8 +1370,8 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
     // NXRRSET with DNSSEC proof requested, but there's no NSEC at that node.
     // This is an unexpected event (if the zone is supposed to be properly
     // signed with NSECs), but we accept and ignore the oddity.
-    Query(memory_client, Name("nonsec.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("nonsec.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1358,8 +1382,8 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
 TEST_F(QueryTest, wildcardNSEC) {
     // The qname matches *.wild.example.com.  The response should contain
     // an NSEC that proves the non existence of a closer name.
-    Query(memory_client, Name("www.wild.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("www.wild.example.com"), RRType::A(),
+                  response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
                   (string(wild_txt).replace(0, 1, "www") +
                    string("www.wild.example.com. 3600 IN RRSIG ") +
@@ -1378,8 +1402,8 @@ TEST_F(QueryTest, wildcardNSEC) {
 TEST_F(QueryTest, CNAMEwildNSEC) {
     // Similar to the previous case, but the matching wildcard record is
     // CNAME.
-    Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
-          response, true).process();
+    query.process(memory_client, Name("www.cnamewild.example.com"),
+                  RRType::A(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(cnamewild_txt).replace(0, 1, "www") +
                    string("www.cnamewild.example.com. 3600 IN RRSIG ") +
@@ -1401,8 +1425,8 @@ TEST_F(QueryTest, wildcardNSEC3) {
     // of identifying the next closer name.
     mock_finder->addRecord(nsec3_atwild_txt);
 
-    Query(memory_client, Name("x.y.wild.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("x.y.wild.example.com"), RRType::A(),
+                  response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
                   (string(wild_txt).replace(0, 1, "x.y") +
                    string("x.y.wild.example.com. 3600 IN RRSIG ") +
@@ -1426,8 +1450,8 @@ TEST_F(QueryTest, CNAMEwildNSEC3) {
     mock_finder->setNSEC3Flag(true);
     mock_finder->addRecord(nsec3_atcnamewild_txt);
 
-    Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
-          response, true).process();
+    query.process(memory_client, Name("www.cnamewild.example.com"),
+                  RRType::A(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(cnamewild_txt).replace(0, 1, "www") +
                    string("www.cnamewild.example.com. 3600 IN RRSIG ") +
@@ -1449,8 +1473,8 @@ TEST_F(QueryTest, badWildcardNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC3);
 }
 
@@ -1460,8 +1484,8 @@ TEST_F(QueryTest, badWildcardProof1) {
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
@@ -1469,8 +1493,8 @@ TEST_F(QueryTest, badWildcardProof2) {
     // "wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
@@ -1479,8 +1503,8 @@ TEST_F(QueryTest, badWildcardProof3) {
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
@@ -1488,8 +1512,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
     // NXRRSET on WILDCARD with DNSSEC proof.  We should have SOA, NSEC that
     // proves the NXRRSET and their RRSIGs. In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence RRSETs of wildcard.
-    Query(memory_client, Name("www.wild.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("www.wild.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1505,8 +1529,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
     // proves the NXRRSET and their RRSIGs. In this case we need two NSEC RRs,
     // one proves NXDOMAIN and the other proves non existence RRSETs of
     // wildcard.
-    Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(),
-          response, true).process();
+    query.process(memory_client, Name("www1.uwild.example.com"),
+                  RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1528,8 +1552,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
     mock_finder->addRecord(nsec3_uwild_txt);
     mock_finder->setNSEC3Flag(true);
 
-    Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(),
-          response, true).process();
+    query.process(memory_client, Name("www1.uwild.example.com"),
+                  RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
@@ -1562,8 +1586,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Collision) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(Query(memory_client, Name("www1.uwild.example.com"),
-                       RRType::TXT(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www1.uwild.example.com"),
+                               RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
@@ -1579,8 +1603,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
     mock_finder->addRecord(nsec3_wild_txt);
     mock_finder->addRecord(nsec3_uwild_txt);
 
-    EXPECT_THROW(Query(memory_client, Name("www1.uwild.example.com"),
-                       RRType::TXT(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www1.uwild.example.com"),
+                               RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
@@ -1588,8 +1612,8 @@ TEST_F(QueryTest, wildcardEmptyWithNSEC) {
     // Empty WILDCARD with DNSSEC proof.  We should have SOA, NSEC that proves
     // the NXDOMAIN and their RRSIGs. In this case we need two NSEC RRs,
     // one proves NXDOMAIN and the other proves non existence wildcard.
-    Query(memory_client, Name("a.t.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("a.t.example.com"), RRType::A(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1612,19 +1636,19 @@ TEST_F(QueryTest, noSOA) {
     mock_finder->setSOAFlag(false);
 
     // The NX Domain
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"),
-                       qtype, response).process(), Query::NoSOA);
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response), Query::NoSOA);
     // Of course, we don't look into the response, as it throwed
 
     // NXRRSET
-    EXPECT_THROW(Query(memory_client, Name("nxrrset.example.com"),
-                       qtype, response).process(), Query::NoSOA);
+    EXPECT_THROW(query.process(memory_client, Name("nxrrset.example.com"),
+                               qtype, response), Query::NoSOA);
 }
 
 TEST_F(QueryTest, noMatchZone) {
     // there's a zone in the memory datasource but it doesn't match the qname.
     // should result in REFUSED.
-    Query(memory_client, Name("example.org"), qtype, response).process();
+    query.process(memory_client, Name("example.org"), qtype, response);
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
@@ -1635,8 +1659,8 @@ TEST_F(QueryTest, noMatchZone) {
  * A record, other to unknown out of zone one.
  */
 TEST_F(QueryTest, MX) {
-    Query(memory_client, Name("mx.example.com"), RRType::MX(),
-          response).process();
+    query.process(memory_client, Name("mx.example.com"), RRType::MX(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
                   mx_txt, NULL,
@@ -1649,8 +1673,8 @@ TEST_F(QueryTest, MX) {
  * This should not trigger the additional processing for the exchange.
  */
 TEST_F(QueryTest, MXAlias) {
-    Query(memory_client, Name("cnamemx.example.com"), RRType::MX(),
-          response).process();
+    query.process(memory_client, Name("cnamemx.example.com"), RRType::MX(),
+                  response);
 
     // there shouldn't be no additional RRs for the exchanges (we have 3
     // RRs for the NS).  The normal MX case is tested separately so we don't
@@ -1669,8 +1693,8 @@ TEST_F(QueryTest, MXAlias) {
  * returned.
  */
 TEST_F(QueryTest, CNAME) {
-    Query(memory_client, Name("cname.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_txt, NULL, NULL);
@@ -1679,8 +1703,8 @@ TEST_F(QueryTest, CNAME) {
 TEST_F(QueryTest, explicitCNAME) {
     // same owner name as the CNAME test but explicitly query for CNAME RR.
     // expect the same response as we don't provide a full chain yet.
-    Query(memory_client, Name("cname.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::CNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_txt, zone_ns_txt, ns_addrs_txt);
@@ -1691,8 +1715,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
     // note: with chaining, what should be expected is not trivial:
     // BIND 9 returns the CNAME in answer and SOA in authority, no additional.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional.
-    Query(memory_client, Name("cname.example.com"), RRType::TXT(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::TXT(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_txt, NULL, NULL);
@@ -1700,8 +1724,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
 
 TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
     // same owner name as the NXRRSET test but explicitly query for CNAME RR.
-    Query(memory_client, Name("cname.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::CNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_txt, zone_ns_txt, ns_addrs_txt);
@@ -1714,8 +1738,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
     // RCODE being NXDOMAIN.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional,
     // RCODE being NOERROR.
-    Query(memory_client, Name("cnamenxdom.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("cnamenxdom.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_nxdom_txt, NULL, NULL);
@@ -1723,8 +1747,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
 
 TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
     // same owner name as the NXDOMAIN test but explicitly query for CNAME RR.
-    Query(memory_client, Name("cnamenxdom.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cnamenxdom.example.com"),
+                  RRType::CNAME(), response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
@@ -1739,8 +1763,8 @@ TEST_F(QueryTest, CNAME_OUT) {
      * Then the same test should be done with .org included there and
      * see what it does (depends on what we want to do)
      */
-    Query(memory_client, Name("cnameout.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("cnameout.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_out_txt, NULL, NULL);
@@ -1748,8 +1772,8 @@ TEST_F(QueryTest, CNAME_OUT) {
 
 TEST_F(QueryTest, explicitCNAME_OUT) {
     // same owner name as the OUT test but explicitly query for CNAME RR.
-    Query(memory_client, Name("cnameout.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cnameout.example.com"), RRType::CNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_out_txt, zone_ns_txt, ns_addrs_txt);
@@ -1764,8 +1788,8 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
  * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
  */
 TEST_F(QueryTest, DNAME) {
-    Query(memory_client, Name("www.dname.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("www.dname.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
         (string(dname_txt) + synthetized_cname_txt).c_str(),
@@ -1780,8 +1804,8 @@ TEST_F(QueryTest, DNAME) {
  * DNAME.
  */
 TEST_F(QueryTest, DNAME_ANY) {
-    Query(memory_client, Name("www.dname.example.com"), RRType::ANY(),
-        response).process();
+    query.process(memory_client, Name("www.dname.example.com"), RRType::ANY(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
         (string(dname_txt) + synthetized_cname_txt).c_str(), NULL, NULL);
@@ -1789,8 +1813,8 @@ TEST_F(QueryTest, DNAME_ANY) {
 
 // Test when we ask for DNAME explicitly, it does no synthetizing.
 TEST_F(QueryTest, explicitDNAME) {
-    Query(memory_client, Name("dname.example.com"), RRType::DNAME(),
-        response).process();
+    query.process(memory_client, Name("dname.example.com"), RRType::DNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         dname_txt, zone_ns_txt, ns_addrs_txt);
@@ -1801,8 +1825,8 @@ TEST_F(QueryTest, explicitDNAME) {
  * the CNAME, it should return the RRset.
  */
 TEST_F(QueryTest, DNAME_A) {
-    Query(memory_client, Name("dname.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("dname.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         dname_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -1813,8 +1837,8 @@ TEST_F(QueryTest, DNAME_A) {
  * It should not synthetize the CNAME.
  */
 TEST_F(QueryTest, DNAME_NX_RRSET) {
-    EXPECT_NO_THROW(Query(memory_client, Name("dname.example.com"),
-        RRType::TXT(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("dname.example.com"),
+                    RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
         NULL, soa_txt, NULL, mock_finder->getOrigin());
@@ -1833,8 +1857,8 @@ TEST_F(QueryTest, LongDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(Query(memory_client, longname, RRType::A(),
-        response).process());
+    EXPECT_NO_THROW(query.process(memory_client, longname, RRType::A(),
+                    response));
 
     responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
         dname_txt, NULL, NULL);
@@ -1852,8 +1876,8 @@ TEST_F(QueryTest, MaxLenDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(Query(memory_client, longname, RRType::A(),
-        response).process());
+    EXPECT_NO_THROW(query.process(memory_client, longname, RRType::A(),
+                    response));
 
     // Check the answer is OK
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
@@ -2038,8 +2062,9 @@ TEST_F(QueryTest, dsAboveDelegation) {
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("delegation.example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
                   (string(delegation_ds_txt) + "\n" +
@@ -2061,9 +2086,9 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(Query(memory_client,
-                          Name("unsigned-delegation.example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("unsigned-delegation.example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) +
@@ -2079,8 +2104,8 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 // when it happens to be sent to the child zone, as described in RFC 4035,
 // section 3.1.4.1. The example is inspired by the B.8. example from the RFC.
 TEST_F(QueryTest, dsBelowDelegation) {
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2096,8 +2121,8 @@ TEST_F(QueryTest, dsBelowDelegation) {
 // In our implementation NSEC/NSEC3 isn't attached in this case.
 TEST_F(QueryTest, dsBelowDelegationWithDS) {
     mock_finder->addRecord(zone_ds_txt); // add the DS to the child's apex
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2109,16 +2134,16 @@ TEST_F(QueryTest, dsBelowDelegationWithDS) {
 // server.  It should just like the "noZone" test case, but DS query involves
 // special processing, so we test it explicitly.
 TEST_F(QueryTest, dsNoZone) {
-    Query(memory_client, Name("example"), RRType::DS(), response,
-          true).process();
+    query.process(memory_client, Name("example"), RRType::DS(), response,
+                  true);
     responseCheck(response, Rcode::REFUSED(), 0, 0, 0, 0, NULL, NULL, NULL);
 }
 
 // DS query for a "grandchild" zone.  This should result in normal
 // delegation (unless this server also has authority of the grandchild zone).
 TEST_F(QueryTest, dsAtGrandParent) {
-    Query(memory_client, Name("grand.delegation.example.com"), RRType::DS(),
-          response, true).process();
+    query.process(memory_client, Name("grand.delegation.example.com"),
+                  RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), 0, 0, 6, 6, NULL,
                   (string(delegation_txt) + string(delegation_ds_txt) +
                    "delegation.example.com. 3600 IN RRSIG " +
@@ -2136,7 +2161,7 @@ TEST_F(QueryTest, dsAtGrandParentAndChild) {
     const Name childname("grand.delegation.example.com");
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(childname)));
-    Query(memory_client, childname, RRType::DS(), response, true).process();
+    query.process(memory_client, childname, RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" +
                    childname.toText() + " 3600 IN RRSIG " +
@@ -2154,8 +2179,8 @@ TEST_F(QueryTest, dsAtRoot) {
     // Pretend to be a root server.
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME())));
-    Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
-          true).process();
+    query.process(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+                  true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(". 3600 IN SOA . . 0 0 0 0 0\n") +
                    ". 3600 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" +
@@ -2171,8 +2196,8 @@ TEST_F(QueryTest, dsAtRootWithDS) {
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME(),
                                                       true)));
-    Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
-          true).process();
+    query.process(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+                  true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD"
                           "3CD34AC1AFE51DE\n") +
@@ -2188,8 +2213,8 @@ TEST_F(QueryTest, nxrrsetWithNSEC3) {
 
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC3 that proves the
     // NXRRSET and their RRSIGs.
-    Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("www.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2211,8 +2236,9 @@ TEST_F(QueryTest, nxrrsetMissingNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(Query(memory_client, Name("www.example.com"), RRType::TXT(),
-                       response, true).process(), Query::BadNSEC3);
+    EXPECT_THROW(query.process(memory_client, Name("www.example.com"),
+                               RRType::TXT(), response, true),
+                 Query::BadNSEC3);
 }
 
 TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
@@ -2221,8 +2247,8 @@ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
 
     // This delegation has no DS, but does have a matching NSEC3 record
     // (See RFC5155 section 7.2.4)
-    Query(memory_client, Name("unsigned-delegation.example.com."),
-          RRType::DS(), response, true).process();
+    query.process(memory_client, Name("unsigned-delegation.example.com."),
+                  RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
@@ -2243,8 +2269,8 @@ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
     // 'next closer' should have opt-out set, though that is not
     // actually checked)
     // (See RFC5155 section 7.2.4)
-    Query(memory_client, Name("unsigned-delegation-optout.example.com."),
-          RRType::DS(), response, true).process();
+    query.process(memory_client, Name("unsigned-delegation-optout.example.com."),
+                  RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
@@ -2270,8 +2296,8 @@ TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
     // This will be the covering NSEC3 for the possible wildcard
     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
 
-    Query(memory_client, Name("nxdomain.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("nxdomain.example.com"), qtype,
+                  response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
                   (string(soa_txt) +
@@ -2305,8 +2331,8 @@ TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"),
-                       RRType::TXT(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
@@ -2324,8 +2350,8 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3, &wname);
 
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"), qtype,
+                               response, true),
                  Query::BadNSEC3);
 }
 

+ 1 - 0
tests/lettuce/configurations/example.org.inmem.config

@@ -0,0 +1 @@
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "auth", "debuglevel": 99}]}, "Auth": {"database_file": "", "listen_on": [{"port": 47806, "address": "127.0.0.1"}], "datasources": [{"zones": [{"origin": "example.org", "file": "data/example.org"}], "type": "memory"}]}}

+ 12 - 0
tests/lettuce/data/example.org

@@ -0,0 +1,12 @@
+example.org.	3600	IN	SOA	ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+example.org.	3600	IN	NS	ns1.example.org.
+example.org.	3600	IN	NS	ns2.example.org.
+example.org.	3600	IN	MX	10 mail.example.org.
+www.example.org.	3600	IN	A	192.0.2.1
+mail.example.org.	3600	IN	A	192.0.2.10
+sub.example.org.	3600	IN	NS	ns.sub.example.org.
+ns.sub.example.org.	3600	IN	A	192.0.2.101
+dname.example.org.	3600	IN	DNAME	dname.example.info.
+dname2.foo.example.org.	3600	IN	DNAME	dname2.example.info.
+ns1.example.org.	3600	IN	A	192.0.2.3
+ns2.example.org.	3600	IN	A	192.0.2.4

+ 81 - 0
tests/lettuce/features/queries.feature

@@ -0,0 +1,81 @@
+Feature: Querying feature
+    This feature is a collection of non-specific querying tests;
+    for instance whether multiple queries in a row return consistent
+    answers.
+
+    Scenario: Repeated queries
+        Given I have bind10 running with configuration example.org.inmem.config
+        A query for www.example.org should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 1
+        The last query response should have nscount 2
+        The last query response should have adcount 2
+
+        The answer section of the last query response should be
+        """
+        www.example.org.	3600	IN	A	192.0.2.1
+        """
+        The authority section of the last query response should be
+        """
+        example.org.		3600	IN	NS	ns1.example.org.
+        example.org.		3600	IN	NS	ns2.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns1.example.org.	3600	IN	A	192.0.2.3
+        ns2.example.org.	3600	IN	A	192.0.2.4
+        """
+
+        # Repeat of the above
+        A query for www.example.org should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 1
+        The last query response should have nscount 2
+        The last query response should have adcount 2
+
+        The answer section of the last query response should be
+        """
+        www.example.org.	3600	IN	A	192.0.2.1
+        """
+        The authority section of the last query response should be
+        """
+        example.org.		3600	IN	NS	ns1.example.org.
+        example.org.		3600	IN	NS	ns2.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns1.example.org.	3600	IN	A	192.0.2.3
+        ns2.example.org.	3600	IN	A	192.0.2.4
+        """
+
+        # And now query something completely different
+        A query for nosuchname.example.org should have rcode NXDOMAIN
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 0
+        The last query response should have nscount 1
+        The last query response should have adcount 0
+        The authority section of the last query response should be
+        """
+        example.org.		3600	IN	SOA	ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+        """
+
+    Scenario: ANY query
+        Given I have bind10 running with configuration example.org.inmem.config
+        A query for example.org type ANY should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 4
+        The last query response should have nscount 0
+        The last query response should have adcount 3
+        The answer section of the last query response should be
+        """
+        example.org.		3600	IN	NS	ns1.example.org.
+        example.org.		3600	IN	NS	ns2.example.org.
+        example.org.		3600	IN	SOA	ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+        example.org.		3600	IN	MX	10 mail.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns1.example.org.	3600	IN	A	192.0.2.3
+        ns2.example.org.	3600	IN	A	192.0.2.4
+        mail.example.org.	3600	IN	A	192.0.2.10
+        """