123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753 |
- // Copyright (C) 2010 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.
- #include <config.h>
- #include <gtest/gtest.h>
- #include <boost/shared_ptr.hpp>
- #include <boost/foreach.hpp>
- #include <boost/lexical_cast.hpp>
- #include <cmath>
- #include <dns/rrclass.h>
- #include <dns/rdataclass.h>
- #include "../asiolink.h"
- #include "../zone_entry.h"
- #include "../nameserver_entry.h"
- #include "../address_request_callback.h"
- #include "../nsas_entry_compare.h"
- #include "../hash_deleter.h"
- #include "nsas_test.h"
- using namespace isc::nsas;
- using namespace asiolink;
- using namespace std;
- using namespace isc::dns;
- namespace {
- /// \brief Inherited version with access into its internals for tests
- class InheritedZoneEntry : public ZoneEntry {
- public:
- InheritedZoneEntry(
- boost::shared_ptr<isc::resolve::ResolverInterface> resolver,
- const std::string& name, const RRClass& class_code,
- boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
- boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru) :
- ZoneEntry(resolver, name, class_code, nameserver_table,
- nameserver_lru)
- { }
- NameserverVector& nameservers() { return nameservers_; }
- };
- /// \brief Test Fixture Class
- class ZoneEntryTest : public TestWithRdata {
- protected:
- /// \brief Constructor
- ZoneEntryTest() :
- nameserver_table_(new HashTable<NameserverEntry>(
- new NsasEntryCompare<NameserverEntry>)),
- nameserver_lru_(new LruList<NameserverEntry>(
- (3 * nameserver_table_->tableSize()),
- new HashDeleter<NameserverEntry>(*nameserver_table_))),
- resolver_(new TestResolver),
- callback_(new Callback)
- { }
- /// \brief Tables of nameservers to pass into zone entry constructor
- boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table_;
- boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
- /// \brief The resolver
- boost::shared_ptr<TestResolver> resolver_;
- /**
- * \short A callback we use into the zone.
- *
- * It counts failures and stores successufll results.
- */
- struct Callback : public AddressRequestCallback {
- Callback() : unreachable_count_(0) {}
- size_t unreachable_count_;
- vector<NameserverAddress> successes_;
- virtual void unreachable() { unreachable_count_ ++; }
- virtual void success(const NameserverAddress& address) {
- successes_.push_back(address);
- }
- };
- boost::shared_ptr<Callback> callback_;
- // Empty callback to pass to nameserver entry, to do injection of them
- struct NseCallback : public NameserverEntry::Callback {
- virtual void operator()(boost::shared_ptr<NameserverEntry>) { }
- };
- boost::shared_ptr<NseCallback> nseCallback() {
- return (boost::shared_ptr<NseCallback>(new NseCallback));
- }
- /**
- * \short Function returning a new zone.
- *
- * Convenience funcion, just creating a new zone, to shorten the code.
- */
- boost::shared_ptr<InheritedZoneEntry> getZone() {
- return (boost::shared_ptr<InheritedZoneEntry>(new InheritedZoneEntry(
- resolver_, EXAMPLE_CO_UK, RRClass::IN(), nameserver_table_,
- nameserver_lru_)));
- }
- /**
- * \short Creates and injects a NameserverEntry
- *
- * This is used by few tests checking it works when the nameserver
- * hash table already contains the NameserverEntry. This function
- * creates one and puts it into the hash table.
- */
- boost::shared_ptr<NameserverEntry> injectEntry() {
- boost::shared_ptr<NameserverEntry> nse(new NameserverEntry(ns_name_.toText(),
- RRClass::IN()));
- nameserver_table_->add(nse, HashKey(ns_name_.toText(), RRClass::IN()));
- EXPECT_EQ(Fetchable::NOT_ASKED, nse->getState());
- return (nse);
- }
- /**
- * \short Common part of few tests.
- *
- * All the tests NameserverEntryReady, NameserverEntryNotAsked,
- * NameserverEntryInProgress, NameserverEntryExpired,
- * NameserverEntryUnreachable check that it does not break
- * when the nameserver hash table already contains the nameserver
- * in one of the Fetchable::State.
- *
- * All the tests prepare the NameserverEntry and then call this
- * to see if the zone really works. This asks and checks if it
- * asks for the IP addresses or not and if it succeeds or fails.
- *
- * \param answer Should it ask for IP addresses of the nameserver?
- * If not, it expects it already asked during the preparation
- * (therefore the request count in resolver is 2).
- * \param success_count How many callbacks to the zone should
- * succeed.
- * \param failure_count How many callbacks to the zone should
- * fail.
- */
- void checkInjected(bool answer, size_t success_count = 1,
- size_t failure_count = 0)
- {
- // Create the zone and check it acts correctly
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- resolver_->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::NS()), rr_single_);
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(2, resolver_->requests.size());
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
- if (answer) {
- EXPECT_NO_THROW(resolver_->answer(0, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1")));
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::1")));
- }
- // Check the answers
- EXPECT_EQ(Fetchable::READY, zone->getState());
- EXPECT_EQ(failure_count, callback_->unreachable_count_);
- EXPECT_EQ(success_count, callback_->successes_.size());
- for (size_t i = 0; i < callback_->successes_.size(); ++ i) {
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[i].getAddress()) ||
- IOAddress("2001:db8::1").equal(
- callback_->successes_[i].getAddress()));
- }
- }
- };
- /// Tests of the default constructor
- TEST_F(ZoneEntryTest, DefaultConstructor) {
- // Default constructor should not create any RRsets
- InheritedZoneEntry alpha(resolver_, EXAMPLE_CO_UK,
- RRClass::IN(), nameserver_table_, nameserver_lru_);
- EXPECT_EQ(EXAMPLE_CO_UK, alpha.getName());
- EXPECT_EQ(RRClass::IN(), alpha.getClass());
- EXPECT_TRUE(alpha.nameservers().empty());
- }
- /**
- * \short Test with no nameservers.
- *
- * When we create a zone that does not have any nameservers,
- * it should return failures right away (eg. no queries to nameservers
- * should be generated anywhere and the failure should be provided).
- */
- TEST_F(ZoneEntryTest, CallbackNoNS) {
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- // feed it with a callback
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- // Give it the (empty) nameserver list
- EXPECT_NO_THROW(resolver_->provideNS(0, rr_empty_));
- // It should tell us it is unreachable.
- EXPECT_TRUE(callback_->successes_.empty());
- EXPECT_EQ(1, callback_->unreachable_count_);
- }
- /**
- * \short Test how the zone behaves when the list of nameserves change.
- *
- * We use TTL of 0, so it asks every time for new list of nameservers.
- * This allows us to pass a different list each time.
- *
- * So, this implicitly tests that it behaves correctly with 0 TTL as well,
- * it means that it answers even with 0 TTL and that it answers only
- * one query (or, all queries queued at that time).
- *
- * We change the list twice, to see it can ask for another nameserver and
- * then to see if it can return to the previous (already cached) nameserver.
- */
- TEST_F(ZoneEntryTest, ChangedNS) {
- // Make it zero TTL, so it expires right away
- rr_single_->setTTL(RRTTL(0));
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- // Feed it with callback
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- EXPECT_NO_THROW(resolver_->provideNS(0, rr_single_));
- // It should not be answered yet, it should ask for the IP addresses
- // (trough the NameserverEntry there)
- EXPECT_TRUE(callback_->successes_.empty());
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 1, 2));
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1")));
- ASSERT_EQ(1, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[0].getAddress()));
- EXPECT_NO_THROW(resolver_->answer(2, ns_name_, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::1")));
- EXPECT_EQ(1, callback_->successes_.size());
- // It should request the NSs again, as TTL is 0
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(4, resolver_->requests.size());
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- Name different_name("ns.example.com");
- // Create a different answer
- RRsetPtr different_ns(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::NS(), RRTTL(0)));
- different_ns->addRdata(rdata::generic::NS(different_name));
- EXPECT_NO_THROW(resolver_->provideNS(3, different_ns));
- // It should become ready and ask for the new nameserver addresses
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // Answer one of the IP addresses, we should get an address now
- EXPECT_TRUE(resolver_->asksIPs(different_name, 4, 5));
- EXPECT_NO_THROW(resolver_->answer(4, different_name, RRType::A(),
- rdata::in::A("192.0.2.2")));
- ASSERT_EQ(2, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.2").equal(
- callback_->successes_[1].getAddress()));
- // And now, switch back, as it timed out again
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(7, resolver_->requests.size());
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- // When we answer with the original, it should still be cached and
- // we should get the answer
- EXPECT_NO_THROW(resolver_->provideNS(0, rr_single_));
- EXPECT_EQ(7, resolver_->requests.size());
- EXPECT_EQ(Fetchable::READY, zone->getState());
- ASSERT_EQ(3, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[0].getAddress()));
- }
- /**
- * \short Check it works when everything is answered.
- *
- * This test emulates a situation when all queries for NS rrsets and
- * IP addresses (of the NameserverEntry objects inside) are answered
- * positively. All the callbacks should be called and answer to them
- * provided.
- */
- TEST_F(ZoneEntryTest, CallbacksAnswered) {
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- // Feed it with a callback
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- EXPECT_NO_THROW(resolver_->provideNS(0, rr_single_));
- // It should not be answered yet, its NameserverEntry should ask for the
- // IP addresses
- EXPECT_TRUE(callback_->successes_.empty());
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 1, 2));
- // We should be READY, as it marks we have nameservers
- // (not that they are ready)
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // Give two more callbacks, with different address families
- zone->addCallback(callback_, V4_ONLY);
- zone->addCallback(callback_, V6_ONLY);
- // Nothing more is asked
- EXPECT_EQ(3, resolver_->requests.size());
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1")));
- // Two are answered (ANY and V4)
- ASSERT_EQ(2, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[0].getAddress()));
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[1].getAddress()));
- // None are rejected
- EXPECT_EQ(0, callback_->unreachable_count_);
- // Answer the IPv6 one as well
- EXPECT_NO_THROW(resolver_->answer(2, ns_name_, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::1")));
- // This should answer the third callback
- EXPECT_EQ(0, callback_->unreachable_count_);
- ASSERT_EQ(3, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("2001:db8::1").equal(
- callback_->successes_[2].getAddress()));
- // It should think it is ready
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // When we ask something more, it should be answered right away
- zone->addCallback(callback_, V4_ONLY);
- EXPECT_EQ(3, resolver_->requests.size());
- ASSERT_EQ(4, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[3].getAddress()));
- EXPECT_EQ(0, callback_->unreachable_count_);
- }
- /**
- * \short Test zone reachable only on IPv4.
- *
- * This test simulates a zone with its nameservers reachable only
- * over IPv4. It means we answer the NS query, the A query, but
- * we generate a failure for AAAA.
- *
- * The callbacks asking for any address and IPv4 address should be
- * called successfully, while the ones asking for IPv6 addresses should
- * fail.
- */
- TEST_F(ZoneEntryTest, CallbacksAOnly) {
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- EXPECT_NO_THROW(resolver_->provideNS(0, rr_single_));
- // It should not be answered yet, it should ask for the IP addresses
- EXPECT_TRUE(callback_->successes_.empty());
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 1, 2));
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // Give two more callbacks, with different address families
- zone->addCallback(callback_, V4_ONLY);
- zone->addCallback(callback_, V6_ONLY);
- ASSERT_GE(resolver_->requests.size(), 3);
- // We tell its NameserverEntry we can't get IPv6 address
- resolver_->requests[2].second->failure();
- // One should be rejected (V6_ONLY one), but two still stay
- EXPECT_EQ(0, callback_->successes_.size());
- EXPECT_EQ(1, callback_->unreachable_count_);
- // Answer the A one and see it answers what can be answered
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1")));
- ASSERT_EQ(2, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[0].getAddress()));
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[1].getAddress()));
- EXPECT_EQ(1, callback_->unreachable_count_);
- // Everything arriwed, so we are ready
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // Try asking something more and see it asks no more
- zone->addCallback(callback_, V4_ONLY);
- EXPECT_EQ(3, resolver_->requests.size());
- ASSERT_EQ(3, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[2].getAddress()));
- EXPECT_EQ(1, callback_->unreachable_count_);
- zone->addCallback(callback_, V6_ONLY);
- EXPECT_EQ(3, resolver_->requests.size());
- EXPECT_EQ(3, callback_->successes_.size());
- EXPECT_EQ(2, callback_->unreachable_count_);
- }
- /**
- * \short Test with unreachable and v6-reachable nameserver.
- *
- * In this test we have a zone with two nameservers. The first one of them
- * is unreachable, it will not have any addresses. We check that the ZoneEntry
- * is patient and asks and waits for the second one and then returns the
- * (successful) answers to us.
- */
- TEST_F(ZoneEntryTest, CallbackTwoNS) {
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- zone->addCallback(callback_, V4_ONLY);
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- EXPECT_NO_THROW(resolver_->provideNS(0, rrns_));
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // It asks a question (we do not know which nameserver)
- boost::shared_ptr<Name> name;
- ASSERT_NO_THROW(name.reset(new Name((*resolver_)[1]->getName())));
- ASSERT_TRUE(resolver_->asksIPs(*name, 1, 2));
- resolver_->requests[1].second->failure();
- // Nothing should be answered or failed yet, there's second one
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_EQ(0, callback_->successes_.size());
- ASSERT_TRUE(resolver_->asksIPs((*resolver_)[3]->getName(), 3, 4));
- // Fail the second one
- resolver_->requests[3].second->failure();
- // The callback should be failed now, as there is no chance of getting
- // v4 address
- EXPECT_EQ(1, callback_->unreachable_count_);
- EXPECT_EQ(0, callback_->successes_.size());
- // And question for v6 or any should still wait while v4 should be failed
- // right away
- zone->addCallback(callback_, V6_ONLY);
- EXPECT_EQ(1, callback_->unreachable_count_);
- EXPECT_EQ(0, callback_->successes_.size());
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(1, callback_->unreachable_count_);
- EXPECT_EQ(0, callback_->successes_.size());
- zone->addCallback(callback_, V4_ONLY);
- EXPECT_EQ(2, callback_->unreachable_count_);
- EXPECT_EQ(0, callback_->successes_.size());
- // Answer the IPv6 one
- EXPECT_NO_THROW(resolver_->answer(2, (*resolver_)[2]->getName(),
- RRType::AAAA(), rdata::in::AAAA("2001:db8::1")));
- // Ready, as we have at last some address
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // The other callbacks should be answered now
- EXPECT_EQ(2, callback_->unreachable_count_);
- ASSERT_EQ(2, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("2001:db8::1").equal(
- callback_->successes_[0].getAddress()));
- EXPECT_TRUE(IOAddress("2001:db8::1").equal(
- callback_->successes_[1].getAddress()));
- }
- /**
- * \short This test checks it works with answers from cache.
- *
- * The resolver might provide the answer by calling the callback both sometime
- * later or directly from its resolve method, causing recursion back inside
- * the ZoneEntry. This test checks it works even in the second case (eg. that
- * the ZoneEntry is able to handle callback called directly from the
- * resolve). Tries checking both positive and negative answers.
- */
- TEST_F(ZoneEntryTest, DirectAnswer) {
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- // One unsuccessfull attempt, nameservers fail
- resolver_->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::NS()), RRsetPtr());
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(0, callback_->successes_.size());
- EXPECT_EQ(1, callback_->unreachable_count_);
- EXPECT_EQ(0, resolver_->requests.size());
- EXPECT_EQ(Fetchable::UNREACHABLE, zone->getState());
- // Successfull attempt now
- zone = getZone();
- // First, fill the answers to all the questions it should ask
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- resolver_->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
- RRType::NS()), rr_single_);
- Name ns_name("ns.example.net");
- rrv4_->setName(ns_name);
- resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(), RRType::A()),
- rrv4_);
- rrv6_->setName(ns_name);
- resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(),
- RRType::AAAA()), rrv6_);
- // Reset the results
- callback_->unreachable_count_ = 0;
- // Ask for the IP address
- zone->addCallback(callback_, ANY_OK);
- // It should be answered right away, positively
- EXPECT_EQ(1, callback_->successes_.size());
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_EQ(0, resolver_->requests.size());
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // Reset the results
- callback_->successes_.clear();
- // Now, pretend we do not have IP addresses
- resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(), RRType::A()),
- RRsetPtr());
- resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(),
- RRType::AAAA()), RRsetPtr());
- // Get another zone and ask it again. It should fail.
- // Clean the table first, though, so it does not find the old nameserver
- nameserver_table_->remove(HashKey(ns_name.toText(), RRClass::IN()));
- zone = getZone();
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(0, callback_->successes_.size());
- EXPECT_EQ(1, callback_->unreachable_count_);
- EXPECT_EQ(0, resolver_->requests.size());
- // It should be ready, but have no IP addresses on the nameservers
- EXPECT_EQ(Fetchable::READY, zone->getState());
- }
- /**
- * \short Test it works with timeouting NameserverEntries.
- *
- * In this test we have a zone with nameserver addresses at TTL 0.
- * So, the NameserverEntry expires each time the ZoneEntry tries to get
- * its addresses and must ask it again.
- */
- TEST_F(ZoneEntryTest, AddressTimeout) {
- boost::shared_ptr<InheritedZoneEntry> zone(getZone());
- EXPECT_EQ(Fetchable::NOT_ASKED, zone->getState());
- zone->addCallback(callback_, ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, zone->getState());
- EXPECT_NO_THROW(resolver_->provideNS(0, rr_single_));
- // It should not be answered yet, it should ask for the IP addresses
- EXPECT_TRUE(callback_->successes_.empty());
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 1, 2));
- // We should be READY, as it marks we have nameservers
- // (not that they are ready)
- EXPECT_EQ(Fetchable::READY, zone->getState());
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1"), 0));
- // It answers, not rejects
- ASSERT_EQ(1, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[0].getAddress()));
- EXPECT_EQ(0, callback_->unreachable_count_);
- // As well with IPv6
- EXPECT_NO_THROW(resolver_->answer(2, ns_name_, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::1"), 0));
- EXPECT_EQ(1, callback_->successes_.size());
- EXPECT_EQ(Fetchable::READY, zone->getState());
- // When we ask for another one, it should ask for the addresses again
- zone->addCallback(callback_, ANY_OK);
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 3, 4));
- EXPECT_EQ(0, callback_->unreachable_count_);
- EXPECT_EQ(1, callback_->successes_.size());
- EXPECT_NO_THROW(resolver_->answer(3, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1"), 0));
- EXPECT_EQ(0, callback_->unreachable_count_);
- ASSERT_EQ(2, callback_->successes_.size());
- EXPECT_TRUE(IOAddress("192.0.2.1").equal(
- callback_->successes_[1].getAddress()));
- }
- /**
- * \short Injection tests.
- *
- * These tests check the ZoneEntry does not break when the nameserver hash
- * table already contains a NameserverEntry in some given state. Each test
- * for a different state.
- */
- //@{
- /// \short Test how the zone reacts to a nameserver entry in ready state
- TEST_F(ZoneEntryTest, NameserverEntryReady) {
- // Inject the entry
- boost::shared_ptr<NameserverEntry> nse(injectEntry());
- // Fill it with data
- nse->askIP(resolver_, nseCallback(), ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, nse->getState());
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
- EXPECT_NO_THROW(resolver_->answer(0, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1")));
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::1")));
- EXPECT_EQ(Fetchable::READY, nse->getState());
- checkInjected(false);
- }
- /// \short Test how the zone reacts to a nameserver in not asked state
- TEST_F(ZoneEntryTest, NameserverEntryNotAsked) {
- // Inject the entry
- injectEntry();
- // We do not need it, nothing to modify on it
- checkInjected(true);
- }
- /// \short What if the zone finds a nameserver in progress?
- TEST_F(ZoneEntryTest, NameserverEntryInProgress) {
- // Prepare the nameserver entry
- boost::shared_ptr<NameserverEntry> nse(injectEntry());
- nse->askIP(resolver_, nseCallback(), ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, nse->getState());
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
- checkInjected(true);
- }
- /// \short Check Zone's reaction to found expired nameserver
- TEST_F(ZoneEntryTest, NameserverEntryExpired) {
- boost::shared_ptr<NameserverEntry> nse(injectEntry());
- nse->askIP(resolver_, nseCallback(), ANY_OK);
- EXPECT_EQ(Fetchable::IN_PROGRESS, nse->getState());
- EXPECT_TRUE(resolver_->asksIPs(ns_name_, 0, 1));
- EXPECT_NO_THROW(resolver_->answer(0, ns_name_, RRType::A(),
- rdata::in::A("192.0.2.1"), 0));
- EXPECT_NO_THROW(resolver_->answer(1, ns_name_, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::1"), 0));
- EXPECT_EQ(Fetchable::READY, nse->getState());
- NameserverEntry::AddressVector addresses;
- EXPECT_EQ(Fetchable::EXPIRED, nse->getAddresses(addresses));
- EXPECT_EQ(Fetchable::EXPIRED, nse->getState());
- resolver_->requests.clear();
- checkInjected(true);
- }
- /// \short Check how it reacts to an unreachable zone already in the table
- TEST_F(ZoneEntryTest, NameserverEntryUnreachable) {
- boost::shared_ptr<NameserverEntry> nse(injectEntry());
- nse->askIP(resolver_, nseCallback(), ANY_OK);
- ASSERT_EQ(2, resolver_->requests.size());
- resolver_->requests[0].second->failure();
- resolver_->requests[1].second->failure();
- EXPECT_EQ(Fetchable::UNREACHABLE, nse->getState());
- checkInjected(false, 0, 1);
- }
- //@}
- // Count hits of each address
- void
- countHits(size_t *counts, const vector<NameserverAddress>& successes) {
- BOOST_FOREACH(const NameserverAddress& address, successes) {
- // We use the last digit as an index
- string address_string(address.getAddress().toText());
- size_t index(address_string[address_string.size() - 1] - '0' - 1);
- ASSERT_LT(index, 3);
- counts[index] ++;
- }
- }
- // Select one address from the address list
- TEST_F(ZoneEntryTest, AddressSelection) {
- const size_t repeats = 100000;
- // Create the zone, give it 2 nameservers and total of 3 addresses
- // (one of them is ipv6)
- boost::shared_ptr<ZoneEntry> zone(getZone());
- zone->addCallback(callback_, ANY_OK);
- EXPECT_NO_THROW(resolver_->provideNS(0, rrns_));
- ASSERT_GT(resolver_->requests.size(), 1);
- Name name1(resolver_->requests[1].first->getName());
- EXPECT_TRUE(resolver_->asksIPs(name1, 1, 2));
- resolver_->answer(1, name1, RRType::A(), rdata::in::A("192.0.2.1"));
- resolver_->answer(2, name1, RRType::AAAA(),
- rdata::in::AAAA("2001:db8::2"));
- ASSERT_GT(resolver_->requests.size(), 3);
- Name name2(resolver_->requests[3].first->getName());
- EXPECT_TRUE(resolver_->asksIPs(name2, 3, 4));
- resolver_->answer(3, name2, RRType::A(), rdata::in::A("192.0.2.3"));
- resolver_->requests[4].second->failure();
- boost::shared_ptr<NameserverEntry> ns1(nameserver_table_->get(HashKey(
- name1.toText(), RRClass::IN()))),
- ns2(nameserver_table_->get(HashKey(name2.toText(), RRClass::IN())));
- size_t counts[3] = {0, 0, 0};
- callback_->successes_.clear();
- // Test they have the same probabilities when they have the same RTT
- for (size_t i(0); i < repeats; ++ i) {
- zone->addCallback(callback_, ANY_OK);
- }
- countHits(counts, callback_->successes_);
- // We repeat the simulation for N=repeats times
- // for each address, the probability is p = 1/3, the average mu = N*p
- // variance sigma^2 = N * p * (1-p) = N * 1/3 * 2/3 = N*2/9
- // sigma = sqrt(N*2/9)
- // we should make sure that mu - 4sigma < c < mu + 4sigma
- // which means for 99.99366% of the time this should be true
- double p = 1.0 / 3.0;
- double mu = repeats * p;
- double sigma = sqrt(repeats * p * (1 - p));
- for (size_t i(0); i < 3; ++ i) {
- ASSERT_TRUE(fabs(counts[i] - mu) < 4*sigma);
- }
- // reset the environment
- callback_->successes_.clear();
- counts[0] = counts[1] = counts[2] = 0;
- // Test when the RTT is not the same
- ns1->setAddressRTT(IOAddress("192.0.2.1"), 1);
- ns1->setAddressRTT(IOAddress("2001:db8::2"), 2);
- ns2->setAddressRTT(IOAddress("192.0.2.3"), 3);
- for (size_t i(0); i < repeats; ++ i) {
- zone->addCallback(callback_, ANY_OK);
- }
- countHits(counts, callback_->successes_);
- // We expect that the selection probability for each address that
- // it will be in the range of [mu-4Sigma, mu+4Sigma]
- double ps[3];
- ps[0] = 1.0/(1.0 + 1.0/4.0 + 1.0/9.0);
- ps[1] = (1.0/4.0)/(1.0 + 1.0/4.0 + 1.0/9.0);
- ps[2] = (1.0/9.0)/(1.0 + 1.0/4.0 + 1.0/9.0);
- for (size_t i(0); i < 3; ++ i) {
- double mu = repeats * ps[i];
- double sigma = sqrt(repeats * ps[i] * (1 - ps[i]));
- ASSERT_TRUE(fabs(counts[i] - mu) < 4 * sigma);
- }
- // reset the environment
- callback_->successes_.clear();
- counts[0] = counts[1] = counts[2] = 0;
- // Test with unreachable address
- ns1->setAddressRTT(IOAddress("192.0.2.1"), 1);
- ns1->setAddressRTT(IOAddress("2001:db8::2"), 100);
- ns2->setAddressUnreachable(IOAddress("192.0.2.3"));
- for (size_t i(0); i < repeats; ++ i) {
- zone->addCallback(callback_, ANY_OK);
- }
- countHits(counts, callback_->successes_);
- // The unreachable one shouldn't be called
- EXPECT_EQ(0, counts[2]);
- // reset the environment
- callback_->successes_.clear();
- counts[0] = counts[1] = counts[2] = 0;
- // Test with all unreachable
- ns1->setAddressUnreachable(IOAddress("192.0.2.1"));
- ns1->setAddressUnreachable(IOAddress("2001:db8::2"));
- ns2->setAddressUnreachable(IOAddress("192.0.2.3"));
- for (size_t i(0); i < repeats; ++ i) {
- zone->addCallback(callback_, ANY_OK);
- }
- countHits(counts, callback_->successes_);
- // They should have about the same probability
- for (size_t i(0); i < 3; ++ i) {
- ASSERT_TRUE(fabs(counts[i] - mu) < 4*sigma);
- }
- // TODO: The unreachable server should be changed to reachable after 5minutes, but how to test?
- }
- } // namespace
|