BIND 10 master, updated. 0640f80c7a62cf9c3e289f8290c5a3b44f4385f5 Merge branch 'work/lowcache'
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Apr 27 08:37:10 UTC 2011
The branch, master has been updated
via 0640f80c7a62cf9c3e289f8290c5a3b44f4385f5 (commit)
via 309bc10ee624106cf75165efd4251312deed3212 (commit)
via abdb0ff5e8cdd781d7c4960bb51d8538f4e3b44e (commit)
via b963854734ff8ba78ccb14f2e5a481c9426881c6 (commit)
via ea46adf9f6bbdd78a06ae748d979986a75b9b8d2 (commit)
via ffbe8a099bece23efb94e3adbe8fdfff8cfb379e (commit)
via 3c194549a61e68ed38bbcd90e69f1f954ff286f4 (commit)
from 4903de33e8f5d16ed99b6ad6b22fac023e653f71 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 0640f80c7a62cf9c3e289f8290c5a3b44f4385f5
Merge: 4903de33e8f5d16ed99b6ad6b22fac023e653f71 309bc10ee624106cf75165efd4251312deed3212
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Wed Apr 27 10:26:35 2011 +0200
Merge branch 'work/lowcache'
commit 309bc10ee624106cf75165efd4251312deed3212
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Apr 26 20:59:59 2011 +0200
[trac859] Split the logic to separate function
commit abdb0ff5e8cdd781d7c4960bb51d8538f4e3b44e
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Tue Apr 26 13:38:25 2011 +0200
[trac859] Remove unused class
commit b963854734ff8ba78ccb14f2e5a481c9426881c6
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Fri Apr 22 21:59:49 2011 +0200
[trac859] Start delegating as deep as possible
commit ea46adf9f6bbdd78a06ae748d979986a75b9b8d2
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Fri Apr 22 21:13:12 2011 +0200
[trac859] Test for correct resolving start
commit ffbe8a099bece23efb94e3adbe8fdfff8cfb379e
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Fri Apr 22 18:21:21 2011 +0200
[trac859] Make TestResolver reusable by other libs
commit 3c194549a61e68ed38bbcd90e69f1f954ff286f4
Author: Michal 'vorner' Vaner <michal.vaner at nic.cz>
Date: Fri Apr 22 16:09:15 2011 +0200
[trac859] Remove leftover forward declaration
-----------------------------------------------------------------------
Summary of changes:
src/lib/nsas/nameserver_address_store.h | 1 -
src/lib/nsas/tests/nameserver_address_unittest.cc | 3 +-
src/lib/nsas/tests/nameserver_entry_unittest.cc | 3 +-
src/lib/nsas/tests/nsas_test.h | 148 +----------------
src/lib/resolve/recursive_query.cc | 66 +++++++-
src/lib/resolve/tests/recursive_query_unittest.cc | 112 +++++++++++-
src/lib/util/unittests/Makefile.am | 2 +-
src/lib/util/unittests/resolver.h | 193 +++++++++++++++++++++
8 files changed, 367 insertions(+), 161 deletions(-)
create mode 100644 src/lib/util/unittests/resolver.h
-----------------------------------------------------------------------
diff --git a/src/lib/nsas/nameserver_address_store.h b/src/lib/nsas/nameserver_address_store.h
index 148052d..87845c9 100644
--- a/src/lib/nsas/nameserver_address_store.h
+++ b/src/lib/nsas/nameserver_address_store.h
@@ -38,7 +38,6 @@ template<class T> class LruList;
namespace nsas {
-class ResolverInterface;
template<class T> class HashTable;
class ZoneEntry;
class NameserverEntry;
diff --git a/src/lib/nsas/tests/nameserver_address_unittest.cc b/src/lib/nsas/tests/nameserver_address_unittest.cc
index e3bc5de..1b211e9 100644
--- a/src/lib/nsas/tests/nameserver_address_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_address_unittest.cc
@@ -51,7 +51,8 @@ public:
ns_.reset(new NameserverEntry(name_.toText(), RRClass::IN()));
ns_->askIP(resolver_.get(), boost::shared_ptr<Callback>(new Callback), ANY_OK);
resolver_->asksIPs(name_, 0, 1);
- resolver_->requests[0].second->success(createResponseMessage(rrv4_));
+ resolver_->requests[0].second->success(
+ isc::util::unittests::createResponseMessage(rrv4_));
}
// Return the sample NameserverEntry
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 7e9f675..3435d26 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -72,7 +72,8 @@ private:
RRsetPtr set)
{
if (set) {
- resolver->requests[index].second->success(createResponseMessage(set));
+ resolver->requests[index].second->success(
+ isc::util::unittests::createResponseMessage(set));
} else {
resolver->requests[index].second->failure();
}
diff --git a/src/lib/nsas/tests/nsas_test.h b/src/lib/nsas/tests/nsas_test.h
index 033df81..2dd95ef 100644
--- a/src/lib/nsas/tests/nsas_test.h
+++ b/src/lib/nsas/tests/nsas_test.h
@@ -27,6 +27,7 @@
#include <config.h>
#include <util/buffer.h>
+#include <util/unittests/resolver.h>
#include <dns/message.h>
#include <dns/rdata.h>
#include <dns/rrtype.h>
@@ -35,24 +36,12 @@
#include <dns/rcode.h>
#include <dns/messagerenderer.h>
#include <dns/rdataclass.h>
-#include <resolve/resolver_interface.h>
#include "../nsas_entry.h"
using namespace isc::dns::rdata;
using namespace isc::dns;
using namespace isc::util;
-
-namespace {
- MessagePtr
- createResponseMessage(RRsetPtr answer_rrset)
- {
- MessagePtr response(new Message(Message::RENDER));
- response->setOpcode(Opcode::QUERY());
- response->setRcode(Rcode::NOERROR());
- response->addRRset(Message::SECTION_ANSWER, answer_rrset);
- return response;
- }
-}
+using isc::util::unittests::TestResolver;
namespace isc {
namespace dns {
@@ -223,139 +212,6 @@ private:
static const uint32_t HASHTABLE_DEFAULT_SIZE = 1009; ///< First prime above 1000
-using namespace std;
-
-/*
- * This pretends to be a resolver. It stores the queries and
- * they can be answered.
- */
-class TestResolver : public isc::resolve::ResolverInterface {
- private:
- bool checkIndex(size_t index) {
- return (requests.size() > index);
- }
-
- typedef std::map<isc::dns::Question, RRsetPtr >
- PresetAnswers;
- PresetAnswers answers_;
- public:
- typedef pair<QuestionPtr, CallbackPtr> Request;
- vector<Request> requests;
-
- /// \brief Destructor
- ///
- /// This is important. All callbacks in the requests vector must be
- /// called to remove them from internal loops. Without this, destroying
- /// the NSAS object will leave memory assigned.
- ~TestResolver() {
- for (size_t i = 0; i < requests.size(); ++i) {
- requests[i].second->failure();
- }
- }
-
- virtual void resolve(const QuestionPtr& q, const CallbackPtr& c) {
- PresetAnswers::iterator it(answers_.find(*q));
- if (it == answers_.end()) {
- requests.push_back(Request(q, c));
- } else {
- if (it->second) {
- c->success(createResponseMessage(it->second));
- } else {
- c->failure();
- }
- }
- }
-
- /*
- * Add a preset answer. If shared_ptr() is passed (eg. NULL),
- * it will generate failure. If the question is not preset,
- * it goes to requests and you can answer later.
- */
- void addPresetAnswer(const isc::dns::Question& question,
- RRsetPtr answer)
- {
- answers_[question] = answer;
- }
-
- // Thrown if the query at the given index does not exist.
- class NoSuchRequest : public std::exception { };
-
- // Thrown if the answer does not match the query
- class DifferentRequest : public std::exception { };
-
- QuestionPtr operator[](size_t index) {
- if (index >= requests.size()) {
- throw NoSuchRequest();
- }
- return (requests[index].first);
- }
- /*
- * Looks if the two provided requests in resolver are A and AAAA.
- * Sorts them so index1 is A.
- *
- * Returns false if there aren't enough elements
- */
- bool asksIPs(const Name& name, size_t index1, size_t index2) {
- size_t max = (index1 < index2) ? index2 : index1;
- if (!checkIndex(max)) {
- return false;
- }
- EXPECT_EQ(name, (*this)[index1]->getName());
- EXPECT_EQ(name, (*this)[index2]->getName());
- EXPECT_EQ(RRClass::IN(), (*this)[index1]->getClass());
- EXPECT_EQ(RRClass::IN(), (*this)[index2]->getClass());
- // If they are the other way around, swap
- if ((*this)[index1]->getType() == RRType::AAAA() &&
- (*this)[index2]->getType() == RRType::A())
- {
- TestResolver::Request tmp((*this).requests[index1]);
- (*this).requests[index1] =
- (*this).requests[index2];
- (*this).requests[index2] = tmp;
- }
- // Check the correct addresses
- EXPECT_EQ(RRType::A(), (*this)[index1]->getType());
- EXPECT_EQ(RRType::AAAA(), (*this)[index2]->getType());
- return (true);
- }
-
- /*
- * Sends a simple answer to a query.
- * 1) Provide index of a query and the address(es) to pass.
- * 2) Provide index of query and components of address to pass.
- */
- void answer(size_t index, RRsetPtr& set) {
- if (index >= requests.size()) {
- throw NoSuchRequest();
- }
- requests[index].second->success(createResponseMessage(set));
- }
-
- void answer(size_t index, const Name& name, const RRType& type,
- const rdata::Rdata& rdata, size_t TTL = 100)
- {
- RRsetPtr set(new RRset(name, RRClass::IN(),
- type, RRTTL(TTL)));
- set->addRdata(rdata);
- answer(index, set);
- }
-
-
- void provideNS(size_t index,
- RRsetPtr nameservers)
- {
- if (index >= requests.size()) {
- throw NoSuchRequest();
- }
- if (requests[index].first->getName() != nameservers->getName() ||
- requests[index].first->getType() != RRType::NS())
- {
- throw DifferentRequest();
- }
- requests[index].second->success(createResponseMessage(nameservers));
- }
-};
-
// String constants. These should end in a dot.
static const std::string EXAMPLE_CO_UK("example.co.uk.");
static const std::string EXAMPLE_NET("example.net.");
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index c0d5ee6..34411ee 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -28,6 +28,7 @@
#include <dns/message.h>
#include <dns/opcode.h>
#include <dns/exceptions.h>
+#include <dns/rdataclass.h>
#include <resolve/resolve.h>
#include <cache/resolver_cache.h>
@@ -48,6 +49,65 @@ using namespace isc::asiolink;
namespace isc {
namespace asiodns {
+namespace {
+// Function to check if the given name/class has any address in the cache
+bool
+hasAddress(const Name& name, const RRClass& rrClass,
+ const isc::cache::ResolverCache& cache)
+{
+ // FIXME: If we are single-stack and we get only the other type of
+ // address, what should we do? In that case, it will be considered
+ // unreachable, which is most probably true, because A and AAAA will
+ // usually have the same RTT, so we should have both or none from the
+ // glue.
+ return (cache.lookup(name, RRType::A(), rrClass) != RRsetPtr() ||
+ cache.lookup(name, RRType::AAAA(), rrClass) != RRsetPtr());
+}
+
+}
+
+/// \brief Find deepest usable delegation in the cache
+///
+/// This finds the deepest delegation we have in cache and is safe to use.
+/// It is not public function, therefore it's not in header. But it's not
+/// in anonymous namespace, so we can call it from unittests.
+/// \param name The name we want to delegate to.
+/// \param cache The place too look for known delegations.
+std::string
+deepestDelegation(Name name, RRClass rrclass,
+ isc::cache::ResolverCache& cache)
+{
+ RRsetPtr cachedNS;
+ // Look for delegation point from bottom, until we find one with
+ // IP address or get to root.
+ //
+ // We need delegation with IP address so we can ask it right away.
+ // If we don't have the IP address, we would need to ask above it
+ // anyway in the best case, and the NS could be inside the zone,
+ // and we could get all loopy with the NSAS in the worst case.
+ while (name.getLabelCount() > 1 &&
+ (cachedNS = cache.lookupDeepestNS(name, rrclass)) != RRsetPtr()) {
+ // Look if we have an IP address for the NS
+ for (RdataIteratorPtr ns(cachedNS->getRdataIterator());
+ !ns->isLast(); ns->next()) {
+ // Do we have IP for this specific NS?
+ if (hasAddress(dynamic_cast<const rdata::generic::NS&>(
+ ns->getCurrent()).getNSName(), rrclass,
+ cache)) {
+ // Found one, stop checking and use this zone
+ // (there may be more addresses, that's only better)
+ return (cachedNS->getName().toText());
+ }
+ }
+ // We don't have anything for this one, so try something higher
+ if (name.getLabelCount() > 1) {
+ name = name.split(1);
+ }
+ }
+ // Fallback, nothing found, start at root
+ return (".");
+}
+
typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
// Here we do not use the typedef above, as the SunStudio compiler
@@ -239,7 +299,6 @@ private:
// if we have a response for our query stored already. if
// so, call handlerecursiveresponse(), if not, we call send()
void doLookup() {
- cur_zone_ = ".";
dlog("doLookup: try cache");
Message cached_message(Message::RENDER);
isc::resolve::initResponseMessage(question_, cached_message);
@@ -255,9 +314,12 @@ private:
stop();
}
} else {
+ dlog("doLookup: get lowest usable delegation from cache");
+ cur_zone_ = deepestDelegation(question_.getName(),
+ question_.getClass(), cache_);
send();
}
-
+
}
// Send the current question to the given nameserver address
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
index 3338893..2520994 100644
--- a/src/lib/resolve/tests/recursive_query_unittest.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -31,7 +31,9 @@
#include <dns/rcode.h>
#include <util/buffer.h>
+#include <util/unittests/resolver.h>
#include <dns/message.h>
+#include <dns/rdataclass.h>
#include <nsas/nameserver_address_store.h>
#include <cache/resolver_cache.h>
@@ -59,6 +61,18 @@ using namespace isc::asiolink;
using namespace isc::dns;
using namespace isc::util;
+namespace isc {
+namespace asiodns {
+
+// This is defined in recursive_query.cc, but not in header (it's not public
+// function). So bring it in to be tested.
+std::string
+deepestDelegation(Name name, RRClass rrclass,
+ isc::cache::ResolverCache& cache);
+
+}
+}
+
namespace {
const char* const TEST_SERVER_PORT = "53535";
const char* const TEST_CLIENT_PORT = "53536";
@@ -110,6 +124,9 @@ class RecursiveQueryTest : public ::testing::Test {
protected:
RecursiveQueryTest();
~RecursiveQueryTest() {
+ // It would delete itself, but after the io_service_, which could
+ // segfailt in case there were unhandled requests
+ resolver_.reset();
if (res_ != NULL) {
freeaddrinfo(res_);
}
@@ -348,12 +365,6 @@ protected:
private:
bool* done_;
};
-
- class MockResolver : public isc::resolve::ResolverInterface {
- void resolve(const QuestionPtr& question,
- const ResolverInterface::CallbackPtr& callback) {
- }
- };
// This version of mock server just stops the io_service when it is resumed
// the second time. (Used in the clientTimeout test, where resume
@@ -423,16 +434,17 @@ protected:
vector<uint8_t> callback_data_;
int sock_;
struct addrinfo* res_;
+ boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
};
RecursiveQueryTest::RecursiveQueryTest() :
dns_service_(NULL), callback_(NULL), callback_protocol_(0),
- callback_native_(-1), sock_(-1), res_(NULL)
+ callback_native_(-1), sock_(-1), res_(NULL),
+ resolver_(new isc::util::unittests::TestResolver())
{
io_service_ = new IOService();
setDNSService(true, true);
- boost::shared_ptr<MockResolver>mock_resolver(new MockResolver());
- nsas_ = new isc::nsas::NameserverAddressStore(mock_resolver);
+ nsas_ = new isc::nsas::NameserverAddressStore(resolver_);
}
TEST_F(RecursiveQueryTest, v6UDPSend) {
@@ -857,7 +869,89 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
EXPECT_EQ(0, answer->getRRCount(Message::SECTION_ANSWER));
}
+// Test that we don't start at root when we have a lower NS cached.
+TEST_F(RecursiveQueryTest, CachedNS) {
+ setDNSService(true, true);
+
+ // Check we have a reasonable fallback - if there's nothing of interest
+ // in the cache, start at root.
+ EXPECT_EQ(".", deepestDelegation(Name("www.somewhere.deep.example.org"),
+ RRClass::IN(), cache_));
+
+ // Prefill the cache. There's a zone with a NS and IP address for one
+ // of them (to see that one is enough) and another deeper one, with NS,
+ // but without IP.
+ RRsetPtr nsUpper(new RRset(Name("example.org"), RRClass::IN(),
+ RRType::NS(), RRTTL(300)));
+ nsUpper->addRdata(rdata::generic::NS(Name("ns.example.org")));
+ nsUpper->addRdata(rdata::generic::NS(Name("ns2.example.org")));
+
+ RRsetPtr nsLower(new RRset(Name("somewhere.deep.example.org"),
+ RRClass::IN(), RRType::NS(), RRTTL(300)));
+ nsLower->addRdata(rdata::generic::NS(Name("ns.somewhere.deep.example.org"))
+ );
+
+ RRsetPtr nsIp(new RRset(Name("ns2.example.org"), RRClass::IN(),
+ RRType::A(), RRTTL(300)));
+ nsIp->addRdata(rdata::in::A("192.0.2.1"));
+
+ // Make sure the test runs in the correct environment (we don't test
+ // the cache, but we need it to unswer this way for the test, so we
+ // just make sure)
+ ASSERT_TRUE(cache_.update(nsUpper));
+ ASSERT_TRUE(cache_.update(nsLower));
+ ASSERT_TRUE(cache_.update(nsIp));
+ RRsetPtr deepest(cache_.lookupDeepestNS(Name(
+ "www.somewhere.deep.example.org"), RRClass::IN()));
+ ASSERT_NE(RRsetPtr(), deepest);
+ ASSERT_EQ(nsLower->getName(), deepest->getName());
+
+ // Direct check of the function that chooses the delegation point
+ // It should not use nsLower, because we don't have IP address for
+ // that one. But it can choose nsUpper.
+ EXPECT_EQ("example.org.",
+ deepestDelegation(Name("www.somewhere.deep.example.org"),
+ RRClass::IN(), cache_));
+
+ // Now more complex and indirect test:
+ // We ask it to resolve the name for us. It will pick up a delegation
+ // point and ask NSAS for it. NSAS will in turn ask resolver for NS record
+ // of the delegation point. We then pick it up from the fake resolver
+ // and check it is the correct one. This checks the delegation point
+ // travels safely trough the whole path there (it would be enough to check
+ // it up to NSAS, but replacing NSAS is more complicated, so we just
+ // include in the test as well for simplicity).
+
+ // Prepare the recursive query
+ vector<pair<string, uint16_t> > roots;
+ roots.push_back(pair<string, uint16_t>("192.0.2.2", 53));
+
+ RecursiveQuery rq(*dns_service_, *nsas_, cache_,
+ vector<pair<string, uint16_t> >(), roots);
+ // Ask a question at the bottom. It should not use the lower NS, because
+ // it would lead to a loop in NS. But it can use the nsUpper one, it has
+ // an IP address and we can avoid asking root.
+ Question q(Name("www.somewhere.deep.example.org"), RRClass::IN(),
+ RRType::A());
+ OutputBufferPtr buffer(new OutputBuffer(0));
+ MessagePtr answer(new Message(Message::RENDER));
+ bool done;
+ // The server is here so we have something to pass there
+ MockServer server(*io_service_);
+ rq.resolve(q, answer, buffer, &server);
+ // We don't need to run the service in this test. We are interested only
+ // in the place it starts resolving at
+
+ // Look what is asked by NSAS - it should be our delegation point.
+ EXPECT_NO_THROW(EXPECT_EQ(nsUpper->getName(),
+ (*resolver_)[0]->getName()) <<
+ "It starts resolving at the wrong place") <<
+ "It does not ask NSAS anything, how does it know where to send?";
+}
+
// TODO: add tests that check whether the cache is updated on succesfull
// responses, and not updated on failures.
+
+
}
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index b702647..4d5896f 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -2,6 +2,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CXXFLAGS = $(B10_CXXFLAGS)
lib_LTLIBRARIES = libutil_unittests.la
-libutil_unittests_la_SOURCES = fork.h fork.cc
+libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/unittests/resolver.h b/src/lib/util/unittests/resolver.h
new file mode 100644
index 0000000..560a892
--- /dev/null
+++ b/src/lib/util/unittests/resolver.h
@@ -0,0 +1,193 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef UTIL_UNITTEST_RESOLVER_H
+#define UTIL_UNITTEST_RESOLVER_H
+
+/// \file resolver.h
+/// \brief Fake resolver
+
+#include <map>
+#include <dns/rrset.h>
+#include <dns/question.h>
+#include <dns/message.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rrttl.h>
+#include <resolve/resolver_interface.h>
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// \brief Put rrset into a message as an answer
+inline static isc::dns::MessagePtr
+createResponseMessage(isc::dns::RRsetPtr answer_rrset)
+{
+ isc::dns::MessagePtr response(new isc::dns::Message(
+ isc::dns::Message::RENDER));
+ response->setOpcode(isc::dns::Opcode::QUERY());
+ response->setRcode(isc::dns::Rcode::NOERROR());
+ response->addRRset(isc::dns::Message::SECTION_ANSWER, answer_rrset);
+ return response;
+}
+
+/// \brief Mock resolver
+///
+/// This class pretends to be a resolver. However, it only stores the
+/// requests and can answer them right away by prepared answers. It doesn't
+/// do any real work and is intended for testing purposes.
+class TestResolver : public isc::resolve::ResolverInterface {
+ private:
+ bool checkIndex(size_t index) {
+ return (requests.size() > index);
+ }
+
+ typedef std::map<isc::dns::Question, isc::dns::RRsetPtr>
+ PresetAnswers;
+ PresetAnswers answers_;
+ public:
+ typedef std::pair<isc::dns::QuestionPtr, CallbackPtr> Request;
+ /// \brief List of requests the tested class sent trough resolve
+ std::vector<Request> requests;
+
+ /// \brief Destructor
+ ///
+ /// This is important. All callbacks in the requests vector must be
+ /// called to remove them from internal loops. Without this, destroying
+ /// the NSAS object will leave memory assigned.
+ ~TestResolver() {
+ for (size_t i = 0; i < requests.size(); ++i) {
+ requests[i].second->failure();
+ }
+ }
+
+ /// \brief Testing version of resolve
+ ///
+ /// If there's a prepared answer (provided by addPresetAnswer), it
+ /// answers it right away. Otherwise it just stores the request in
+ /// the requests member so it can be examined later.
+ virtual void resolve(const isc::dns::QuestionPtr& q,
+ const CallbackPtr& c)
+ {
+ PresetAnswers::iterator it(answers_.find(*q));
+ if (it == answers_.end()) {
+ requests.push_back(Request(q, c));
+ } else {
+ if (it->second) {
+ c->success(createResponseMessage(it->second));
+ } else {
+ c->failure();
+ }
+ }
+ }
+
+ /// \brief Add a preset answer.
+ ///
+ /// Add a preset answer. If shared_ptr() is passed (eg. NULL),
+ /// it will generate failure. If the question is not preset,
+ /// it goes to requests and you can answer later.
+ void addPresetAnswer(const isc::dns::Question& question,
+ isc::dns::RRsetPtr answer)
+ {
+ answers_[question] = answer;
+ }
+
+ /// \brief Thrown if the query at the given index does not exist.
+ class NoSuchRequest : public std::exception { };
+
+ /// \brief Thrown if the answer does not match the query
+ class DifferentRequest : public std::exception { };
+
+ /// \brief Provides the question of request on given answer
+ isc::dns::QuestionPtr operator[](size_t index) {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ return (requests[index].first);
+ }
+ /// \brief Test it asks for IP addresses
+ /// Looks if the two provided requests in resolver are A and AAAA.
+ /// Sorts them so index1 is A.
+ ///
+ /// Returns false if there aren't enough elements
+ bool asksIPs(const isc::dns::Name& name, size_t index1,
+ size_t index2)
+ {
+ size_t max = (index1 < index2) ? index2 : index1;
+ if (!checkIndex(max)) {
+ return false;
+ }
+ EXPECT_EQ(name, (*this)[index1]->getName());
+ EXPECT_EQ(name, (*this)[index2]->getName());
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*this)[index1]->getClass());
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*this)[index2]->getClass());
+ // If they are the other way around, swap
+ if ((*this)[index1]->getType() == isc::dns::RRType::AAAA() &&
+ (*this)[index2]->getType() == isc::dns::RRType::A())
+ {
+ TestResolver::Request tmp((*this).requests[index1]);
+ (*this).requests[index1] =
+ (*this).requests[index2];
+ (*this).requests[index2] = tmp;
+ }
+ // Check the correct addresses
+ EXPECT_EQ(isc::dns::RRType::A(), (*this)[index1]->getType());
+ EXPECT_EQ(isc::dns::RRType::AAAA(), (*this)[index2]->getType());
+ return (true);
+ }
+
+ /// \brief Answer a request
+ /// Sends a simple answer to a query.
+ /// 1) Provide index of a query and the address(es) to pass.
+ /// 2) Provide index of query and components of address to pass.
+ void answer(size_t index, isc::dns::RRsetPtr& set) {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ requests[index].second->success(createResponseMessage(set));
+ }
+
+ void answer(size_t index, const isc::dns::Name& name,
+ const isc::dns::RRType& type,
+ const isc::dns::rdata::Rdata& rdata, size_t TTL = 100)
+ {
+ isc::dns::RRsetPtr set(new isc::dns::RRset(name,
+ isc::dns::RRClass::IN(),
+ type,
+ isc::dns::RRTTL(TTL)));
+ set->addRdata(rdata);
+ answer(index, set);
+ }
+ /// \Answer the query at index by list of nameservers
+ void provideNS(size_t index, isc::dns::RRsetPtr nameservers)
+ {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ if (requests[index].first->getName() != nameservers->getName() ||
+ requests[index].first->getType() != isc::dns::RRType::NS())
+ {
+ throw DifferentRequest();
+ }
+ requests[index].second->success(createResponseMessage(nameservers));
+ }
+};
+
+}
+}
+}
+
+#endif
More information about the bind10-changes
mailing list