[svn] commit: r3815 - in /branches/trac356: ./ doc/ src/lib/nsas/ src/lib/nsas/tests/
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Dec 13 15:25:10 UTC 2010
Author: vorner
Date: Mon Dec 13 15:25:09 2010
New Revision: 3815
Log:
Merge #408 (NSAS Logic)
Added:
branches/trac356/src/lib/nsas/TODO
- copied unchanged from r3814, branches/trac408/src/lib/nsas/TODO
branches/trac356/src/lib/nsas/fetchable.h
- copied unchanged from r3814, branches/trac408/src/lib/nsas/fetchable.h
branches/trac356/src/lib/nsas/nameserver_address.cc
- copied unchanged from r3814, branches/trac408/src/lib/nsas/nameserver_address.cc
branches/trac356/src/lib/nsas/resolver_interface.h
- copied unchanged from r3814, branches/trac408/src/lib/nsas/resolver_interface.h
branches/trac356/src/lib/nsas/tests/fetchable_unittest.cc
- copied unchanged from r3814, branches/trac408/src/lib/nsas/tests/fetchable_unittest.cc
branches/trac356/src/lib/nsas/zone_entry.cc
- copied unchanged from r3814, branches/trac408/src/lib/nsas/zone_entry.cc
Modified:
branches/trac356/ (props changed)
branches/trac356/doc/Doxyfile
branches/trac356/src/lib/nsas/Makefile.am
branches/trac356/src/lib/nsas/address_request_callback.h
branches/trac356/src/lib/nsas/asiolink.h
branches/trac356/src/lib/nsas/hash.cc
branches/trac356/src/lib/nsas/hash.h
branches/trac356/src/lib/nsas/hash_key.cc
branches/trac356/src/lib/nsas/hash_key.h
branches/trac356/src/lib/nsas/hash_table.h
branches/trac356/src/lib/nsas/lru_list.h
branches/trac356/src/lib/nsas/nameserver_address.h
branches/trac356/src/lib/nsas/nameserver_address_store.cc
branches/trac356/src/lib/nsas/nameserver_address_store.h
branches/trac356/src/lib/nsas/nameserver_entry.cc
branches/trac356/src/lib/nsas/nameserver_entry.h
branches/trac356/src/lib/nsas/nsas_entry.h
branches/trac356/src/lib/nsas/nsas_types.h
branches/trac356/src/lib/nsas/random_number_generator.h
branches/trac356/src/lib/nsas/tests/Makefile.am
branches/trac356/src/lib/nsas/tests/address_entry_unittest.cc
branches/trac356/src/lib/nsas/tests/hash_deleter_unittest.cc
branches/trac356/src/lib/nsas/tests/hash_key_unittest.cc
branches/trac356/src/lib/nsas/tests/hash_table_unittest.cc
branches/trac356/src/lib/nsas/tests/hash_unittest.cc
branches/trac356/src/lib/nsas/tests/lru_list_unittest.cc
branches/trac356/src/lib/nsas/tests/nameserver_address_store_unittest.cc
branches/trac356/src/lib/nsas/tests/nameserver_address_unittest.cc
branches/trac356/src/lib/nsas/tests/nameserver_entry_unittest.cc
branches/trac356/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
branches/trac356/src/lib/nsas/tests/nsas_test.h
branches/trac356/src/lib/nsas/tests/zone_entry_unittest.cc
branches/trac356/src/lib/nsas/zone_entry.h
Modified: branches/trac356/doc/Doxyfile
==============================================================================
--- branches/trac356/doc/Doxyfile (original)
+++ branches/trac356/doc/Doxyfile Mon Dec 13 15:25:09 2010
@@ -568,7 +568,7 @@
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench
+INPUT = ../src/lib/cc ../src/lib/config ../src/lib/dns ../src/lib/exceptions ../src/lib/datasrc ../src/bin/auth ../src/lib/bench ../src/lib/nsas
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
Modified: branches/trac356/src/lib/nsas/Makefile.am
==============================================================================
--- branches/trac356/src/lib/nsas/Makefile.am (original)
+++ branches/trac356/src/lib/nsas/Makefile.am Mon Dec 13 15:25:09 2010
@@ -15,11 +15,14 @@
libnsas_la_SOURCES += hash_table.h
libnsas_la_SOURCES += lru_list.h
libnsas_la_SOURCES += nameserver_address_store.cc nameserver_address_store.h
-libnsas_la_SOURCES += nameserver_address.h
+libnsas_la_SOURCES += nameserver_address.h nameserver_address.cc
libnsas_la_SOURCES += nameserver_entry.cc nameserver_entry.h
libnsas_la_SOURCES += nsas_entry_compare.h
-libnsas_la_SOURCES += nsas_entry.h
+libnsas_la_SOURCES += nsas_entry.h nsas_types.h
+libnsas_la_SOURCES += zone_entry.cc zone_entry.h
+libnsas_la_SOURCES += fetchable.h
+libnsas_la_SOURCES += address_request_callback.h
+libnsas_la_SOURCES += resolver_interface.h
libnsas_la_SOURCES += random_number_generator.h
-libnsas_la_SOURCES += zone_entry.h
CLEANFILES = *.gcno *.gcda
Modified: branches/trac356/src/lib/nsas/address_request_callback.h
==============================================================================
--- branches/trac356/src/lib/nsas/address_request_callback.h (original)
+++ branches/trac356/src/lib/nsas/address_request_callback.h Mon Dec 13 15:25:09 2010
@@ -18,6 +18,7 @@
#define __ADDRESS_REQUEST_CALLBACK_H
#include "asiolink.h"
+#include "nameserver_address.h"
namespace isc {
namespace nsas {
@@ -55,7 +56,7 @@
/// This method is used when an address has been retrieved for the request.
///
/// \param address Address to be used to access the nameserver.
- virtual void success(const asiolink::IOAddress& address) = 0;
+ virtual void success(const NameserverAddress& address) = 0;
/// \brief Unreachable
///
Modified: branches/trac356/src/lib/nsas/asiolink.h
==============================================================================
--- branches/trac356/src/lib/nsas/asiolink.h (original)
+++ branches/trac356/src/lib/nsas/asiolink.h Mon Dec 13 15:25:09 2010
@@ -35,6 +35,9 @@
IOAddress(const std::string& address_str) : address_(address_str)
{}
+ /// \param Just a virtual destructor
+ virtual ~ IOAddress() { }
+
/// \return Textual representation of the address
std::string toText() const
{return address_;}
Modified: branches/trac356/src/lib/nsas/hash.cc
==============================================================================
--- branches/trac356/src/lib/nsas/hash.cc (original)
+++ branches/trac356/src/lib/nsas/hash.cc Mon Dec 13 15:25:09 2010
@@ -60,7 +60,7 @@
#include <cassert>
#include <string>
-#include "config.h"
+#include <config.h>
#include "hash.h"
@@ -72,7 +72,8 @@
// Constructor.
Hash::Hash(uint32_t tablesize, uint32_t maxkeylen, bool randomise) :
- tablesize_(tablesize), maxkeylen_(min(maxkeylen, (255 - sizeof(uint16_t))))
+ tablesize_(tablesize), maxkeylen_(min<uint32_t>(maxkeylen,
+ (255 - sizeof(uint16_t))))
{
// (Code taken from BIND-9)
//
@@ -150,7 +151,7 @@
char bytes[sizeof(uint16_t)]; // Byte equivalent
} convert;
- convert.class_code = key.class_code;
+ convert.class_code = key.class_code.getCode();
for (int j = 0; j < sizeof(uint16_t); ++j, ++i) {
partial_sum += convert.bytes[j] * randvec_[i];
}
Modified: branches/trac356/src/lib/nsas/hash.h
==============================================================================
--- branches/trac356/src/lib/nsas/hash.h (original)
+++ branches/trac356/src/lib/nsas/hash.h Mon Dec 13 15:25:09 2010
@@ -20,7 +20,7 @@
#include <stdint.h>
#include <vector>
-#include "exceptions/exceptions.h"
+#include <exceptions/exceptions.h>
#include "hash_key.h"
Modified: branches/trac356/src/lib/nsas/hash_key.cc
==============================================================================
--- branches/trac356/src/lib/nsas/hash_key.cc (original)
+++ branches/trac356/src/lib/nsas/hash_key.cc Mon Dec 13 15:25:09 2010
@@ -14,9 +14,9 @@
// $Id$
-#include <string.h>
+#include <cstring>
-#include "config.h"
+#include <config.h>
#include "hash_key.h"
namespace isc {
Modified: branches/trac356/src/lib/nsas/hash_key.h
==============================================================================
--- branches/trac356/src/lib/nsas/hash_key.h (original)
+++ branches/trac356/src/lib/nsas/hash_key.h Mon Dec 13 15:25:09 2010
@@ -17,9 +17,11 @@
#ifndef __HASH_KEY_H
#define __HASH_KEY_H
+#include <dns/rrclass.h>
+
#include <stdint.h>
#include <string>
-#include "config.h"
+#include <config.h>
namespace isc {
namespace nsas {
@@ -51,8 +53,11 @@
/// \param the_key Array of bytes for which key is to be constructed
/// \param the_keylen Length of the byte array
/// \param the_class_code Class of this entry
- HashKey(const char* the_key, uint32_t the_keylen, uint16_t the_class_code) :
- key(the_key), keylen(the_keylen), class_code(the_class_code)
+ HashKey(const char* the_key, uint32_t the_keylen,
+ const isc::dns::RRClass& the_class_code) :
+ key(the_key),
+ keylen(the_keylen),
+ class_code(the_class_code)
{}
/// \brief String Constructor
@@ -61,8 +66,11 @@
///
/// \param the_key Name to use as the key for the hash
/// \param the_class_code Class of this entry
- HashKey(const std::string& the_key, uint16_t the_class_code) :
- key(the_key.c_str()), keylen(the_key.size()), class_code(the_class_code)
+ HashKey(const std::string& the_key,
+ const isc::dns::RRClass& the_class_code) :
+ key(the_key.c_str()),
+ keylen(the_key.size()),
+ class_code(the_class_code)
{}
/// \brief Equality
@@ -82,7 +90,7 @@
const char* key; ///< Pointer to the start of the key string
uint32_t keylen; ///< Length of the key string
- uint16_t class_code; ///< Class associated with the key
+ isc::dns::RRClass class_code; ///< Class associated with the key
};
} // namespace nsas
Modified: branches/trac356/src/lib/nsas/hash_table.h
==============================================================================
--- branches/trac356/src/lib/nsas/hash_table.h (original)
+++ branches/trac356/src/lib/nsas/hash_table.h Mon Dec 13 15:25:09 2010
@@ -24,7 +24,7 @@
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
#include <list>
-#include "config.h"
+#include <config.h>
#include "hash.h"
#include "hash_key.h"
@@ -160,8 +160,12 @@
/// \param key Name of the object (and class). The hash of this is
/// calculated and used to index the table.
///
- /// \return Shared pointer to the object.
- virtual boost::shared_ptr<T> get(const HashKey& key);
+ /// \return Shared pointer to the object or NULL if it is not there.
+ boost::shared_ptr<T> get(const HashKey& key) {
+ uint32_t index = hash_(key);
+ sharable_lock lock(table_[index].mutex_);
+ return getInternal(key, index);
+ }
/// \brief Remove Entry
///
@@ -173,7 +177,7 @@
/// calculated and used to index the table.
///
/// \return true if the object was deleted, false if it was not found.
- virtual bool remove(const HashKey& key);
+ bool remove(const HashKey& key);
/// \brief Add Entry
///
@@ -189,15 +193,66 @@
/// same name already exists, the existing object is replaced. If false,
// the addition fails and a status is returned.
/// \return true if the object was successfully added, false otherwise.
- virtual bool add(boost::shared_ptr<T>& object, const HashKey& key,
- bool replace = false);
+ bool add(boost::shared_ptr<T>& object, const HashKey& key,
+ bool replace = false)
+ {
+ uint32_t index = hash_(key);
+ scoped_lock lock(table_[index].mutex_);
+ return addInternal(object, key, index, replace);
+ }
+
+ /**
+ * \brief Attomicly lookup an entry or add a new one if it does not exist.
+ *
+ * Looks up an entry specified by key in the table. If it is not there,
+ * it calls generator() and adds its result to the table under given key.
+ * It is performed attomically to prevent race conditions.
+ *
+ * \param key The entry to lookup.
+ * \param generator will be called when the item is not there. Its result
+ * will be added and returned. The generator should return as soon
+ * as possible, the slot is locked during its execution.
+ * \return The boolean part of pair tells if the value was added (true
+ * means new value, false looked up one). The other part is the
+ * object, either found or created.
+ * \todo This uses a scoped_lock, which does not allow sharing and is
+ * used a lot in the code. It might turn out in future that it is a
+ * problem and that most of the accesses is read only. In that case we
+ * could split it to fast-slow path - first try to find it with
+ * shared_lock. If it fails, lock by scoped_lock, try to find again (we
+ * unlocked it, so it might have appeared) and if it still isn't there,
+ * create it. Not implemented now as it might or might not help (it
+ * could even slow it down) and the code would get more complicated.
+ */
+ template<class Generator>
+ std::pair<bool, boost::shared_ptr<T> > getOrAdd(const HashKey& key,
+ const Generator& generator)
+ {
+ uint32_t index = hash_(key);
+ scoped_lock lock(table_[index].mutex_);
+ boost::shared_ptr<T> result(getInternal(key, index));
+ if (result) {
+ return (std::pair<bool, boost::shared_ptr<T> >(false, result));
+ } else {
+ result = generator();
+ addInternal(result, key, index);
+ return (std::pair<bool, boost::shared_ptr<T> >(true, result));
+ }
+ }
/// \brief Returns Size of Hash Table
///
/// \return Size of hash table
- virtual uint32_t tableSize() const {
+ uint32_t tableSize() const {
return table_.size();
}
+
+protected:
+ // Internal parts, expect to be already locked
+ boost::shared_ptr<T> getInternal(const HashKey& key,
+ uint32_t index);
+ bool addInternal(boost::shared_ptr<T>& object, const HashKey& key,
+ uint32_t index, bool replace = false);
private:
Hash hash_; ///< Hashing function
@@ -214,15 +269,9 @@
// Lookup an object in the table
template <typename T>
-boost::shared_ptr<T> HashTable<T>::get(const HashKey& key) {
-
- // Calculate the hash value
- uint32_t index = hash_(key);
-
- // Take out a read lock on this hash slot. The lock is released when this
- // object goes out of scope.
- sharable_lock lock(table_[index].mutex_);
-
+boost::shared_ptr<T> HashTable<T>::getInternal(const HashKey& key,
+ uint32_t index)
+{
// Locate the object.
typename HashTableSlot<T>::iterator i;
for (i = table_[index].list_.begin(); i != table_[index].list_.end(); ++i) {
@@ -267,17 +316,10 @@
// Add an entry to the hash table
template <typename T>
-bool HashTable<T>::add(boost::shared_ptr<T>& object, const HashKey& key,
- bool replace)
+bool HashTable<T>::addInternal(boost::shared_ptr<T>& object,
+ const HashKey& key, uint32_t index, bool replace)
{
-
- // Calculate the hash value
- uint32_t index = hash_(key);
-
- // Access to the elements of this hash slot are accessed under a mutex.
- scoped_lock lock(table_[index].mutex_);
-
- // Now search this list to see if the element already exists.
+ // Search this list to see if the element already exists.
typename HashTableSlot<T>::iterator i;
for (i = table_[index].list_.begin(); i != table_[index].list_.end(); ++i) {
if ((*compare_)(i->get(), key)) {
Modified: branches/trac356/src/lib/nsas/lru_list.h
==============================================================================
--- branches/trac356/src/lib/nsas/lru_list.h (original)
+++ branches/trac356/src/lib/nsas/lru_list.h Mon Dec 13 15:25:09 2010
@@ -25,8 +25,7 @@
#include <boost/thread.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
-#include "config.h"
-
+#include <config.h>
namespace isc {
namespace nsas {
Modified: branches/trac356/src/lib/nsas/nameserver_address.h
==============================================================================
--- branches/trac356/src/lib/nsas/nameserver_address.h (original)
+++ branches/trac356/src/lib/nsas/nameserver_address.h Mon Dec 13 15:25:09 2010
@@ -19,11 +19,17 @@
#include <boost/shared_ptr.hpp>
+#include <exceptions/exceptions.h>
+
#include "asiolink.h"
-#include "nameserver_entry.h"
+#include "address_entry.h"
+#include "nsas_types.h"
namespace isc {
namespace nsas {
+
+class ZoneEntry;
+class NameserverEntry;
/// \brief Empty \c NameserverEntry pointer exception
///
@@ -31,7 +37,8 @@
/// into \c NameserverAddress' constructor is NULL
class NullNameserverEntryPointer : public isc::Exception {
public:
- NullNameserverEntryPointer(const char* file, size_t line, const char* what) :
+ NullNameserverEntryPointer(const char* file, size_t line,
+ const char* what) :
isc::Exception(file, line, what)
{}
};
@@ -39,49 +46,43 @@
/// \brief Nameserver Address
///
/// This class implements the object that returned from NSAS when the resolver
-/// request an address for the name server. It contains one IOAddress object
+/// request an address for the name server. It contains one address
/// that can be used by resolver. When the resolver get query back from the name
-/// server, it should update the name server's RTT(Round Trip Time) with this
+/// server, it should update the name server's RTT(Round Trip Time) with this
/// object.
+///
+/// It is not thread safe, only reentrant. It is expected to be kept inside
+/// the resolver and used only once for the address and once for the update.
class NameserverAddress {
public:
/// \brief Constructor
///
/// The NameserverAddress object will contain one shared_ptr object that
- /// pointed to NameserverEntry which contains the address as well as it's
+ /// pointed to NameserverEntry which contains the address as well as it's
/// corresponding index. The user can update it's RTT with the index later.
///
/// \param namerserver A shared_ptr that points to a NameserverEntry object
/// the shared_ptr can avoid the NameserverEntry object being dropped while the
/// request is processing.
/// \param index The address's index in NameserverEntry's addresses vector
- /// \param family Address family, AF_INET or AF_INET6
- NameserverAddress(boost::shared_ptr<NameserverEntry>& nameserver, uint32_t index, short family):
- ns_(nameserver), index_(index), family_(family)
+ /// \param family Address family, V4_ONLY or V6_ONLY
+ NameserverAddress(const boost::shared_ptr<NameserverEntry>& nameserver,
+ const AddressEntry& address, AddressFamily family):
+ ns_(nameserver), address_(address), family_(family)
{
- if(!ns_.get()) {
+ if(!ns_) {
isc_throw(NullNameserverEntryPointer, "NULL NameserverEntry pointer.");
}
}
/// \brief Default Constructor
- ///
- NameserverAddress(): index_(0), family_(AF_INET)
- {
- }
-
- /// \brief Destructor
- ///
- /// Empty destructor.
- ~NameserverAddress()
- {
- }
+ NameserverAddress() : address_(asiolink::IOAddress("::1")) { }
/// \brief Return address
///
- asiolink::IOAddress getAddress() const {
- return ns_.get()->getAddressAtIndex(index_, family_);
+ asiolink::IOAddress getAddress() const {
+ return (address_.getAddress());
}
/// \brief Update Round-trip Time
@@ -89,14 +90,27 @@
/// When the user get one request back from the name server, it should
/// update the address's RTT.
/// \param rtt The new Round-Trip Time
- void updateRTT(uint32_t rtt) {
- ns_.get()->updateAddressRTTAtIndex(rtt, index_, family_);
+ void updateRTT(uint32_t rtt) const;
+
+ /// Short access to the AddressEntry inside.
+ //@{
+ const AddressEntry& getAddressEntry() const {
+ return (address_);
}
+ AddressEntry& getAddressEntry() {
+ return (address_);
+ }
+ //@}
private:
+ /*
+ * Note: Previous implementation used index into the entry. That is wrong,
+ * as the list of addresses may change. Thil would cause setting a
+ * different address or a crash.
+ */
boost::shared_ptr<NameserverEntry> ns_; ///< Shared-pointer to NameserverEntry object
- uint32_t index_; ///< The address index in NameserverEntry
- short family_; ///< Address family AF_INET or AF_INET6
+ AddressEntry address_; ///< The address
+ AddressFamily family_; ///< The address family (V4_ONLY or V6_ONLY)
};
} // namespace nsas
Modified: branches/trac356/src/lib/nsas/nameserver_address_store.cc
==============================================================================
--- branches/trac356/src/lib/nsas/nameserver_address_store.cc (original)
+++ branches/trac356/src/lib/nsas/nameserver_address_store.cc Mon Dec 13 15:25:09 2010
@@ -14,15 +14,25 @@
// $Id$
+#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
+#include <boost/foreach.hpp>
-#include "config.h"
+#include <config.h>
+#include <dns/rdataclass.h>
+#include "hash_table.h"
+#include "lru_list.h"
#include "hash_deleter.h"
#include "nsas_entry_compare.h"
#include "nameserver_entry.h"
#include "nameserver_address_store.h"
#include "zone_entry.h"
+#include "address_request_callback.h"
+
+using namespace isc::dns;
+using namespace std;
+using namespace boost;
namespace isc {
namespace nsas {
@@ -32,13 +42,54 @@
// The LRU lists are set equal to three times the size of the respective
// hash table, on the assumption that three elements is the longest linear
// search we want to do when looking up names in the hash table.
-NameserverAddressStore::NameserverAddressStore(uint32_t zonehashsize,
+NameserverAddressStore::NameserverAddressStore(
+ shared_ptr<ResolverInterface> resolver, uint32_t zonehashsize,
uint32_t nshashsize) :
- zone_hash_(new NsasEntryCompare<ZoneEntry>, zonehashsize),
- nameserver_hash_(new NsasEntryCompare<NameserverEntry>, nshashsize),
- zone_lru_((3 * zonehashsize), new HashDeleter<ZoneEntry>(zone_hash_)),
- nameserver_lru_((3 * nshashsize), new HashDeleter<NameserverEntry>(nameserver_hash_))
+ zone_hash_(new HashTable<ZoneEntry>(new NsasEntryCompare<ZoneEntry>,
+ zonehashsize)),
+ nameserver_hash_(new HashTable<NameserverEntry>(
+ new NsasEntryCompare<NameserverEntry>, nshashsize)),
+ zone_lru_(new LruList<ZoneEntry>((3 * zonehashsize),
+ new HashDeleter<ZoneEntry>(*zone_hash_))),
+ nameserver_lru_(new LruList<NameserverEntry>((3 * nshashsize),
+ new HashDeleter<NameserverEntry>(*nameserver_hash_))),
+ resolver_(resolver)
+{ }
+
+namespace {
+
+/*
+ * We use pointers here so there's no call to any copy constructor.
+ * It is easier for the compiler to inline it and prove that there's
+ * no need to copy anything. In that case, the bind should not be
+ * called at all to create the object, just call the function.
+ */
+shared_ptr<ZoneEntry>
+newZone(const shared_ptr<ResolverInterface>* resolver, const string* zone,
+ const RRClass* class_code,
+ const shared_ptr<HashTable<NameserverEntry> >* ns_hash,
+ const shared_ptr<LruList<NameserverEntry> >* ns_lru)
{
+ shared_ptr<ZoneEntry> result(new ZoneEntry(*resolver, *zone, *class_code,
+ *ns_hash, *ns_lru));
+ return (result);
+}
+
+}
+
+void
+NameserverAddressStore::lookup(const string& zone, const RRClass& class_code,
+ shared_ptr<AddressRequestCallback> callback, AddressFamily family)
+{
+ pair<bool, shared_ptr<ZoneEntry> > zone_obj(zone_hash_->getOrAdd(HashKey(
+ zone, class_code), boost::bind(newZone, &resolver_, &zone, &class_code,
+ &nameserver_hash_, &nameserver_lru_)));
+ if (zone_obj.first) {
+ zone_lru_->add(zone_obj.second);
+ } else {
+ zone_lru_->touch(zone_obj.second);
+ }
+ zone_obj.second->addCallback(callback, family);
}
} // namespace nsas
Modified: branches/trac356/src/lib/nsas/nameserver_address_store.h
==============================================================================
--- branches/trac356/src/lib/nsas/nameserver_address_store.h (original)
+++ branches/trac356/src/lib/nsas/nameserver_address_store.h Mon Dec 13 15:25:09 2010
@@ -20,16 +20,25 @@
#include <string>
#include <vector>
-#include "rrset.h"
+#include <boost/shared_ptr.hpp>
-#include "address_request_callback.h"
-#include "hash_table.h"
-#include "nameserver_entry.h"
-#include "lru_list.h"
-#include "zone_entry.h"
+#include "nsas_types.h"
namespace isc {
+// Some forward declarations, so we do not need to include so many headers
+
+namespace dns {
+class RRClass;
+}
+
namespace nsas {
+
+class ResolverInterface;
+template<class T> class HashTable;
+template<class T> class LruList;
+class ZoneEntry;
+class NameserverEntry;
+class AddressRequestCallback;
/// \brief Nameserver Address Store
///
@@ -46,15 +55,17 @@
/// The constructor sizes all the tables. As there are various
/// relationships between the table sizes, and as some values are best as
/// prime numbers, the table sizes are determined by compile-time values.
- ///
+ ///
+ /// \param resolver Which resolver object (or resolver-like, in case of
+ /// tests) should it use to ask questions.
/// \param zonehashsize Size of the zone hash table. The default value of
/// 1009 is the first prime number above 1000.
/// \param nshash size Size of the nameserver hash table. The default
- /// value of 2003 is the first prime number over 2000, and by implication,
+ /// value of 3001 is the first prime number over 3000, and by implication,
/// there is an assumption that there will be more nameservers than zones
/// in the store.
- NameserverAddressStore(uint32_t zonehashsize = 1009,
- uint32_t nshashsize = 3001);
+ NameserverAddressStore(boost::shared_ptr<ResolverInterface> resolver,
+ uint32_t zonehashsize = 1009, uint32_t nshashsize = 3001);
/// \brief Destructor
///
@@ -67,15 +78,13 @@
/// Looks up the address of a nameserver in the zone.
///
/// \param zone Name of zone for which an address is required.
- /// \param authority Authority RRset from the referral containing the
- /// nameservers that serve the zone.
- /// \param additional Additional RRset(s) for authority information. These
- /// are taken from the referral.
+ /// \param class_code Class of the zone.
/// \param callback Callback object used to pass the result back to the
/// caller.
- /* void lookup(const std::string& zone, isc::dns::AbstractRRset& authority,
- const std::vector<isc::dns::AbstractRRset>& additional
- boost::shared_ptr<isc::dns::AddressRequestCallback> callback ); */
+ /// \param family Which address is requested.
+ void lookup(const std::string& zone, const dns::RRClass& class_code,
+ boost::shared_ptr<AddressRequestCallback> callback, AddressFamily
+ family = ANY_OK);
/// \brief Protected Members
///
@@ -90,12 +99,15 @@
//@{
protected:
// Zone and nameserver hash tables
- HashTable<ZoneEntry> zone_hash_;
- HashTable<NameserverEntry> nameserver_hash_;
+ boost::shared_ptr<HashTable<ZoneEntry> > zone_hash_;
+ boost::shared_ptr<HashTable<NameserverEntry> > nameserver_hash_;
// ... and the LRU lists
- LruList<ZoneEntry> zone_lru_;
- LruList<NameserverEntry> nameserver_lru_;
+ boost::shared_ptr<LruList<ZoneEntry> > zone_lru_;
+ boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
+ // The resolver we use
+private:
+ boost::shared_ptr<ResolverInterface> resolver_;
//}@
};
Modified: branches/trac356/src/lib/nsas/nameserver_entry.cc
==============================================================================
--- branches/trac356/src/lib/nsas/nameserver_entry.cc (original)
+++ branches/trac356/src/lib/nsas/nameserver_entry.cc Mon Dec 13 15:25:09 2010
@@ -18,20 +18,24 @@
#include <functional>
#include <cassert>
#include <iostream>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
#include <ctype.h>
#include <strings.h>
-#include "config.h"
-
-#include "exceptions/exceptions.h"
-#include "name.h"
-#include "rrclass.h"
-#include "rrttl.h"
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrttl.h>
+#include <dns/question.h>
#include "address_entry.h"
#include "nameserver_address.h"
#include "nameserver_entry.h"
+#include "resolver_interface.h"
using namespace asiolink;
using namespace isc::nsas;
@@ -42,246 +46,381 @@
namespace isc {
namespace nsas {
-// Constructor, initialized with the list of addresses associated with this
-// nameserver.
-NameserverEntry::NameserverEntry(const AbstractRRset* v4Set,
- const AbstractRRset* v6Set, time_t curtime) : expiration_(0)
+namespace {
+
+// Just shorter type alias
+typedef recursive_mutex::scoped_lock Lock;
+
+}
+
+// Returns the list of addresses matching the given family
+Fetchable::State
+NameserverEntry::getAddresses(AddressVector& addresses,
+ AddressFamily family, bool expired_ok)
{
- uint32_t rtt = 1; // Round-trip time for an address
- string v4name = ""; // Name from the V4 RRset
- string v6name = ""; // Name from the v6 RRset
- uint16_t v4class = 0; // Class of V4 RRset
- uint16_t v6class = 0; // Class for V6 RRset
-
- // Get the time for setting the expiration time.
- if (curtime == 0) {
- curtime = time(NULL);
- }
-
- // Add the v4 addresses to the list of addresses. Each address is assigned
- // a small RTT that ensures that each server is used at least once (in a
- // random order).
-
-
- // Do the V4 addresses first
- // XXX: Do we need to check that these are V4 addresses?
- if (v4Set) {
- RdataIteratorPtr i = v4Set->getRdataIterator();
- i->first();
- while (! i->isLast()) {
- v4_addresses_.push_back(AddressEntry(IOAddress(i->getCurrent().toText()),
- rtt));
- i->next();
- }
-
- // Set the expiration time and extract the owner name and class
- expiration_ = curtime + v4Set->getTTL().getValue();
- v4name = v4Set->getName().toText(false); // Ensure trailing dot
- v4class = v4Set->getClass().getCode();
-
- // Update the address selector
- updateAddressSelector(v4_addresses_, v4_address_selector_);
- }
-
- // Now the v6 addresses
- // XXX: Do we need to check that these are V6 addresses?
- if (v6Set) {
- RdataIteratorPtr i = v6Set->getRdataIterator();
- i->first();
- while (! i->isLast()) {
- v6_addresses_.push_back(AddressEntry(IOAddress(i->getCurrent().toText()),
- rtt));
- i->next();
- }
-
- // Update the expiration time of the data
- time_t v6expiration = curtime + v6Set->getTTL().getValue();
- if (expiration_ == 0) {
- expiration_ = v6expiration;
- }
- else {
- expiration_ = min(expiration_, v6expiration);
- }
-
- // Extract the name of the v6 set and its class
- v6name = v6Set->getName().toText(false); // Ensure trailing dot
- v6class = v6Set->getClass().getCode();
-
- // Update the address selector
- updateAddressSelector(v6_addresses_, v6_address_selector_);
- }
-
- // TODO: Log a problem if both V4 and V6 address were null.
-
- if (v4Set && v6Set) {
-
- // If two owner names were specified and they were different, something
- // has gone wrong with the logic that created this object.
- if (strcasecmp(v4name.c_str(), v6name.c_str()) != 0) {
- isc_throw(InconsistentOwnerNames,
- "Owner names for NameserverEntry RRsets are different");
- }
-
- // Likewise with the class
- if (v4class != v6class) {
- isc_throw(InconsistentClass,
- "Class codes for NameserverEntry RRsets are different");
- }
- }
-
- // Otherwise set the owner name
- name_ = v4Set ? v4name : v6name;
- classCode_ = v4Set ? v4class : v6class;
-}
-
-// Returns the list of addresses matching the given family
-void NameserverEntry::getAddresses(AddressVector& addresses, short family) const {
-
- // Quick check that allows validation of the assumption in the header file
- // that a family value of 0 will select all address families.
- assert(AF_INET != 0);
- assert(AF_INET6 != 0);
-
- // Now copy all entries that meet the criteria. Since remove_copy_if
- // does the inverse (copies all entries that do not meet the criteria),
- // the predicate for address selection is negated.
- remove_copy_if(v4_addresses_.begin(), v4_addresses_.end(), back_inserter(addresses),
- bind1st(AddressSelection(), family));
- remove_copy_if(v6_addresses_.begin(), v6_addresses_.end(), back_inserter(addresses),
- bind1st(AddressSelection(), family));
-}
-
-// Return one address matching the given family
-bool NameserverEntry::getAddress(NameserverAddress& address, short family)
+ Lock lock(mutex_);
+
+ // Check TTL
+ time_t now(time(NULL));
+ // We take = as well, so we catch TTL 0 correctly
+ // expiration_ == 0 means not set, the reason is we are UNREACHABLE or
+ // NOT_ASKED or IN_PROGRESS
+ if (getState() != NOT_ASKED && expiration_ && expiration_ <= now) {
+ setState(EXPIRED);
+ }
+
+ if (getState() == EXPIRED && !expired_ok) {
+ return EXPIRED;
+ }
+
+ switch (getState()) {
+ case IN_PROGRESS:
+ /*
+ * Did we receive the address already?
+ *
+ * We might have already received the addresses for this family
+ * and still wait for the other (in which case has_address_[family]
+ * will be true). We might already received a negative answer,
+ * in which case expect_address_[family] is false and
+ * has_address_[family] is false as well.
+ */
+ if (!has_address_[family] && expect_address_[family]) {
+ return IN_PROGRESS;
+ }
+ // If we do not expect the address, then fall trough to READY
+ case EXPIRED: // If expired_ok, we pretend to be ready
+ case READY:
+ if (!has_address_[family]) {
+ return UNREACHABLE;
+ }
+ break; // OK, we give some answers
+ case NOT_ASKED:
+ case UNREACHABLE:
+ // Reject giving any data
+ return (getState());
+ }
+
+ shared_ptr<NameserverEntry> self(shared_from_this());
+ // If any address is OK, just pass everything we have
+ if (family == ANY_OK) {
+ BOOST_FOREACH(const AddressEntry& entry, addresses_[V6_ONLY]) {
+ addresses.push_back(NameserverAddress(self, entry, V6_ONLY));
+ }
+ BOOST_FOREACH(const AddressEntry& entry, addresses_[V4_ONLY]) {
+ addresses.push_back(NameserverAddress(self, entry, V4_ONLY));
+ }
+ } else {
+ BOOST_FOREACH(const AddressEntry& entry, addresses_[family]) {
+ addresses.push_back(NameserverAddress(self, entry, family));
+ }
+ }
+ if (getState() == EXPIRED && expired_ok) {
+ return READY;
+ }
+ return getState();
+}
+
+// Return the address corresponding to the family
+asiolink::IOAddress
+NameserverEntry::getAddressAtIndex(size_t index, AddressFamily family) const {
+ Lock lock(mutex_);
+
+ assert(index < addresses_[family].size());
+
+ return (addresses_[family][index].getAddress());
+}
+
+// Set the address RTT to a specific value
+void
+NameserverEntry::setAddressRTT(const IOAddress& address, uint32_t rtt) {
+ Lock lock(mutex_);
+
+ // Search through the list of addresses for a match
+ AddressFamily family(V4_ONLY);
+ for (;;) {
+ BOOST_FOREACH(AddressEntry& entry, addresses_[family]) {
+ if (entry.getAddress().equal(address)) {
+ entry.setRTT(rtt);
+ return;
+ }
+ }
+
+ // Hack. C++ does not allow ++ on enums, enumerating trough them is pain
+ switch (family) {
+ case V4_ONLY: family = V6_ONLY; break;
+ default: return;
+ }
+ }
+}
+
+// Update the address's rtt
+#define UPDATE_RTT_ALPHA 0.7
+void
+NameserverEntry::updateAddressRTTAtIndex(uint32_t rtt, size_t index,
+ AddressFamily family)
{
- // Get the shared_ptr object that point to "this" object
- shared_ptr<NameserverEntry> shared_ptr_to_this = shared_from_this();
-
- if(family == AF_INET){
- if(v4_addresses_.size() == 0) return false;
-
- address = NameserverAddress(shared_ptr_to_this, v4_address_selector_(), AF_INET);
- return true;
- } else if(family == AF_INET6){
- if(v6_addresses_.size() == 0) return false;
-
- //address = NameserverAddress(shared_from_this(), v6_address_selector_(), AF_INET6);
- return true;
- }
- return false;
-}
-
-// Return the address corresponding to the family
-asiolink::IOAddress NameserverEntry::getAddressAtIndex(uint32_t index, short family) const
-{
- const vector<AddressEntry> *addresses = &v4_addresses_;
- if(family == AF_INET6){
- addresses = &v6_addresses_;
- }
- assert(index < addresses->size());
-
- return (*addresses)[index].getAddress();
-}
-
-// Set the address RTT to a specific value
-void NameserverEntry::setAddressRTT(const IOAddress& address, uint32_t rtt) {
-
- // Search through the list of addresses for a match
- for (AddressVectorIterator i = v4_addresses_.begin(); i != v4_addresses_.end(); ++i) {
- if (i->getAddress().equal(address)) {
- i->setRTT(rtt);
-
- // Update the selector
- updateAddressSelector(v4_addresses_, v4_address_selector_);
- return;
- }
- }
-
- // Search the v6 list
- for (AddressVectorIterator i = v6_addresses_.begin(); i != v6_addresses_.end(); ++i) {
- if (i->getAddress().equal(address)) {
- i->setRTT(rtt);
-
- // Update the selector
- updateAddressSelector(v6_addresses_, v6_address_selector_);
- return;
- }
- }
-}
-
-// Update the address's rtt
-#define UPDATE_RTT_ALPHA 0.7
-void NameserverEntry::updateAddressRTTAtIndex(uint32_t rtt, uint32_t index, short family) {
- vector<AddressEntry>* addresses = &v4_addresses_;
- if(family == AF_INET6){
- addresses = &v6_addresses_;
- }
+ Lock lock(mutex_);
//make sure it is a valid index
- if(index >= addresses->size()) return;
+ if(index >= addresses_[family].size()) return;
// Smoothly update the rtt
// The algorithm is as the same as bind8/bind9:
// new_rtt = old_rtt * alpha + new_rtt * (1 - alpha), where alpha is a float number in [0, 1.0]
// The default value for alpha is 0.7
- uint32_t old_rtt = (*addresses)[index].getRTT();
- uint32_t new_rtt = (int)(old_rtt * UPDATE_RTT_ALPHA + rtt * (1 - UPDATE_RTT_ALPHA));
- (*addresses)[index].setRTT(new_rtt);
-
- // Update the selector
- if(family == AF_INET) {
- updateAddressSelector(v4_addresses_, v4_address_selector_);
- } else if(family == AF_INET6) {
- updateAddressSelector(v6_addresses_, v6_address_selector_);
+ uint32_t old_rtt = addresses_[family][index].getRTT();
+ uint32_t new_rtt = (uint32_t)(old_rtt * UPDATE_RTT_ALPHA + rtt *
+ (1 - UPDATE_RTT_ALPHA));
+ addresses_[family][index].setRTT(new_rtt);
+}
+
+void
+NameserverEntry::updateAddressRTT(uint32_t rtt,
+ const asiolink::IOAddress& address, AddressFamily family)
+{
+ Lock lock(mutex_);
+ for (size_t i(0); i < addresses_[family].size(); ++ i) {
+ if (addresses_[family][i].getAddress().equal(address)) {
+ updateAddressRTTAtIndex(rtt, i, family);
+ return;
+ }
}
}
// Sets the address to be unreachable
-void NameserverEntry::setAddressUnreachable(const IOAddress& address) {
+void
+NameserverEntry::setAddressUnreachable(const IOAddress& address) {
setAddressRTT(address, AddressEntry::UNREACHABLE);
}
-// Update the address selector according to the RTTs
-//
-// Each address has a probability to be selected if multiple addresses are available
-// The weight factor is equal to 1/(rtt*rtt), then all the weight factors are normalized
-// to make the sum equal to 1.0
-void NameserverEntry::updateAddressSelector(std::vector<AddressEntry>& addresses,
- WeightedRandomIntegerGenerator& selector)
+/**
+ * \short A callback into the resolver.
+ *
+ * Whenever we ask the resolver something, this is created and the answer is
+ * fed back trough this. It holds a shared pointer to the entry so it is not
+ * destroyed too soon.
+ */
+class NameserverEntry::ResolverCallback : public ResolverInterface::Callback {
+ public:
+ ResolverCallback(shared_ptr<NameserverEntry> entry,
+ AddressFamily family, const RRType& type) :
+ entry_(entry),
+ family_(family),
+ type_(type)
+ { }
+ /**
+ * \short We received the address successfully.
+ *
+ * This extracts the addresses out from the response and puts them
+ * inside the entry. It tries to reuse the address entries from before (if there were any), to keep their RTTs.
+ */
+ virtual void success(const shared_ptr<AbstractRRset>& response) {
+ time_t now = time(NULL);
+
+ Lock lock(entry_->mutex_);
+
+ vector<AddressEntry> entries;
+
+ if (response->getType() != type_ ||
+ response->getClass() != RRClass(entry_->getClass()))
+ {
+ // TODO Log we got answer of different type
+ failureInternal(lock);
+ return;
+ }
+
+ RdataIteratorPtr i(response->getRdataIterator());
+ // TODO Remove at merge with trunk
+ i->first();
+ while (! i->isLast()) {
+ // Try to find the original value and reuse it
+ string address(i->getCurrent().toText());
+ AddressEntry *found(NULL);
+ BOOST_FOREACH(AddressEntry& entry,
+ entry_->previous_addresses_[family_])
+ {
+ if (entry.getAddress().toText() == address) {
+ // Good, found it.
+ found = &entry;
+ break;
+ }
+ }
+ // If we found it, use it. If not, create a new one.
+ entries.push_back(found ? *found : AddressEntry(IOAddress(
+ i->getCurrent().toText()), 1));
+ i->next();
+ }
+
+ // We no longer need the previous set of addresses, we have
+ // the current ones now.
+ entry_->previous_addresses_[family_].clear();
+
+ if (entries.empty()) {
+ // No data there, count it as a failure
+ failureInternal(lock);
+ } else {
+ // We received the data, so mark it
+ entry_->expect_address_[family_] = false;
+ entry_->expect_address_[ANY_OK] =
+ entry_->expect_address_[V4_ONLY] ||
+ entry_->expect_address_[V6_ONLY];
+ // Everything is here (all address families)
+ if (!entry_->expect_address_[ANY_OK]) {
+ entry_->setState(READY);
+ }
+ // We have some address
+ entry_->has_address_[ANY_OK] =
+ entry_->has_address_[family_] = true;
+ // Insert the entries inside
+ entry_->addresses_[family_].swap(entries);
+ // Update the expiration time. If it is 0, it means we
+ // did not set it yet, so reset
+ time_t expiration(now + response->getTTL().getValue());
+ if (entry_->expiration_) {
+ // We expire at the time first address expires
+ entry_->expiration_ = min(entry_->expiration_, expiration);
+ } else {
+ // We have no expiration time set, use this one
+ entry_->expiration_ = expiration;
+ }
+ // Run the right callbacks
+ dispatchCallbacks(lock);
+ }
+ }
+ /**
+ * \short The resolver failed to retrieve the data.
+ *
+ * So mark the current address family as unreachable.
+ */
+ virtual void failure() {
+ Lock lock(entry_->mutex_);
+ failureInternal(lock);
+ }
+ private:
+ shared_ptr<NameserverEntry> entry_;
+ AddressFamily family_;
+ RRType type_;
+
+ // Dispatches all relevant callbacks. Keeps lock unlocked afterwards.
+ // TODO: We might want to use recursive lock and get rid of this
+ void dispatchCallbacks(Lock& lock)
+ {
+ // We dispatch ANY addresses if there is at last one address or
+ // there's no chance we'll get some in future
+ bool dispatch_any = entry_->has_address_[ANY_OK] ||
+ !entry_->expect_address_[ANY_OK];
+ // Sort out the callbacks we want
+ vector<CallbackPair> keep;
+ vector<shared_ptr<NameserverEntry::Callback> > dispatch;
+ BOOST_FOREACH(const CallbackPair &callback, entry_->callbacks_)
+ {
+ if (callback.first == family_ || (dispatch_any &&
+ callback.first == ANY_OK))
+ {
+ dispatch.push_back(callback.second);
+ } else {
+ keep.push_back(callback);
+ }
+ }
+ // Put there only the ones that we do not want, drop the rest
+ keep.swap(entry_->callbacks_);
+ keep.clear();
+
+ // We can't keep the lock while we execute callbacks
+ lock.unlock();
+ // Run all the callbacks
+ /*
+ * FIXME: This is not completely exception safe. If there's an
+ * exception in a callback, we lose the rest of them.
+ */
+ BOOST_FOREACH(const shared_ptr<NameserverEntry::Callback>&
+ callback, dispatch)
+ {
+ (*callback)(entry_);
+ }
+ }
+
+ // Handle a failure to optain data. Dispatches callbacks and leaves
+ // lock unlocked
+ void failureInternal(Lock &lock) {
+ // Set state of the addresses
+ entry_->expect_address_[family_] = false;
+ entry_->expect_address_[ANY_OK] =
+ entry_->expect_address_[V4_ONLY] ||
+ entry_->expect_address_[V6_ONLY];
+ // When we do not expect any more addresses, decide the state
+ if (!entry_->expect_address_[ANY_OK]) {
+ if (entry_->has_address_[ANY_OK]) {
+ // We have at last one kind of address, so OK
+ entry_->setState(READY);
+ } else {
+ // No addresses :-(
+ entry_->setState(UNREACHABLE);
+ }
+ }
+ // Drop the previous addresses, no use of them now
+ entry_->previous_addresses_[family_].clear();
+ // Dispatch any relevant callbacks
+ dispatchCallbacks(lock);
+ }
+};
+
+void
+NameserverEntry::askIP(shared_ptr<ResolverInterface> resolver,
+ const RRType& type, AddressFamily family)
{
- vector<double> probabilities;
- for(vector<AddressEntry>::iterator it = addresses.begin();
- it != addresses.end(); ++it){
- uint32_t rtt = (*it).getRTT();
- if(rtt == 0) {
- isc_throw(RTTIsZero, "The RTT is 0");
- }
-
- if(rtt == AddressEntry::UNREACHABLE) {
- probabilities.push_back(0);
+ QuestionPtr question(new Question(Name(getName()), RRClass(getClass()),
+ type));
+ shared_ptr<ResolverCallback> callback(new ResolverCallback(
+ shared_from_this(), family, type));
+ resolver->resolve(question, callback);
+}
+
+void
+NameserverEntry::askIP(shared_ptr<ResolverInterface> resolver,
+ shared_ptr<Callback> callback, AddressFamily family)
+{
+ Lock lock(mutex_);
+
+ if (getState() == EXPIRED || getState() == NOT_ASKED) {
+ // We will request the addresses
+
+ // Set internal state first
+ // We store the old addresses so we can pick their RTT when
+ // we get the same addresses again (most probably)
+ previous_addresses_[V4_ONLY].clear();
+ previous_addresses_[V6_ONLY].clear();
+ addresses_[V4_ONLY].swap(previous_addresses_[V4_ONLY]);
+ addresses_[V6_ONLY].swap(previous_addresses_[V6_ONLY]);
+ setState(IN_PROGRESS);
+ has_address_[V4_ONLY] = has_address_[V6_ONLY] = has_address_[ANY_OK] =
+ false;
+ expect_address_[V4_ONLY] = expect_address_[V6_ONLY] =
+ expect_address_[ANY_OK] = true;
+ expiration_ = 0;
+
+ // Store the callback
+ callbacks_.push_back(CallbackPair(family, callback));
+
+ // Ask for both types of addresses
+ // We are unlocked here, as the callback from that might want to lock
+ lock.unlock();
+ askIP(resolver, RRType::A(), V4_ONLY);
+ askIP(resolver, RRType::AAAA(), V6_ONLY);
+ // Make sure we end the routine when we are not locked
+ return;
+ } else {
+ // We already asked. Do we expect this address type still to come?
+ if (!expect_address_[family]) {
+ // We do not expect it to come, dispatch right away
+ lock.unlock();
+ (*callback)(shared_from_this());
+ return;
} else {
- probabilities.push_back(1.0/(rtt*rtt));
- }
- }
- // Calculate the sum
- double sum = accumulate(probabilities.begin(), probabilities.end(), 0.0);
-
- if(sum != 0) {
- // Normalize the probabilities to make the sum equal to 1.0
- for(vector<double>::iterator it = probabilities.begin();
- it != probabilities.end(); ++it){
- (*it) /= sum;
- }
- } else if(probabilities.size() > 0){
- // If all the nameservers are unreachable, the sum will be 0
- // So give each server equal opportunity to be selected.
- for(vector<double>::iterator it = probabilities.begin();
- it != probabilities.end(); ++it){
- (*it) = 1.0/probabilities.size();
- }
- }
-
- selector.reset(probabilities);
+ // It will come in future, store the callback until then
+ callbacks_.push_back(CallbackPair(family, callback));
+ }
+ }
}
} // namespace dns
Modified: branches/trac356/src/lib/nsas/nameserver_entry.h
==============================================================================
--- branches/trac356/src/lib/nsas/nameserver_entry.h (original)
+++ branches/trac356/src/lib/nsas/nameserver_entry.h Mon Dec 13 15:25:09 2010
@@ -20,15 +20,21 @@
#include <string>
#include <vector>
#include <boost/thread.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <exceptions/exceptions.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
#include "address_entry.h"
#include "asiolink.h"
-#include "exceptions/exceptions.h"
+#include "nsas_types.h"
#include "hash_key.h"
#include "lru_list.h"
+#include "fetchable.h"
+#include "resolver_interface.h"
#include "nsas_entry.h"
-#include "random_number_generator.h"
-#include "rrset.h"
+#include "nameserver_address.h"
namespace isc {
namespace nsas {
@@ -67,20 +73,14 @@
{}
};
-
+class ZoneEntry;
+class ResolverInterface;
/// \brief Nameserver Entry
///
/// Describes a nameserver and its addresses. A nameserver be authoritative
/// for several zones (hence is pointed to by more than one zone entry), and
/// may have several addresses associated with it.
-///
-/// When created, zero or more addresses may be given. At any time, the list
-/// of addresses may be updated. This may occur (a) after creation, either to
-/// to get the list of addresses when none have been supplied or to replace
-/// glue records, or (b) when the object has been accessed but found to be
-/// expired (the address records have reached their TTL).
-/// TODO: Add code for update of addresses
///
/// The addresses expire after their TTL has been reached. For simplicity,
/// (and because it is unlikely that A and AAAA records from the same zone have
@@ -90,68 +90,57 @@
///
/// As this object will be stored in the nameserver address store LRU list,
/// it is derived from the LRU list entry class.
-
-class NameserverEntry : public NsasEntry<NameserverEntry>{
+///
+/// It uses shared_from_this in its methods. It must live inside a shared_ptr.
+
+class NameserverEntry : public NsasEntry<NameserverEntry>, public Fetchable {
public:
/// List of addresses associated with this nameserver
- typedef std::vector<AddressEntry> AddressVector;
+ typedef std::vector<NameserverAddress> AddressVector;
typedef AddressVector::iterator AddressVectorIterator;
/// \brief Constructor where no A records are supplied.
///
/// \param name Name of the nameserver,
/// \param class_code class of the nameserver
- NameserverEntry(const std::string& name, uint16_t class_code) :
- name_(name), classCode_(class_code)
- {}
-
- /// Constructor where one or more RRsets of A/AAAA records are supplied.
- /// The class is taken from class of address records and the name from
- /// the owner of the records. If both sets of information are supplied
- /// and the owner names are different, the V4 set wins out; the V6 set of
- /// information is ignored and an error message is logged.
- ///
- /// \param v4Set RRset of A records
- /// \param v6Set RRset of AAAA records
- /// \param curtime Current time. Present for testing, but also as a
- /// possible optimisation if the caller has the current time (it saves
- /// the overhead of a call to time()). The default value of 0 requests
- /// the constructor to get its own copy of the current time.
- NameserverEntry(const isc::dns::AbstractRRset* v4Set,
- const isc::dns::AbstractRRset* v6Set, time_t curtime = 0);
-
- /// \brief Virtual Destructor
- virtual ~NameserverEntry()
- {}
-
- /// \brief Return Address
- ///
- /// Returns a vector of addresses corresponding to this nameserver.
- /// It is up to the caller to
- ///
- /// \param addresses Vector of address entries into which will be appended
- /// addresses that match the specified criteria. (The reason for choosing
- /// this signature is that addresses from more than one nameserver may be
- /// retrieved, in which case appending to an existing list of addresses is
- /// convenient.)
- /// \param family Set to AF_INET/AF_INET6 for V6/V6 addresses, anything
- /// else for all addresses.
- virtual void getAddresses(NameserverEntry::AddressVector& addresses,
- short family = 0) const;
-
- /// \brief Return one address
- ///
- /// Return one address corresponding to this nameserver
- /// \param address NameserverAddress object used to receive the address
- /// \param family The family of user request, AF_INET or AF_INET6
- /// \return true if one address is found, false otherwise
- virtual bool getAddress(NameserverAddress& address, short family);
+ NameserverEntry(const std::string& name,
+ const isc::dns::RRClass& class_code) :
+ name_(name),
+ classCode_(class_code),
+ expiration_(0)
+ {}
+
+ /*
+ * \brief Return Address
+ *
+ * Returns a vector of addresses corresponding to this nameserver.
+ *
+ * \param addresses Vector of address entries into which will be appended
+ * addresses that match the specified criteria. (The reason for
+ * choosing this signature is that addresses from more than one
+ * nameserver may be retrieved, in which case appending to an existing
+ * list of addresses is convenient.)
+ * \param family The family of address that is requested.
+ * \param expired_ok Return addresses even when expired. This is here
+ * because an address with TTL 0 is expired at the exact time it
+ * arrives. But when we call the callback, the owner of callback
+ * is allowed to use them anyway so it should set expired_ok
+ * to true.
+ * \return The state this is currently in. If the TTL expires, it enters
+ * the EXPIRED state by itself and passes no addresses. It may be
+ * IN_PROGRESS and still return some addresses (when one address family
+ * arrived and is is returned, but the other is still on the way).
+ * \todo Should we sort out unreachable addresses as well?
+ */
+ Fetchable::State getAddresses(AddressVector& addresses,
+ AddressFamily family = ANY_OK, bool expired_ok = false);
/// \brief Return Address that corresponding to the index
///
/// \param index The address index in the address vector
- /// \param family The address family, AF_INET or AF_INET6
- virtual asiolink::IOAddress getAddressAtIndex(uint32_t index, short family) const;
+ /// \param family The address family, V4_ONLY or V6_ONLY
+ asiolink::IOAddress getAddressAtIndex(size_t index,
+ AddressFamily family) const;
/// \brief Update RTT
///
@@ -159,29 +148,45 @@
///
/// \param address Address to update
/// \param RTT New RTT for the address
- virtual void setAddressRTT(const asiolink::IOAddress& address, uint32_t rtt);
+ void setAddressRTT(const asiolink::IOAddress& address, uint32_t rtt);
/// \brief Update RTT of the address that corresponding to the index
///
+ /// Shouldn't probably be used directly. Use corresponding
+ /// NameserverAddress.
/// \param rtt Round-Trip Time
/// \param index The address's index in address vector
- /// \param family The address family, AF_INET or AF_INET6
- virtual void updateAddressRTTAtIndex(uint32_t rtt, uint32_t index, short family);
+ /// \param family The address family, V4_ONLY or V6_ONLY
+ void updateAddressRTTAtIndex(uint32_t rtt, size_t index,
+ AddressFamily family);
+ /**
+ * \short Update RTT of an address.
+ *
+ * This is similar to updateAddressRTTAtIndex, but you pass the address,
+ * not it's index. Passing the index might be unsafe, because the position
+ * of the address or the cound of addresses may change in time.
+ *
+ * \param rtt Round-Trip Time
+ * \param address The address whose RTT should be updated.
+ * \param family The address family, V4_ONLY or V6_ONLY
+ */
+ void updateAddressRTT(uint32_t rtt, const asiolink::IOAddress& address,
+ AddressFamily family);
/// \brief Set Address Unreachable
///
/// Sets the specified address to be unreachable
///
/// \param address Address to update
- virtual void setAddressUnreachable(const asiolink::IOAddress& address);
+ void setAddressUnreachable(const asiolink::IOAddress& address);
/// \return Owner Name of RRset
- virtual std::string getName() const {
+ std::string getName() const {
return name_;
}
/// \return Class of RRset
- virtual short getClass() const {
+ const isc::dns::RRClass& getClass() const {
return classCode_;
}
@@ -197,43 +202,75 @@
/// Returns the expiration time of addresses for this nameserver. For
/// simplicity, this quantity is calculated as the minimum expiration time
/// of the A and AAAA address records.
- virtual time_t getExpiration() const {
+ time_t getExpiration() const {
return expiration_;
}
- /// \brief Predicate for Address Selection
- ///
- /// Returns false if the address family of a given entry matches the address
- /// family given or if the address family is 0 (which means return all
- /// addresses). This curious logic is needed for use in the remove_copy_if
- /// algorithm, which copies all values apart from those for which the
- /// criteria is met.
- class AddressSelection : public std::binary_function<short, AddressEntry, bool> {
- public:
- bool operator()(short family, const AddressEntry& entry) const {
- bool match = (entry.getAddress().getFamily() == family) ||
- (family == 0);
- return (! match);
- }
+ /// \name Obtaining the IP addresses from resolver
+ //@{
+ /// \short A callback that some information here arrived (or are unavailable).
+ struct Callback {
+ virtual void operator()(boost::shared_ptr<NameserverEntry> self) = 0;
+ /// \short Virtual destructor, so descendants are properly cleaned up
+ virtual ~ Callback() {}
};
+ /**
+ * \short Asks the resolver for IP address (or addresses).
+ *
+ * Adds a callback for given zone when they are ready or the information
+ * is found unreachable.
+ *
+ * If it is not in NOT_ASKED or EXPIRED state, it does not ask the for the
+ * IP address again, it just inserts the callback. It is up to the caller
+ * not to insert one callback multiple times.
+ *
+ * The callback might be called directly from this function.
+ *
+ * \param resolver Who to ask.
+ * \param callback The callback.
+ * \param family Which addresses are interesting to the caller. This does
+ * not change which adresses are requested, but the callback might
+ * be executed when at last one requested type is available (eg. not
+ * waiting for the other one).
+ * \return The state the entry is currently in. It can return UNREACHABLE
+ * even when there are addresses, if there are no addresses for this
+ * family.
+ */
+ void askIP(boost::shared_ptr<ResolverInterface> resolver,
+ boost::shared_ptr<Callback> callback, AddressFamily family);
+ //@}
+
private:
- /// \brief Update the address selector according to the RTTs of addresses
- ///
- /// \param addresses The address list
- /// \param selector Weighted random generator
- void updateAddressSelector(std::vector<AddressEntry>& addresses,
- WeightedRandomIntegerGenerator& selector);
-
- boost::mutex mutex_; ///< Mutex protecting this object
- std::string name_; ///< Canonical name of the nameserver
- uint16_t classCode_; ///< Class of the nameserver
- std::vector<AddressEntry> v4_addresses_; ///< Set of V4 addresses
- std::vector<AddressEntry> v6_addresses_; ///< Set of V6 addresses
- time_t expiration_; ///< Summary expiration time
- time_t last_access_; ///< Last access time to the structure
- WeightedRandomIntegerGenerator v4_address_selector_; ///< Generate one integer according to different probability
- WeightedRandomIntegerGenerator v6_address_selector_; ///< Generate one integer according to different probability
+ mutable boost::recursive_mutex mutex_; ///< Mutex protecting this object
+ std::string name_; ///< Canonical name of the nameserver
+ isc::dns::RRClass classCode_; ///< Class of the nameserver
+ /**
+ * \short Address lists.
+ *
+ * Only V4_ONLY and V6_ONLY is used, therefore we use the nearest larger
+ * value as the size of the array.
+ *
+ * previous_addresses is kept until the data arrive again on re-fetch and
+ * is used to pick up the RTTs from there.
+ */
+ std::vector<AddressEntry> addresses_[ANY_OK], previous_addresses_[ANY_OK];
+ time_t expiration_; ///< Summary expiration time. 0 = unset
+ // Do we have some addresses already? Do we expect some to come?
+ // These are set after asking for IP, if NOT_ASKED, they are uninitialized
+ bool has_address_[ADDR_REQ_MAX], expect_address_[ADDR_REQ_MAX];
+ // Callbacks from resolver
+ class ResolverCallback;
+ friend class ResolverCallback;
+ // Callbacks inserted into this object
+ typedef std::pair<AddressFamily, boost::shared_ptr<Callback> >
+ CallbackPair;
+ std::vector<CallbackPair> callbacks_;
+ /// \short Private version that does the actual asking of one address type
+ ///
+ /// Call unlocked.
+ void askIP(boost::shared_ptr<ResolverInterface> resolver,
+ const isc::dns::RRType&, AddressFamily);
};
} // namespace dns
Modified: branches/trac356/src/lib/nsas/nsas_entry.h
==============================================================================
--- branches/trac356/src/lib/nsas/nsas_entry.h (original)
+++ branches/trac356/src/lib/nsas/nsas_entry.h Mon Dec 13 15:25:09 2010
@@ -20,7 +20,7 @@
#include <boost/enable_shared_from_this.hpp>
#include <iostream>
-#include "exceptions/exceptions.h"
+#include <exceptions/exceptions.h>
#include "hash_key.h"
#include "hash_table.h"
Modified: branches/trac356/src/lib/nsas/nsas_types.h
==============================================================================
--- branches/trac356/src/lib/nsas/nsas_types.h (original)
+++ branches/trac356/src/lib/nsas/nsas_types.h Mon Dec 13 15:25:09 2010
@@ -17,16 +17,33 @@
#ifndef __NSAS_TYPES_H
#define __NSAS_TYPES_H
-#include <vector>
-#include <asio.h>
-
/// \file nsas_types.h
/// \brief Nameserver Address Store Types
///
-/// Defines a set of typedefs used within the Network Address Store.
+/// Defines a set of types used within the Network Address Store.
-/// \brief Array of nameserver addresses
-typedef std::vector<ip::address> NsasAddress
+namespace isc {
+namespace nsas {
+/**
+ * \brief Address requested
+ *
+ * The order is significant, it is used as array indices and sometime only
+ * the first two are used.
+ */
+enum AddressFamily {
+ /// \short Interested only in IPv4 address
+ V4_ONLY,
+ /// \short Interested only in IPv6 address
+ V6_ONLY,
+ /// \short Any address is good
+ ANY_OK,
+ /// \short Bumper value, does not mean anything, it just represents the
+ /// max value
+ ADDR_REQ_MAX
+};
+
+}
+}
#endif // __NSAS_TYPES_H
Modified: branches/trac356/src/lib/nsas/random_number_generator.h
==============================================================================
--- branches/trac356/src/lib/nsas/random_number_generator.h (original)
+++ branches/trac356/src/lib/nsas/random_number_generator.h Mon Dec 13 15:25:09 2010
@@ -60,7 +60,7 @@
/// \brief Weighted random integer generator
///
/// Generate random integers according different probabilities
-class WeightedRandomIntegerGenerator{
+class WeightedRandomIntegerGenerator {
public:
/// \brief Constructor
///
@@ -71,7 +71,8 @@
/// other integers and the probability is proportional to its value.
/// \param min The minimum integer that generated, other integers will be
/// min, min + 1, ..., min + probabilities.size() - 1
- WeightedRandomIntegerGenerator(const std::vector<double>& probabilities, int min = 0):
+ WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
+ size_t min = 0):
dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
{
// The probabilities must be valid
@@ -82,7 +83,7 @@
// Init with the current time
rng_.seed(time(NULL));
}
-
+
/// \brief Default constructor
///
WeightedRandomIntegerGenerator():
@@ -95,7 +96,7 @@
/// Change the weights of each integers
/// \param probabilities The probabies for all the integers
/// \param min The minimum integer that generated
- void reset(const std::vector<double>& probabilities, int min = 0)
+ void reset(const std::vector<double>& probabilities, size_t min = 0)
{
// The probabilities must be valid
assert(isProbabilitiesValid(probabilities));
@@ -109,21 +110,13 @@
// Reset the minimum integer
min_ = min;
-
- // Reset the random number generator
- rng_.seed(time(NULL));
}
/// \brief Generate weighted random integer
- int operator()()
+ size_t operator()()
{
return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_())
- cumulative_.begin() + min_;
- }
-
- /// \brief Destroctor
- ~WeightedRandomIntegerGenerator()
- {
}
private:
@@ -156,7 +149,7 @@
boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
boost::uniform_real<> dist_; ///< Uniformly distributed real numbers
UniformRealGenerator uniform_real_gen_; ///< Uniformly distributed random real numbers generator
- int min_; ///< The minimum integer that will be generated
+ size_t min_; ///< The minimum integer that will be generated
};
} // namespace dns
Modified: branches/trac356/src/lib/nsas/tests/Makefile.am
==============================================================================
--- branches/trac356/src/lib/nsas/tests/Makefile.am (original)
+++ branches/trac356/src/lib/nsas/tests/Makefile.am Mon Dec 13 15:25:09 2010
@@ -27,8 +27,9 @@
run_unittests_SOURCES += nameserver_address_store_unittest.cc
run_unittests_SOURCES += nameserver_entry_unittest.cc
run_unittests_SOURCES += nsas_entry_compare_unittest.cc
-run_unittests_SOURCES += nsas_test_utilities.h
+run_unittests_SOURCES += nsas_test.h
run_unittests_SOURCES += zone_entry_unittest.cc
+run_unittests_SOURCES += fetchable_unittest.cc
run_unittests_SOURCES += random_number_generator_unittest.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
Modified: branches/trac356/src/lib/nsas/tests/address_entry_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/address_entry_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/address_entry_unittest.cc Mon Dec 13 15:25:09 2010
@@ -24,8 +24,8 @@
#include <stdint.h>
-#include "asiolink.h"
-#include "address_entry.h"
+#include "../asiolink.h"
+#include "../address_entry.h"
static std::string V4A_TEXT("1.2.3.4");
static std::string V4B_TEXT("5.6.7.8");
Modified: branches/trac356/src/lib/nsas/tests/hash_deleter_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/hash_deleter_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/hash_deleter_unittest.cc Mon Dec 13 15:25:09 2010
@@ -21,16 +21,19 @@
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
-#include "nsas_entry.h"
-#include "hash_table.h"
-#include "hash_key.h"
-#include "lru_list.h"
-#include "hash_deleter.h"
+#include <dns/rrclass.h>
+
+#include "../nsas_entry.h"
+#include "../hash_table.h"
+#include "../hash_key.h"
+#include "../lru_list.h"
+#include "../hash_deleter.h"
#include "nsas_test.h"
-#include "nsas_entry_compare.h"
+#include "../nsas_entry_compare.h"
using namespace std;
+using namespace isc::dns;
namespace isc {
namespace nsas {
@@ -40,13 +43,13 @@
class HashDeleterTest : public ::testing::Test {
protected:
HashDeleterTest() :
- entry1_(new TestEntry("alpha", 1)),
- entry2_(new TestEntry("beta", 2)),
- entry3_(new TestEntry("gamma", 3)),
- entry4_(new TestEntry("delta", 4)),
- entry5_(new TestEntry("epsilon", 5)),
- entry6_(new TestEntry("zeta", 6)),
- entry7_(new TestEntry("eta", 7)),
+ entry1_(new TestEntry("alpha", RRClass::IN())),
+ entry2_(new TestEntry("beta", RRClass::CH())),
+ entry3_(new TestEntry("gamma", RRClass::HS())),
+ entry4_(new TestEntry("delta", RRClass::IN())),
+ entry5_(new TestEntry("epsilon", RRClass::CH())),
+ entry6_(new TestEntry("zeta", RRClass::HS())),
+ entry7_(new TestEntry("eta", RRClass::IN())),
hash_table_(new NsasEntryCompare<TestEntry>()),
lru_list_(3, new HashDeleter<TestEntry>(hash_table_))
{}
Modified: branches/trac356/src/lib/nsas/tests/hash_key_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/hash_key_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/hash_key_unittest.cc Mon Dec 13 15:25:09 2010
@@ -21,9 +21,11 @@
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
-#include "hash_key.h"
+#include "../hash_key.h"
+#include <dns/rrclass.h>
using namespace std;
+using namespace isc::dns;
namespace isc {
namespace nsas {
@@ -38,17 +40,17 @@
// Basic constructor
string test1("ABCDEF");
- HashKey key1(test1.c_str(), test1.size(), 1);
+ HashKey key1(test1.c_str(), test1.size(), RRClass::IN());
EXPECT_EQ(key1.key, test1.c_str());
EXPECT_EQ(key1.keylen, test1.size());
- EXPECT_EQ(key1.class_code, 1);
+ EXPECT_EQ(key1.class_code, RRClass::IN());
// String constructor
string test2("uvwxyz");
- HashKey key2(test2, 2);
+ HashKey key2(test2, RRClass::CH());
EXPECT_EQ(key2.key, test2.c_str());
EXPECT_EQ(key2.keylen, test2.size());
- EXPECT_EQ(key2.class_code, 2);
+ EXPECT_EQ(key2.class_code, RRClass::CH());
}
// Equality check
@@ -59,17 +61,25 @@
string test4("ABCDE123"); // Different key (almost same)
string test5("uvwxyz987"); // Different key
- EXPECT_TRUE(HashKey(test1, 1) == HashKey(test1, 1)); // Same key and class
- EXPECT_FALSE(HashKey(test1, 1) == HashKey(test1, 2)); // Different class
+ EXPECT_TRUE(HashKey(test1, RRClass::IN()) == HashKey(test1,
+ RRClass::IN())); // Same key and class
+ EXPECT_FALSE(HashKey(test1, RRClass::IN()) == HashKey(test1,
+ RRClass::CH())); // Different class
- EXPECT_TRUE(HashKey(test1, 2) == HashKey(test2, 2)); // Same value key/class
- EXPECT_FALSE(HashKey(test1, 2) == HashKey(test2, 3));
+ EXPECT_TRUE(HashKey(test1, RRClass::CH()) == HashKey(test2,
+ RRClass::CH())); // Same value key/class
+ EXPECT_FALSE(HashKey(test1, RRClass::CH()) == HashKey(test2,
+ RRClass::IN()));
- EXPECT_TRUE(HashKey(test1, 3) == HashKey(test3, 3)); // Same key
- EXPECT_FALSE(HashKey(test1, 3) == HashKey(test3, 4));
+ EXPECT_TRUE(HashKey(test1, RRClass::HS()) == HashKey(test3,
+ RRClass::HS())); // Same key
+ EXPECT_FALSE(HashKey(test1, RRClass::HS()) == HashKey(test3,
+ RRClass::IN()));
- EXPECT_FALSE(HashKey(test1, 1) == HashKey(test4, 1));
- EXPECT_FALSE(HashKey(test1, 1) == HashKey(test5, 1));
+ EXPECT_FALSE(HashKey(test1, RRClass::IN()) == HashKey(test4,
+ RRClass::IN()));
+ EXPECT_FALSE(HashKey(test1, RRClass::IN()) == HashKey(test5,
+ RRClass::IN()));
}
} // namespace nsas
Modified: branches/trac356/src/lib/nsas/tests/hash_table_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/hash_table_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/hash_table_unittest.cc Mon Dec 13 15:25:09 2010
@@ -16,17 +16,22 @@
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
#include <string.h>
#include <iostream>
-#include "hash_table.h"
-#include "hash_key.h"
-
-#include "nsas_entry_compare.h"
+#include <dns/rrclass.h>
+
+#include "../hash_table.h"
+#include "../hash_key.h"
+
+#include "../nsas_entry_compare.h"
#include "nsas_test.h"
using namespace std;
+using boost::shared_ptr;
+using namespace isc::dns;
namespace isc {
namespace nsas {
@@ -44,10 +49,10 @@
// Constructor - initialize the objects
HashTableTest() :
table_(new NsasEntryCompare<TestEntry>()),
- dummy1_(new TestEntry("test", 1)),
- dummy2_(new TestEntry("test", 1)),
- dummy3_(new TestEntry("Something_Else", 1)),
- dummy4_(new TestEntry("test", 3))
+ dummy1_(new TestEntry("test", RRClass::IN())),
+ dummy2_(new TestEntry("test", RRClass::IN())),
+ dummy3_(new TestEntry("Something_Else", RRClass::IN())),
+ dummy4_(new TestEntry("test", RRClass::CH()))
{}
// Members.
@@ -175,6 +180,30 @@
EXPECT_TRUE(value.get() == NULL);
}
+shared_ptr<TestEntry>
+pass(shared_ptr<TestEntry> value) {
+ return (value);
+}
+
+TEST_F(HashTableTest, GetOrAddTest) {
+ // Add one entry
+ EXPECT_TRUE(table_.add(dummy1_, dummy1_->hashKey()));
+
+ // Check it looks it up
+ std::pair<bool, shared_ptr<TestEntry> > result = table_.getOrAdd(
+ dummy1_->hashKey(), boost::bind(pass, dummy3_));
+ EXPECT_FALSE(result.first);
+ EXPECT_EQ(dummy1_.get(), result.second.get());
+
+ // Check it says it adds the value
+ result = table_.getOrAdd(dummy3_->hashKey(), boost::bind(pass, dummy3_));
+ EXPECT_TRUE(result.first);
+ EXPECT_EQ(dummy3_.get(), result.second.get());
+
+ // Check it really did add it
+ EXPECT_EQ(dummy3_.get(), table_.get(dummy3_->hashKey()).get());
+}
+
// Test that objects with the same name and different classes are distinct.
TEST_F(HashTableTest, ClassTest) {
Modified: branches/trac356/src/lib/nsas/tests/hash_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/hash_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/hash_unittest.cc Mon Dec 13 15:25:09 2010
@@ -21,7 +21,7 @@
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
-#include "hash.h"
+#include "../hash.h"
#include "nsas_test.h"
@@ -82,7 +82,8 @@
// Generate hash values
for (int i = 0; i < 50; ++i) {
string name = base + boost::lexical_cast<string>(i);
- uint32_t hashval = hash(HashKey(name.c_str(), name.size(), 0));
+ uint32_t hashval = hash(HashKey(name.c_str(), name.size(),
+ RRClass(0)));
EXPECT_LT(hashval, size);
values.push_back(hashval);
}
@@ -122,18 +123,22 @@
Hash hash(HASHTABLE_DEFAULT_SIZE, 255, false); // Disable randomisation for testing
// Case not ignored, hashes should be different
- uint32_t value1 = hash(HashKey(test1.c_str(), test1.size(), 0), false);
- uint32_t value2 = hash(HashKey(test2.c_str(), test2.size(), 0), false);
+ uint32_t value1 = hash(HashKey(test1.c_str(), test1.size(), RRClass::IN()),
+ false);
+ uint32_t value2 = hash(HashKey(test2.c_str(), test2.size(), RRClass::IN()),
+ false);
EXPECT_NE(value1, value2);
// Case ignored, hashes should be the same
- uint32_t value3 = hash(HashKey(test1.c_str(), test1.size(), 0), true);
- uint32_t value4 = hash(HashKey(test2.c_str(), test2.size(), 0), true);
+ uint32_t value3 = hash(HashKey(test1.c_str(), test1.size(), RRClass::IN()),
+ true);
+ uint32_t value4 = hash(HashKey(test2.c_str(), test2.size(), RRClass::IN()),
+ true);
EXPECT_EQ(value3, value4);
// Check the default setting.
- uint32_t value5 = hash(HashKey(test1.c_str(), test1.size(), 0));
- uint32_t value6 = hash(HashKey(test2.c_str(), test2.size(), 0));
+ uint32_t value5 = hash(HashKey(test1.c_str(), test1.size(), RRClass::IN()));
+ uint32_t value6 = hash(HashKey(test2.c_str(), test2.size(), RRClass::IN()));
EXPECT_EQ(value5, value6);
// ... and just for good measure
@@ -151,7 +156,8 @@
// codes.
vector<uint32_t> values;
for (uint32_t i = 0; i < 10; ++i) {
- values.push_back(hash(HashKey(test1.c_str(), test1.size(), i)));
+ values.push_back(hash(HashKey(test1.c_str(), test1.size(),
+ RRClass(i))));
}
// find the number of unique values in the array. Although there can
@@ -177,8 +183,10 @@
Hash hash(HASHTABLE_DEFAULT_SIZE, string1.size());
// Do two hashes
- uint32_t value1 = hash(HashKey(string1.c_str(), string1.size(), 0));
- uint32_t value2 = hash(HashKey(string2.c_str(), string2.size(), 0));
+ uint32_t value1 = hash(HashKey(string1.c_str(), string1.size(),
+ RRClass(0)));
+ uint32_t value2 = hash(HashKey(string2.c_str(), string2.size(),
+ RRClass(0)));
EXPECT_EQ(value1, value2);
}
Modified: branches/trac356/src/lib/nsas/tests/lru_list_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/lru_list_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/lru_list_unittest.cc Mon Dec 13 15:25:09 2010
@@ -22,8 +22,8 @@
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
-#include "nsas_entry.h"
-#include "lru_list.h"
+#include "../nsas_entry.h"
+#include "../lru_list.h"
#include "nsas_test.h"
@@ -40,7 +40,7 @@
class Dropped : public LruList<TestEntry>::Dropped {
public:
virtual void operator()(TestEntry* entry) const {
- entry->setClass(entry->getClass() | 0x8000);
+ entry->setClass(RRClass(entry->getClass().getCode() | 0x8000));
}
};
@@ -49,13 +49,13 @@
class LruListTest : public ::testing::Test {
protected:
LruListTest() :
- entry1_(new TestEntry("alpha", 1)),
- entry2_(new TestEntry("beta", 2)),
- entry3_(new TestEntry("gamma", 3)),
- entry4_(new TestEntry("delta", 4)),
- entry5_(new TestEntry("epsilon", 5)),
- entry6_(new TestEntry("zeta", 6)),
- entry7_(new TestEntry("eta", 7))
+ entry1_(new TestEntry("alpha", RRClass::IN())),
+ entry2_(new TestEntry("beta", RRClass::CH())),
+ entry3_(new TestEntry("gamma", RRClass::HS())),
+ entry4_(new TestEntry("delta", RRClass::IN())),
+ entry5_(new TestEntry("epsilon", RRClass::HS())),
+ entry6_(new TestEntry("zeta", RRClass::CH())),
+ entry7_(new TestEntry("eta", RRClass::IN()))
{}
virtual ~LruListTest()
@@ -233,22 +233,22 @@
lru.add(entry2_);
lru.add(entry3_);
- EXPECT_EQ(1, entry1_->getClass());
- EXPECT_EQ(2, entry2_->getClass());
+ EXPECT_EQ(RRClass::IN(), entry1_->getClass());
+ EXPECT_EQ(RRClass::CH(), entry2_->getClass());
// Add another entry and check that the handler runs.
- EXPECT_EQ(0, (entry1_->getClass() & 0x8000));
+ EXPECT_EQ(0, (entry1_->getClass().getCode() & 0x8000));
lru.add(entry4_);
- EXPECT_NE(0, (entry1_->getClass() & 0x8000));
-
- EXPECT_EQ(0, (entry2_->getClass() & 0x8000));
+ EXPECT_NE(0, (entry1_->getClass().getCode() & 0x8000));
+
+ EXPECT_EQ(0, (entry2_->getClass().getCode() & 0x8000));
lru.add(entry5_);
- EXPECT_NE(0, (entry2_->getClass() & 0x8000));
+ EXPECT_NE(0, (entry2_->getClass().getCode() & 0x8000));
// Delete an entry and check that the handler does not run.
- EXPECT_EQ(0, (entry3_->getClass() & 0x8000));
+ EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000));
lru.remove(entry3_);
- EXPECT_EQ(0, (entry3_->getClass() & 0x8000));
+ EXPECT_EQ(0, (entry3_->getClass().getCode() & 0x8000));
}
// Miscellaneous tests - pathological conditions
Modified: branches/trac356/src/lib/nsas/tests/nameserver_address_store_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/nameserver_address_store_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/nameserver_address_store_unittest.cc Mon Dec 13 15:25:09 2010
@@ -20,18 +20,28 @@
/// address store. These act to delete zones from the zone hash table when
/// the element reaches the top of the LRU list.
+#include <dns/rrttl.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+
#include <gtest/gtest.h>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/foreach.hpp>
#include <string.h>
-#include <vector>
-
-#include "nameserver_address_store.h"
-#include "nsas_entry_compare.h"
-#include "nameserver_entry.h"
-#include "zone_entry.h"
+#include <cassert>
+
+#include "../nameserver_address_store.h"
+#include "../nsas_entry_compare.h"
+#include "../nameserver_entry.h"
+#include "../zone_entry.h"
+#include "../address_request_callback.h"
#include "nsas_test.h"
+
+using namespace isc::dns;
+using namespace std;
+using namespace boost;
namespace isc {
namespace nsas {
@@ -47,8 +57,10 @@
///
/// \param hashsize Size of the zone hash table
/// \param lrusize Size of the zone hash table
- DerivedNsas(uint32_t hashsize, uint32_t lrusize) :
- NameserverAddressStore(hashsize, lrusize)
+ DerivedNsas(shared_ptr<TestResolver> resolver, uint32_t hashsize,
+ uint32_t lrusize) :
+ NameserverAddressStore(resolver, hashsize, lrusize),
+ resolver_(resolver)
{}
/// \brief Virtual Destructor
@@ -58,43 +70,108 @@
/// \brief Add Nameserver Entry to Hash and LRU Tables
void AddNameserverEntry(boost::shared_ptr<NameserverEntry>& entry) {
HashKey h = entry->hashKey();
- nameserver_hash_.add(entry, h);
- nameserver_lru_.add(entry);
+ nameserver_hash_->add(entry, h);
+ nameserver_lru_->add(entry);
}
/// \brief Add Zone Entry to Hash and LRU Tables
void AddZoneEntry(boost::shared_ptr<ZoneEntry>& entry) {
HashKey h = entry->hashKey();
- zone_hash_.add(entry, h);
- zone_lru_.add(entry);
- }
+ zone_hash_->add(entry, h);
+ zone_lru_->add(entry);
+ }
+ /**
+ * \short Just wraps the common lookup
+ *
+ * It calls the lookup and provides the authority section
+ * if it is asked for by the resolver.
+ */
+ void lookupAndAnswer(const string& name, const RRClass& class_code,
+ shared_ptr<AbstractRRset> authority,
+ shared_ptr<AddressRequestCallback> callback)
+ {
+ size_t size(resolver_->requests.size());
+ NameserverAddressStore::lookup(name, class_code, callback, ANY_OK);
+ // It asked something, the only thing it can ask is the NS list
+ if (size < resolver_->requests.size()) {
+ resolver_->provideNS(size, authority);
+ // Once answered, drop the request so noone else sees it
+ resolver_->requests.erase(resolver_->requests.begin() + size);
+ } else {
+ ADD_FAILURE() << "Not asked for NS";
+ }
+ }
+private:
+ shared_ptr<TestResolver> resolver_;
};
/// \brief Text Fixture Class
-class NameserverAddressStoreTest : public ::testing::Test {
+class NameserverAddressStoreTest : public TestWithRdata {
protected:
- // Constructor - initialize a set of nameserver and zone objects. For convenience,
- // these are stored in vectors.
- NameserverAddressStoreTest()
+ NameserverAddressStoreTest() :
+ authority_(new RRset(Name("example.net."), RRClass::IN(), RRType::NS(),
+ RRTTL(128))),
+ empty_authority_(new RRset(Name("example.net."), RRClass::IN(),
+ RRType::NS(), RRTTL(128))),
+ resolver_(new TestResolver)
{
+ // Constructor - initialize a set of nameserver and zone objects. For
+ // convenience, these are stored in vectors.
for (int i = 1; i <= 9; ++i) {
- std::string name = "nameserver" + boost::lexical_cast<std::string>(i);
- nameservers_.push_back(boost::shared_ptr<NameserverEntry>(new NameserverEntry(name, (40 + i))));
+ std::string name = "nameserver" + boost::lexical_cast<std::string>(
+ i);
+ nameservers_.push_back(boost::shared_ptr<NameserverEntry>(
+ new NameserverEntry(name, RRClass(40 + i))));
}
+ // Some zones. They will not use the tables in this test, so it can be
+ // empty
for (int i = 1; i <= 9; ++i) {
std::string name = "zone" + boost::lexical_cast<std::string>(i);
- zones_.push_back(boost::shared_ptr<ZoneEntry>(new ZoneEntry(name, (40 + i))));
+ zones_.push_back(boost::shared_ptr<ZoneEntry>(new ZoneEntry(
+ resolver_, name, RRClass(40 + i),
+ shared_ptr<HashTable<NameserverEntry> >(),
+ shared_ptr<LruList<NameserverEntry> >())));
}
+
+ // A nameserver serving data
+ authority_->addRdata(ConstRdataPtr(new rdata::generic::NS(Name(
+ "ns.example.com."))));
+
+ // This is reused because of convenience, clear it just in case
+ NSASCallback::results.clear();
}
// Vector of pointers to nameserver and zone entries.
std::vector<boost::shared_ptr<NameserverEntry> > nameservers_;
std::vector<boost::shared_ptr<ZoneEntry> > zones_;
+
+ RRsetPtr authority_, empty_authority_;
+
+ shared_ptr<TestResolver> resolver_;
+
+ class NSASCallback : public AddressRequestCallback {
+ public:
+ typedef pair<bool, NameserverAddress> Result;
+ static vector<Result> results;
+ virtual void success(const NameserverAddress& address) {
+ results.push_back(Result(true, address));
+ }
+ virtual void unreachable() {
+ results.push_back(Result(false, NameserverAddress()));
+ }
+ };
+
+ boost::shared_ptr<AddressRequestCallback> getCallback() {
+ return (boost::shared_ptr<AddressRequestCallback>(new NSASCallback));
+ }
};
+
+vector<NameserverAddressStoreTest::NSASCallback::Result>
+ NameserverAddressStoreTest::NSASCallback::results;
/// \brief Remove Zone Entry from Hash Table
@@ -105,7 +182,7 @@
// Create a NSAS with a hash size of three and a LRU size of 9 (both zone and
// nameserver tables).
- DerivedNsas nsas(2, 2);
+ DerivedNsas nsas(resolver_, 2, 2);
// Add six entries to the tables. After addition the reference count of each element
// should be 3 - one for the entry in the zones_ vector, and one each for the entries
@@ -135,7 +212,7 @@
// Create a NSAS with a hash size of three and a LRU size of 9 (both zone and
// nameserver tables).
- DerivedNsas nsas(2, 2);
+ DerivedNsas nsas(resolver_, 2, 2);
// Add six entries to the tables. After addition the reference count of each element
// should be 3 - one for the entry in the nameservers_ vector, and one each for the entries
@@ -156,5 +233,175 @@
EXPECT_EQ(1, nameservers_[1].use_count());
}
+/**
+ * \short Try lookup on empty store.
+ *
+ * Check if it asks correct questions and it keeps correct internal state.
+ */
+TEST_F(NameserverAddressStoreTest, emptyLookup) {
+ DerivedNsas nsas(resolver_, 10, 10);
+ // Ask it a question
+ nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
+ getCallback());
+ // It should ask for IP addresses for ns.example.com.
+ EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
+
+ // Ask another question for the same zone
+ nsas.lookup("example.net.", RRClass::IN(), getCallback());
+ // It should ask no more questions now
+ EXPECT_EQ(2, resolver_->requests.size());
+
+ // Ask another question with different zone but the same nameserver
+ authority_->setName(Name("example.com."));
+ nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
+ getCallback());
+ // It still should ask nothing
+ EXPECT_EQ(2, resolver_->requests.size());
+
+ // We provide IP address of one nameserver, it should generate all the
+ // results
+ EXPECT_NO_THROW(resolver_->answer(0, Name("ns.example.com."), RRType::A(),
+ rdata::in::A("192.0.2.1")));
+ EXPECT_EQ(3, NSASCallback::results.size());
+ BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
+ EXPECT_TRUE(result.first);
+ EXPECT_EQ("192.0.2.1", result.second.getAddress().toText());
+ }
+}
+
+/**
+ * \short Try looking up a zone that does not have any nameservers.
+ *
+ * It should not ask anything and say it is unreachable right away.
+ */
+TEST_F(NameserverAddressStoreTest, zoneWithoutNameservers) {
+ DerivedNsas nsas(resolver_, 10, 10);
+ // Ask it a question
+ nsas.lookupAndAnswer("example.net.", RRClass::IN(), empty_authority_,
+ getCallback());
+ // There should be no questions, because there's nothing to ask
+ EXPECT_EQ(0, resolver_->requests.size());
+ // And there should be one "unreachable" answer for the query
+ ASSERT_EQ(1, NSASCallback::results.size());
+ EXPECT_FALSE(NSASCallback::results[0].first);
+}
+
+/**
+ * \short Try looking up a zone that has only an unreachable nameserver.
+ *
+ * It should be unreachable. Furthermore, subsequent questions for that zone
+ * or other zone with the same nameserver should be unreachable right away,
+ * without further asking.
+ */
+TEST_F(NameserverAddressStoreTest, unreachableNS) {
+ DerivedNsas nsas(resolver_, 10, 10);
+ // Ask it a question
+ nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
+ getCallback());
+ // It should ask for IP addresses for example.com.
+ EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
+
+ // Ask another question with different zone but the same nameserver
+ authority_->setName(Name("example.com."));
+ nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
+ getCallback());
+ // It should ask nothing more now
+ EXPECT_EQ(2, resolver_->requests.size());
+
+ // We say there are no addresses
+ resolver_->requests[0].second->failure();
+ resolver_->requests[1].second->failure();
+
+ // We should have 2 answers now
+ EXPECT_EQ(2, NSASCallback::results.size());
+ // When we ask one same and one other zone with the same nameserver,
+ // it should generate no questions and answer right away
+ nsas.lookup("example.net.", RRClass::IN(), getCallback());
+ authority_->setName(Name("example.org."));
+ nsas.lookupAndAnswer("example.org.", RRClass::IN(), authority_,
+ getCallback());
+ // There should be 4 negative answers now
+ EXPECT_EQ(4, NSASCallback::results.size());
+ BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
+ EXPECT_FALSE(result.first);
+ }
+}
+
+/**
+ * \short Try to stress it little bit by having multiple zones and nameservers.
+ *
+ * Does some asking, on a set of zones that share some nameservers, with
+ * slower answering, evicting data, etc.
+ */
+TEST_F(NameserverAddressStoreTest, CombinedTest) {
+ // Create small caches, so we get some evictions
+ DerivedNsas nsas(resolver_, 1, 1);
+ // Ask for example.net. It has single nameserver out of the zone
+ nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
+ getCallback());
+ // It should ask for the nameserver IP addresses
+ EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
+ EXPECT_EQ(0, NSASCallback::results.size());
+ // But we do not answer it right away. We create a new zone and
+ // let this nameserver entry get out.
+ rrns_->addRdata(rdata::generic::NS("example.cz"));
+ nsas.lookupAndAnswer(EXAMPLE_CO_UK, RRClass::IN(), rrns_, getCallback());
+ // It really should ask something, one of the nameservers
+ // (or both)
+ ASSERT_GT(resolver_->requests.size(), 2);
+ Name name(resolver_->requests[2].first->getName());
+ EXPECT_TRUE(name == Name("example.fr") || name == Name("example.de") ||
+ name == Name("example.cz"));
+ EXPECT_NO_THROW(resolver_->asksIPs(name, 2, 3));
+ EXPECT_EQ(0, NSASCallback::results.size());
+
+ size_t request_count(resolver_->requests.size());
+ // This should still be in the hash table, so try it asks no more questions
+ nsas.lookup("example.net.", RRClass::IN(), getCallback());
+ EXPECT_EQ(request_count, resolver_->requests.size());
+ EXPECT_EQ(0, NSASCallback::results.size());
+
+ // We respond to one of the 3 nameservers
+ EXPECT_NO_THROW(resolver_->answer(2, name, RRType::A(),
+ rdata::in::A("192.0.2.1")));
+ // That should trigger one answer
+ EXPECT_EQ(1, NSASCallback::results.size());
+ EXPECT_TRUE(NSASCallback::results[0].first);
+ EXPECT_EQ("192.0.2.1",
+ NSASCallback::results[0].second.getAddress().toText());
+ EXPECT_NO_THROW(resolver_->answer(3, name, RRType::AAAA(),
+ rdata::in::AAAA("2001:bd8::1")));
+ // And there should be yet another query
+ ASSERT_GT(resolver_->requests.size(), 4);
+ EXPECT_NE(name, resolver_->requests[4].first->getName());
+ Name another_name = resolver_->requests[4].first->getName();
+ EXPECT_TRUE(another_name == Name("example.fr") ||
+ another_name == Name("example.de") ||
+ another_name == Name("example.cz"));
+ request_count = resolver_->requests.size();
+
+ // But when ask for a different zone with the first nameserver, it should
+ // ask again, as it is evicted already
+ authority_->setName(Name("example.com."));
+ nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
+ getCallback());
+ EXPECT_EQ(request_count + 2, resolver_->requests.size());
+ EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), request_count,
+ request_count + 1));
+ // Now, we answer both queries for the same address
+ // and three (one for the original, one for this one) more answers should
+ // arrive
+ NSASCallback::results.clear();
+ EXPECT_NO_THROW(resolver_->answer(0, Name("ns.example.com."), RRType::A(),
+ rdata::in::A("192.0.2.2")));
+ EXPECT_NO_THROW(resolver_->answer(request_count, Name("ns.example.com."),
+ RRType::A(), rdata::in::A("192.0.2.2")));
+ EXPECT_EQ(3, NSASCallback::results.size());
+ BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
+ EXPECT_TRUE(result.first);
+ EXPECT_EQ("192.0.2.2", result.second.getAddress().toText());
+ }
+}
+
} // namespace nsas
} // namespace isc
Modified: branches/trac356/src/lib/nsas/tests/nameserver_address_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/nameserver_address_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/nameserver_address_unittest.cc Mon Dec 13 15:25:09 2010
@@ -16,13 +16,14 @@
#include <gtest/gtest.h>
-#include "name.h"
-#include "nameserver_address.h"
-#include "rdata.h"
-#include "rrclass.h"
-#include "rrset.h"
-#include "rrttl.h"
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include "../nameserver_address.h"
+#include "../nameserver_entry.h"
#include "nsas_test.h"
namespace isc {
@@ -30,6 +31,7 @@
using namespace dns;
using namespace rdata;
+using namespace boost;
#define TEST_ADDRESS_INDEX 1
@@ -37,35 +39,48 @@
class NameserverEntrySample {
public:
NameserverEntrySample():
- rrv4_(Name("example.org"), RRClass::IN(), RRType::A(), RRTTL(1200))
+ name_("example.org"),
+ rrv4_(new BasicRRset(name_, RRClass::IN(), RRType::A(), RRTTL(1200)))
{
// Add some sample A records
- rrv4_.addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
- rrv4_.addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
- rrv4_.addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
+ rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
+ rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
+ rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
- ns_.reset(new NameserverEntry(&rrv4_, NULL));
+ ns_.reset(new NameserverEntry(name_.toText(), RRClass::IN()));
+ shared_ptr<TestResolver> resolver(new TestResolver);
+ ns_->askIP(resolver, shared_ptr<Callback>(new Callback), ANY_OK);
+ resolver->asksIPs(name_, 0, 1);
+ resolver->requests[0].second->success(rrv4_);
}
// Return the sample NameserverEntry
boost::shared_ptr<NameserverEntry>& getNameserverEntry() { return ns_; }
// Return the IOAddress corresponding to the index in rrv4_
- asiolink::IOAddress getAddressAtIndex(uint32_t index) { return ns_.get()->getAddressAtIndex(index, AF_INET); }
+ asiolink::IOAddress getAddressAtIndex(uint32_t index) {
+ return ns_.get()->getAddressAtIndex(index, V4_ONLY);
+ }
// Return the addresses count stored in RRset
- unsigned int getAddressesCount() const { return rrv4_.getRdataCount(); }
+ unsigned int getAddressesCount() const { return rrv4_->getRdataCount(); }
// Return the RTT of the address
uint32_t getAddressRTTAtIndex(uint32_t index) {
NameserverEntry::AddressVector addresses;
ns_.get()->getAddresses(addresses);
- return addresses[index].getRTT();
+ return (addresses[index].getAddressEntry().getRTT());
}
private:
- BasicRRset rrv4_; ///< Standard RRSet - IN, A, lowercase name
+ Name name_; ///< Name of the sample
+ shared_ptr<BasicRRset> rrv4_; ///< Standard RRSet - IN, A, lowercase name
boost::shared_ptr<NameserverEntry> ns_; ///< Shared_ptr that points to a NameserverEntry object
+
+ class Callback : public NameserverEntry::Callback {
+ public:
+ virtual void operator()(shared_ptr<NameserverEntry>) { }
+ };
};
/// \brief Test Fixture Class
@@ -73,29 +88,26 @@
protected:
// Constructor
NameserverAddressTest():
- ns_address_(ns_sample_.getNameserverEntry(), TEST_ADDRESS_INDEX, AF_INET),
- invalid_ns_address_(ns_sample_.getNameserverEntry(), ns_sample_.getAddressesCount(), AF_INET)
+ ns_address_(ns_sample_.getNameserverEntry(),
+ ns_sample_.getNameserverEntry()->getAddressAtIndex(
+ TEST_ADDRESS_INDEX, V4_ONLY), V4_ONLY)
{
}
NameserverEntrySample ns_sample_;
// Valid NameserverAddress object
NameserverAddress ns_address_;
-
- // NameserverAddress object that constructed with invalid index
- NameserverAddress invalid_ns_address_;
};
// Test that the address is equal to the address in NameserverEntry
TEST_F(NameserverAddressTest, Address) {
EXPECT_TRUE(ns_address_.getAddress().equal( ns_sample_.getAddressAtIndex(TEST_ADDRESS_INDEX)));
- // It will trigger an assert with the invalid index
- ASSERT_DEATH(invalid_ns_address_.getAddress(), "");
-
boost::shared_ptr<NameserverEntry> empty_ne((NameserverEntry*)NULL);
// It will throw an NullNameserverEntryPointer exception with the empty NameserverEntry shared pointer
- ASSERT_THROW({NameserverAddress empty_ns_address(empty_ne, 0, AF_INET);}, NullNameserverEntryPointer);
+ ASSERT_THROW({NameserverAddress empty_ns_address(empty_ne,
+ asiolink::IOAddress("127.0.0.1"), V4_ONLY);},
+ NullNameserverEntryPointer);
}
// Test that the RTT is updated
Modified: branches/trac356/src/lib/nsas/tests/nameserver_entry_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/nameserver_entry_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/nameserver_entry_unittest.cc Mon Dec 13 15:25:09 2010
@@ -16,178 +16,93 @@
#include <iostream>
#include <algorithm>
-#include <cmath>
#include <limits.h>
#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
-#include "rdata.h"
-#include "rrset.h"
-#include "rrclass.h"
-#include "rrttl.h"
-#include "name.h"
-
-#include "asiolink.h"
-#include "address_entry.h"
-#include "nameserver_address.h"
-#include "nameserver_entry.h"
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/rdataclass.h>
+#include <dns/rrttl.h>
+#include <dns/name.h>
+#include <exceptions/exceptions.h>
+
+#include "../asiolink.h"
+#include "../address_entry.h"
+#include "../nameserver_entry.h"
+#include "../nameserver_address.h"
+#include "../zone_entry.h"
#include "nsas_test.h"
+using namespace isc::nsas;
using namespace asiolink;
using namespace std;
using namespace isc::dns;
using namespace rdata;
-
-namespace isc {
-namespace nsas {
-
-// 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.");
-static const std::string MIXED_EXAMPLE_CO_UK("EXAmple.co.uk.");
+using namespace boost;
+
+namespace {
/// \brief Test Fixture Class
-class NameserverEntryTest : public ::testing::Test {
+class NameserverEntryTest : public TestWithRdata {
protected:
-
- /// \brief Constructor
- ///
- /// Initializes the RRsets used in the tests. The RRsets themselves have to
- /// be initialized with the basic data on their construction. The Rdata for
- /// them is added in SetUp().
- NameserverEntryTest() :
- rrv4_(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::A(), RRTTL(1200)),
- rrcase_(Name(MIXED_EXAMPLE_CO_UK), RRClass::IN(), RRType::A(),
- RRTTL(1200)),
- rrch_(Name(EXAMPLE_CO_UK), RRClass::CH(), RRType::A(), RRTTL(1200)),
- rrns_(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::NS(), RRTTL(1200)),
- rrv6_(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::AAAA(), RRTTL(900)),
- rrnet_(Name(EXAMPLE_NET), RRClass::IN(), RRType::A(), RRTTL(600))
- {}
-
- /// \brief Add Rdata to RRsets
- ///
- /// The data are added as const pointers to avoid the stricter type checking
- /// applied by the Rdata code. There is no need for it in these tests.
- virtual void SetUp() {
-
- // A records
- rrv4_.addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
- rrv4_.addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
- rrv4_.addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
-
- // A records
- rrcase_.addRdata(ConstRdataPtr(new RdataTest<A>("13.14.15.16")));
-
- // No idea what Chaosnet address look like other than they are 16 bits
- // The fact that they are type A is probably also incorrect.
- rrch_.addRdata(ConstRdataPtr(new RdataTest<A>("1324")));
-
- // NS records take a single name
- rrns_.addRdata(ConstRdataPtr(new RdataTest<NS>("example.fr")));
- rrns_.addRdata(ConstRdataPtr(new RdataTest<NS>("example.de")));
-
- // AAAA records
- rrv6_.addRdata(ConstRdataPtr(new RdataTest<AAAA>("2001::1002")));
- rrv6_.addRdata(ConstRdataPtr(new RdataTest<AAAA>("dead:beef:feed::")));
-
- // A record for example.net
- rrnet_.addRdata(ConstRdataPtr(new RdataTest<A>("17.18.18.20")));
- }
-
- /// \brief Data for the tests
- BasicRRset rrv4_; ///< Standard RRSet - IN, A, lowercase name
- BasicRRset rrcase_; ///< Mixed-case name
- BasicRRset rrch_; ///< Non-IN RRset (Chaos in this case)
- BasicRRset rrns_; ///< NS RRset
- BasicRRset rrv6_; ///< Standard RRset, IN, AAAA, lowercase name
- BasicRRset rrnet_; ///< example.net A RRset
+ /// \short Just a really stupid callback counting times called
+ struct Callback : public NameserverEntry::Callback {
+ size_t count;
+ virtual void operator()(shared_ptr<NameserverEntry>) {
+ count ++;
+ }
+ Callback() : count(0) { }
+ };
+private:
+ /**
+ * \short Fills an rrset into the NameserverEntry trough resolver.
+ *
+ * This function is used when we want to pass data to a NameserverEntry
+ * trough the resolver.
+ * \param resolver The resolver used by the NameserverEntry
+ * \param index Index of the query in the resolver.
+ * \param set The answer. If the pointer is empty, it is taken
+ * as a failure.
+ */
+ void fillSet(shared_ptr<TestResolver> resolver, size_t index,
+ shared_ptr<BasicRRset> set)
+ {
+ if (set) {
+ resolver->requests[index].second->success(set);
+ } else {
+ resolver->requests[index].second->failure();
+ }
+ }
+protected:
+ /// Fills the nameserver entry with data trough ask IP
+ void fillNSEntry(shared_ptr<NameserverEntry> entry,
+ shared_ptr<BasicRRset> rrv4, shared_ptr<BasicRRset> rrv6)
+ {
+ // Prepare data to run askIP
+ shared_ptr<TestResolver> resolver(new TestResolver);
+ shared_ptr<Callback> callback(new Callback);
+ // Let it ask for data
+ entry->askIP(resolver, callback, ANY_OK);
+ // Check it really asked and sort the queries
+ EXPECT_TRUE(resolver->asksIPs(Name(entry->getName()), 0, 1));
+ // Respond with answers
+ fillSet(resolver, 0, rrv4);
+ fillSet(resolver, 1, rrv6);
+ }
};
-
-/// \brief Compare Vectors of String
-///
-/// Compares two vectors of strings. A GoogleTest check is done on the results.
-///
-/// \param vec1 First vector. This may be reordered in the comparison.
-/// \param vec2 Second vector. This may be reordered in the comparison
-static void CompareStringVectors(vector<string>& vec1, vector<string>& vec2)
-{
- // Check that the vectors are the same size.
- EXPECT_EQ(vec1.size(), vec2.size());
-
- // Get into canonical order
- sort(vec1.begin(), vec1.end());
- sort(vec2.begin(), vec2.end());
-
- // ... and look for a mismatch.
- EXPECT_TRUE(equal(vec1.begin(), vec1.end(), vec2.begin()));
-}
-
-/// \brief Compare Ranges of Addresses
-///
-/// Compares the addresses held in an address vector with those held in the
-/// RRset from which it was dervived and checks that there is a 1:1
-/// mapping between the two.
-///
-/// \param av AddressVector retrieved from NameserverEntry object
-/// \param rrs BasicRRSet from which the vector was created
-static void CompareAddresses(NameserverEntry::AddressVector& av,
- BasicRRset& rrs)
-{
-
- // Extract addresses from address vector into strings
- vector<string> avs;
- BOOST_FOREACH(AddressEntry addr, av) {
- avs.push_back(addr.getAddress().toText());
- }
-
- // Do the same for the Basic RRset
- vector<string> rrstr;
- RdataIteratorPtr i = rrs.getRdataIterator();
- i->first();
- while (! i->isLast()) {
- rrstr.push_back(i->getCurrent().toText());
- i->next();
- }
-
- // ... and compare the results
- CompareStringVectors(avs, rrstr);
-}
-
-
-/// \brief Compare Address Vectors
-///
-/// Compares two address vectors by converting the addresses to string form
-/// and comparing the strings. Any mismatch will be reported.
-///
-/// \param vec1 First address vector
-/// \param vec2 Second address vector
-static void CompareAddressVectors(NameserverEntry::AddressVector& vec1,
- NameserverEntry::AddressVector& vec2) {
-
- // Extract addresses from address vectors into strings
- vector<string> strvec1;
- BOOST_FOREACH(AddressEntry addr, vec1) {
- strvec1.push_back(addr.getAddress().toText());
- }
-
- vector<string> strvec2;
- BOOST_FOREACH(AddressEntry addr, vec2) {
- strvec2.push_back(addr.getAddress().toText());
- }
-
- CompareStringVectors(strvec1, strvec2);
-}
/// Tests of the default constructor
TEST_F(NameserverEntryTest, DefaultConstructor) {
// Default constructor should not create any RRsets
- NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::IN().getCode());
+ NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::IN());
EXPECT_EQ(EXAMPLE_CO_UK, alpha.getName());
- EXPECT_EQ(RRClass::IN().getCode(), alpha.getClass());
+ EXPECT_EQ(RRClass::IN(), alpha.getClass());
// Also check that no addresses have been created.
NameserverEntry::AddressVector addresses;
@@ -195,118 +110,53 @@
EXPECT_TRUE(addresses.empty());
}
-
-/// Tests of constructor passed a list of addresses.
-TEST_F(NameserverEntryTest, AddressListConstructor) {
-
- // Initialize with no addresses and check that data returned has size of
- // zero.
- NameserverEntry alpha(NULL, NULL);
- NameserverEntry::AddressVector av;
- alpha.getAddresses(av);
- EXPECT_EQ(0, av.size());
-
- NameserverEntry::AddressVector av4;
- alpha.getAddresses(av4, AF_INET);
- EXPECT_EQ(0, av4.size());
-
- NameserverEntry::AddressVector av6;
- alpha.getAddresses(av6, AF_INET6);
- EXPECT_EQ(0, av6.size());
-
- // Initialize with V4 addresses only.
- EXPECT_TRUE(rrv4_.getRdataCount() > 0);
- NameserverEntry beta(&rrv4_, NULL);
-
- NameserverEntry::AddressVector bv;
- beta.getAddresses(bv);
- EXPECT_EQ(rrv4_.getRdataCount(), bv.size());
-
- NameserverEntry::AddressVector bv4;
- beta.getAddresses(bv4, AF_INET);
- EXPECT_EQ(rrv4_.getRdataCount(), bv4.size());
-
- NameserverEntry::AddressVector bv6;
- beta.getAddresses(bv6, AF_INET6);
- EXPECT_EQ(0, bv6.size());
-
- // Check that the addresses received are unique.
- SCOPED_TRACE("Checking V4 addresses");
- CompareAddresses(bv4, rrv4_);
-
- // Initialize with V6 addresses only
- EXPECT_TRUE(rrv6_.getRdataCount() > 0);
- NameserverEntry gamma(NULL, &rrv6_);
-
- NameserverEntry::AddressVector cv;
- gamma.getAddresses(cv);
- EXPECT_EQ(rrv6_.getRdataCount(), cv.size());
-
- NameserverEntry::AddressVector cv4;
- gamma.getAddresses(cv4, AF_INET);
- EXPECT_EQ(0, cv4.size());
-
- NameserverEntry::AddressVector cv6;
- gamma.getAddresses(cv6, AF_INET6);
- EXPECT_EQ(rrv6_.getRdataCount(), cv6.size());
-
- SCOPED_TRACE("Checking V6 addresses");
- CompareAddresses(cv6, rrv6_);
-
- // Initialize with both sets of addresses
- NameserverEntry delta(&rrv4_, &rrv6_);
-
- NameserverEntry::AddressVector dv;
- delta.getAddresses(dv);
- EXPECT_EQ((rrv4_.getRdataCount() + rrv6_.getRdataCount()), dv.size());
-
- NameserverEntry::AddressVector dv4;
- delta.getAddresses(dv4, AF_INET);
- EXPECT_EQ(rrv4_.getRdataCount(), dv4.size());
- SCOPED_TRACE("Checking V4 addresses after dual-address family constructor");
- CompareAddresses(dv4, rrv4_);
-
- NameserverEntry::AddressVector dv6;
- delta.getAddresses(dv6, AF_INET6);
- EXPECT_EQ(rrv6_.getRdataCount(), dv6.size());
- SCOPED_TRACE("Checking V6 addresses after dual-address family constructor");
- CompareAddresses(dv6, rrv6_);
-
- // ... and check that the composite of the v4 and v6 addresses is the same
- // as that returned by the get without a filter.
- NameserverEntry::AddressVector dvcomponent;
- delta.getAddresses(dvcomponent, AF_INET);
- delta.getAddresses(dvcomponent, AF_INET6);
- SCOPED_TRACE("Checking V4+V6 addresses same as composite return");
- CompareAddressVectors(dv, dvcomponent);
+// Test the the RTT on tthe created addresses is not 0 and is different
+TEST_F(NameserverEntryTest, InitialRTT) {
+
+ // Get the RTT for the different addresses
+ shared_ptr<NameserverEntry> alpha(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(alpha, rrv4_, rrv6_);
+ NameserverEntry::AddressVector vec;
+ alpha->getAddresses(vec);
+
+ // Check they are not 0 and they are all small, they should be some kind
+ // of randomish numbers, so we can't expect much more here
+ BOOST_FOREACH(NameserverAddress& entry, vec) {
+ EXPECT_GT(entry.getAddressEntry().getRTT(), 0);
+ // 20 is some arbitrary small value
+ EXPECT_LT(entry.getAddressEntry().getRTT(), 20);
+ }
}
// Set an address RTT to a given value
TEST_F(NameserverEntryTest, SetRTT) {
// Get the RTT for the different addresses
- NameserverEntry alpha(&rrv4_, &rrv6_);
+ shared_ptr<NameserverEntry> alpha(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(alpha, rrv4_, rrv6_);
NameserverEntry::AddressVector vec;
- alpha.getAddresses(vec);
-
- EXPECT_TRUE(vec.size() > 0);
+ alpha->getAddresses(vec);
+
+ ASSERT_TRUE(vec.size() > 0);
// Take the first address and change the RTT.
IOAddress first_address = vec[0].getAddress();
- uint32_t first_rtt = vec[0].getRTT();
+ uint32_t first_rtt = vec[0].getAddressEntry().getRTT();
uint32_t new_rtt = first_rtt + 42;
- alpha.setAddressRTT(first_address, new_rtt);
+ alpha->setAddressRTT(first_address, new_rtt);
// Now see if it has changed
NameserverEntry::AddressVector newvec;
- alpha.getAddresses(newvec);
+ alpha->getAddresses(newvec);
int matchcount = 0;
for (NameserverEntry::AddressVectorIterator i = newvec.begin();
i != newvec.end(); ++i) {
if (i->getAddress().equal(first_address)) {
++matchcount;
- EXPECT_EQ(i->getRTT(), new_rtt);
+ EXPECT_EQ(i->getAddressEntry().getRTT(), new_rtt);
}
}
@@ -318,28 +168,30 @@
TEST_F(NameserverEntryTest, Unreachable) {
// Get the RTT for the different addresses
- NameserverEntry alpha(&rrv4_, &rrv6_);
+ shared_ptr<NameserverEntry> alpha(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(alpha, rrv4_, rrv6_);
NameserverEntry::AddressVector vec;
- alpha.getAddresses(vec);
-
- EXPECT_TRUE(vec.size() > 0);
+ alpha->getAddresses(vec);
+
+ ASSERT_TRUE(vec.size() > 0);
// Take the first address and mark as unreachable.
IOAddress first_address = vec[0].getAddress();
- EXPECT_FALSE(vec[0].isUnreachable());
-
- alpha.setAddressUnreachable(first_address);
+ EXPECT_FALSE(vec[0].getAddressEntry().isUnreachable());
+
+ alpha->setAddressUnreachable(first_address);
// Now see if it has changed
NameserverEntry::AddressVector newvec;
- alpha.getAddresses(newvec);
+ alpha->getAddresses(newvec);
int matchcount = 0;
for (NameserverEntry::AddressVectorIterator i = newvec.begin();
i != newvec.end(); ++i) {
if (i->getAddress().equal(first_address)) {
++matchcount;
- EXPECT_TRUE(i->isUnreachable());
+ EXPECT_TRUE(i->getAddressEntry().isUnreachable());
}
}
@@ -352,32 +204,42 @@
// Note that for testing purposes we use the three-argument NameserverEntry
// constructor (where we supply the time). It eliminates intermittent errors
// cause when this test is run just as the clock "ticks over" to another second.
+// TODO Return the way where we can pass time inside somehow
TEST_F(NameserverEntryTest, ExpirationTime) {
time_t curtime = time(NULL);
time_t expiration = 0;
// Test where there is a single TTL
- NameserverEntry alpha(&rrv4_, NULL, curtime);
- expiration = alpha.getExpiration();
- EXPECT_EQ(expiration, curtime + rrv4_.getTTL().getValue());
-
- NameserverEntry beta(NULL, &rrv6_, curtime);
- expiration = beta.getExpiration();
- EXPECT_EQ(expiration, curtime + rrv6_.getTTL().getValue());
+ shared_ptr<NameserverEntry> alpha(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(alpha, rrv4_, shared_ptr<BasicRRset>());
+ expiration = alpha->getExpiration();
+ EXPECT_EQ(expiration, curtime + rrv4_->getTTL().getValue());
+
+ shared_ptr<NameserverEntry> beta(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(beta, shared_ptr<BasicRRset>(), rrv6_);
+ expiration = beta->getExpiration();
+ EXPECT_EQ(expiration, curtime + rrv6_->getTTL().getValue());
// Test where there are two different TTLs
- EXPECT_NE(rrv4_.getTTL().getValue(), rrv6_.getTTL().getValue());
- NameserverEntry gamma(&rrv4_, &rrv6_, curtime);
- uint32_t minttl = min(rrv4_.getTTL().getValue(), rrv6_.getTTL().getValue());
+ EXPECT_NE(rrv4_->getTTL().getValue(), rrv6_->getTTL().getValue());
+ shared_ptr<NameserverEntry> gamma(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(gamma, rrv4_, rrv6_);
+ uint32_t minttl = min(rrv4_->getTTL().getValue(), rrv6_->getTTL().getValue());
+ expiration = gamma->getExpiration();
EXPECT_EQ(expiration, curtime + minttl);
// Finally check where we don't specify a current time. All we know is
// that the expiration time should be greater than the TTL (as the current
// time is greater than zero).
- NameserverEntry delta(&rrv4_, NULL);
- EXPECT_GT(delta.getExpiration(), rrv4_.getTTL().getValue());
+ shared_ptr<NameserverEntry> delta(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(delta, rrv4_, shared_ptr<BasicRRset>());
+ EXPECT_GT(delta->getExpiration(), rrv4_->getTTL().getValue());
}
@@ -385,182 +247,273 @@
TEST_F(NameserverEntryTest, CheckName) {
// Default constructor
- NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::IN().getCode());
+ NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::IN());
EXPECT_EQ(EXAMPLE_CO_UK, alpha.getName());
-
- // Address constructor
- NameserverEntry beta(&rrv4_, NULL);
- EXPECT_EQ(EXAMPLE_CO_UK, beta.getName());
-
- NameserverEntry gamma(NULL, &rrv6_);
- EXPECT_EQ(EXAMPLE_CO_UK, gamma.getName());
-
- NameserverEntry delta(&rrv4_, &rrv6_);
- EXPECT_EQ(EXAMPLE_CO_UK, delta.getName());
-
- // Check that the name is not canonicalised
- NameserverEntry epsilon(&rrcase_, NULL);
- EXPECT_EQ(MIXED_EXAMPLE_CO_UK, epsilon.getName());
-
- // Check that inconsistent names mean that an exception is generated.
- EXPECT_THROW(NameserverEntry zeta(&rrnet_, &rrv6_),
- isc::nsas::InconsistentOwnerNames);
}
// Check that it can cope with non-IN classes.
TEST_F(NameserverEntryTest, CheckClass) {
// Default constructor
- NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::CH().getCode());
- EXPECT_EQ(RRClass::CH().getCode(), alpha.getClass());
-
- // Address constructor
- NameserverEntry beta(&rrch_, NULL);
- EXPECT_EQ(RRClass::CH().getCode(), beta.getClass());
-
- // Ensure that inconsistent class throws an exception
- EXPECT_THROW(NameserverEntry gamma(&rrch_, &rrv6_),
- isc::nsas::InconsistentClass);
-
-}
-
-// Select one address from the address list
-TEST_F(NameserverEntryTest, AddressSelection) {
- const int repeats = 100000;
- boost::shared_ptr<NameserverEntry> ns(new NameserverEntry(&rrv4_, &rrv6_));
-
- NameserverEntry::AddressVector v4Addresses;
- NameserverEntry::AddressVector v6Addresses;
- ns->getAddresses(v4Addresses, AF_INET);
- ns->getAddresses(v6Addresses, AF_INET6);
-
- int c1 = 0;
- int c2 = 0;
- int c3 = 0;
- NameserverAddress ns_address;
- for(int i = 0; i < repeats; ++i){
- ns.get()->getAddress(ns_address, AF_INET);
- asiolink::IOAddress io_address = ns_address.getAddress();
- if(io_address.toText() == v4Addresses[0].getAddress().toText()) ++c1;
- else if(io_address.toText() == v4Addresses[1].getAddress().toText()) ++c2;
- else if(io_address.toText() == v4Addresses[2].getAddress().toText()) ++c3;
- }
- // 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));
- ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
- ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
- ASSERT_TRUE(fabs(c3 - mu) < 4*sigma);
-
- // update the rtt to 1, 2, 3
- ns->setAddressRTT(v4Addresses[0].getAddress(), 1);
- ns->setAddressRTT(v4Addresses[1].getAddress(), 2);
- ns->setAddressRTT(v4Addresses[2].getAddress(), 3);
- c1 = c2 = c3 = 0;
- for(int i = 0; i < repeats; ++i){
- ns.get()->getAddress(ns_address, AF_INET);
- asiolink::IOAddress io_address = ns_address.getAddress();
- if(io_address.toText() == v4Addresses[0].getAddress().toText()) ++c1;
- else if(io_address.toText() == v4Addresses[1].getAddress().toText()) ++c2;
- else if(io_address.toText() == v4Addresses[2].getAddress().toText()) ++c3;
- }
-
- // We expect that the selection probability for each address that
- // it will be in the range of [mu-4Sigma, mu+4Sigma]
- double p1 = 1.0/(1.0 + 1.0/4.0 + 1.0/9.0);
- double p2 = (1.0/4.0)/(1.0 + 1.0/4.0 + 1.0/9.0);
- double p3 = (1.0/9.0)/(1.0 + 1.0/4.0 + 1.0/9.0);
- double mu1 = repeats * 0.7347;
- double mu2 = repeats * 0.18367;
- double mu3 = repeats * 0.08163;
- double sigma1 = sqrt(repeats * p1 * (1 - p1));
- double sigma2 = sqrt(repeats * p2 * (1 - p2));
- double sigma3 = sqrt(repeats * p3 * (1 - p3));
- ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
- ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
- ASSERT_TRUE(fabs(c3 - mu3) < 4*sigma3);
-
- // Test unreachable address
- ns->setAddressRTT(v4Addresses[0].getAddress(), 1);
- ns->setAddressRTT(v4Addresses[1].getAddress(), 100);
- ns->setAddressUnreachable(v4Addresses[2].getAddress());
- c1 = c2 = c3 = 0;
- for(int i = 0; i < repeats; ++i){
- ns.get()->getAddress(ns_address, AF_INET);
- asiolink::IOAddress io_address = ns_address.getAddress();
- if(io_address.toText() == v4Addresses[0].getAddress().toText()) ++c1;
- else if(io_address.toText() == v4Addresses[1].getAddress().toText()) ++c2;
- else if(io_address.toText() == v4Addresses[2].getAddress().toText()) ++c3;
- }
-
- // The 3rd address should not be selected again
- ASSERT_EQ(0, c3);
-
- // Test if all the servers are unrachable
- ns->setAddressUnreachable(v4Addresses[0].getAddress());
- ns->setAddressUnreachable(v4Addresses[1].getAddress());
- ns->setAddressUnreachable(v4Addresses[2].getAddress());
- c1 = c2 = c3 = 0;
- for(int i = 0; i < repeats; ++i){
- ns.get()->getAddress(ns_address, AF_INET);
- asiolink::IOAddress io_address = ns_address.getAddress();
- if(io_address.toText() == v4Addresses[0].getAddress().toText()) ++c1;
- else if(io_address.toText() == v4Addresses[1].getAddress().toText()) ++c2;
- else if(io_address.toText() == v4Addresses[2].getAddress().toText()) ++c3;
- }
-
- // All the unreachable servers should be selected with equal opportunity
- ASSERT_EQ(1, (int)(c1*1.0/c2 + 0.5));
- ASSERT_EQ(1, (int)(c1*1.0/c3 + 0.5));
- p = 1.0 / 3.0;
- mu = repeats * p;
- sigma = sqrt(repeats * p * (1 - p));
- ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
- ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
- ASSERT_TRUE(fabs(c3 - mu) < 4*sigma);
-
- // TODO: The unreachable server should be changed to reachable after 5minutes, but how to test?
+ NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::CH());
+ EXPECT_EQ(RRClass::CH(), alpha.getClass());
+}
+
+// Tests if it asks the IP addresses and calls callbacks when it comes
+// including the right addresses are returned and that they expire
+TEST_F(NameserverEntryTest, IPCallbacks) {
+ shared_ptr<NameserverEntry> entry(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ shared_ptr<Callback> callback(new Callback);
+ shared_ptr<TestResolver> resolver(new TestResolver);
+
+ entry->askIP(resolver, callback, ANY_OK);
+ // Ensure it becomes IN_PROGRESS
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
+ // Now, there should be two queries in the resolver
+ EXPECT_EQ(2, resolver->requests.size());
+ ASSERT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
+
+ // Another one might ask
+ entry->askIP(resolver, callback, V4_ONLY);
+ // There should still be only two queries in the resolver
+ ASSERT_EQ(2, resolver->requests.size());
+
+ // Another one, with need of IPv6 address
+ entry->askIP(resolver, callback, V6_ONLY);
+
+ // Answer one and see that the callbacks are called
+ resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
+ rdata::in::A("192.0.2.1"));
+
+ // Both callbacks that want IPv4 should be called by now
+ EXPECT_EQ(2, callback->count);
+ // It should contain one IP address
+ NameserverEntry::AddressVector addresses;
+ // Still in progress, waiting for the other address
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getAddresses(addresses));
+ EXPECT_EQ(1, addresses.size());
+ // Answer IPv6 address
+ // It is with zero TTL, so it should expire right away
+ resolver->answer(1, Name(EXAMPLE_CO_UK), RRType::AAAA(),
+ rdata::in::AAAA("2001:db8::1"), 0);
+ // The other callback should appear
+ EXPECT_EQ(3, callback->count);
+ // It should return the one address. It should be expired, but
+ // we ignore it for now
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V6_ONLY, true));
+ // Another address should appear
+ EXPECT_EQ(2, addresses.size());
+ // But when we do not ignore it, it should not appear
+ EXPECT_EQ(Fetchable::EXPIRED, entry->getAddresses(addresses, V6_ONLY));
+ EXPECT_EQ(2, addresses.size());
+}
+
+// Test the callback is called even when the address is unreachable
+TEST_F(NameserverEntryTest, IPCallbacksUnreachable) {
+ shared_ptr<NameserverEntry> entry(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ shared_ptr<Callback> callback(new Callback);
+ shared_ptr<TestResolver> resolver(new TestResolver);
+
+ // Ask for its IP
+ entry->askIP(resolver, callback, ANY_OK);
+ // Check it asks the resolver
+ EXPECT_EQ(2, resolver->requests.size());
+ ASSERT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
+ resolver->requests[0].second->failure();
+ // It should still wait for the second one
+ EXPECT_EQ(0, callback->count);
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
+ // It should call the callback now and be unrechable
+ resolver->requests[1].second->failure();
+ EXPECT_EQ(1, callback->count);
+ EXPECT_EQ(Fetchable::UNREACHABLE, entry->getState());
+ NameserverEntry::AddressVector addresses;
+ EXPECT_EQ(Fetchable::UNREACHABLE, entry->getAddresses(addresses));
+ EXPECT_EQ(0, addresses.size());
+}
+
+/*
+ * Tests that it works even when we provide the answer right away, directly
+ * from resolve.
+ */
+TEST_F(NameserverEntryTest, DirectAnswer) {
+ shared_ptr<NameserverEntry> entry(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ shared_ptr<Callback> callback(new Callback);
+ shared_ptr<TestResolver> resolver(new TestResolver);
+ resolver->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
+ RRType::A()), rrv4_);
+ resolver->addPresetAnswer(Question(Name(EXAMPLE_CO_UK), RRClass::IN(),
+ RRType::AAAA()), rrv6_);
+ resolver->addPresetAnswer(Question(Name(EXAMPLE_NET), RRClass::IN(),
+ RRType::A()), shared_ptr<AbstractRRset>());
+ resolver->addPresetAnswer(Question(Name(EXAMPLE_NET), RRClass::IN(),
+ RRType::AAAA()), shared_ptr<AbstractRRset>());
+
+ // A successfull test first
+ entry->askIP(resolver, callback, ANY_OK);
+ EXPECT_EQ(0, resolver->requests.size());
+ EXPECT_EQ(1, callback->count);
+ NameserverEntry::AddressVector addresses;
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses));
+ EXPECT_EQ(5, addresses.size());
+
+ // An unsuccessfull test
+ callback->count = 0;
+ entry.reset(new NameserverEntry(EXAMPLE_NET, RRClass::IN()));
+ entry->askIP(resolver, callback, ANY_OK);
+ EXPECT_EQ(0, resolver->requests.size());
+ EXPECT_EQ(1, callback->count);
+ addresses.clear();
+ EXPECT_EQ(Fetchable::UNREACHABLE, entry->getAddresses(addresses));
+ EXPECT_EQ(0, addresses.size());
+}
+
+/*
+ * This one tests if it works when the data time out and a different
+ * data is received the next time.
+ */
+TEST_F(NameserverEntryTest, ChangedExpired) {
+ shared_ptr<NameserverEntry> entry(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ shared_ptr<Callback> callback(new Callback);
+ shared_ptr<TestResolver> resolver(new TestResolver);
+
+ // Ask the first time
+ entry->askIP(resolver, callback, V4_ONLY);
+ entry->askIP(resolver, callback, V6_ONLY);
+ EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
+ resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
+ rdata::in::A("192.0.2.1"), 0);
+ resolver->answer(1, Name(EXAMPLE_CO_UK), RRType::AAAA(),
+ rdata::in::AAAA("2001:db8::1"), 0);
+ EXPECT_EQ(2, callback->count);
+ NameserverEntry::AddressVector addresses;
+ // We must accept expired as well, as the TTL is 0 (and it is OK,
+ // because we just got the callback)
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V4_ONLY, true));
+ ASSERT_EQ(1, addresses.size());
+ EXPECT_EQ("192.0.2.1", addresses[0].getAddress().toText());
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V6_ONLY, true));
+ ASSERT_EQ(2, addresses.size());
+ EXPECT_EQ("2001:db8::1", addresses[1].getAddress().toText());
+
+ // Ask the second time. The callbacks should not fire right away and it
+ // should request the addresses again
+ entry->askIP(resolver, callback, V4_ONLY);
+ entry->askIP(resolver, callback, V6_ONLY);
+ EXPECT_EQ(2, callback->count);
+ EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 2, 3));
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
+ resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
+ rdata::in::A("192.0.2.2"));
+ resolver->answer(1, Name(EXAMPLE_CO_UK), RRType::AAAA(),
+ rdata::in::AAAA("2001:db8::2"));
+ // We should get the new addresses and they should not expire,
+ // so we should get them without accepting expired
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V4_ONLY));
+ ASSERT_EQ(3, addresses.size());
+ EXPECT_EQ("192.0.2.2", addresses[2].getAddress().toText());
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V6_ONLY));
+ ASSERT_EQ(4, addresses.size());
+ EXPECT_EQ("2001:db8::2", addresses[3].getAddress().toText());
+}
+
+/*
+ * When the data expire and is asked again, the original RTT is kept.
+ */
+TEST_F(NameserverEntryTest, KeepRTT) {
+ shared_ptr<NameserverEntry> entry(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ shared_ptr<Callback> callback(new Callback);
+ shared_ptr<TestResolver> resolver(new TestResolver);
+
+ // Ask the first time
+ entry->askIP(resolver, callback, V4_ONLY);
+ entry->askIP(resolver, callback, V6_ONLY);
+ EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 0, 1));
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
+ resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
+ rdata::in::A("192.0.2.1"), 0);
+ resolver->answer(1, Name(EXAMPLE_CO_UK), RRType::AAAA(),
+ rdata::in::AAAA("2001:db8::1"), 0);
+ EXPECT_EQ(2, callback->count);
+ NameserverEntry::AddressVector addresses;
+ // We must accept expired as well, as the TTL is 0 (and it is OK,
+ // because we just got the callback)
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V4_ONLY, true));
+ ASSERT_EQ(1, addresses.size());
+ EXPECT_EQ("192.0.2.1", addresses[0].getAddress().toText());
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V6_ONLY, true));
+ ASSERT_EQ(2, addresses.size());
+ EXPECT_EQ("2001:db8::1", addresses[1].getAddress().toText());
+ BOOST_FOREACH(const NameserverAddress& address, addresses) {
+ entry->setAddressRTT(address.getAddress(), 123);
+ }
+
+ // Ask the second time. The callbacks should not fire right away and it
+ // should request the addresses again
+ entry->askIP(resolver, callback, V4_ONLY);
+ entry->askIP(resolver, callback, V6_ONLY);
+ EXPECT_EQ(2, callback->count);
+ EXPECT_TRUE(resolver->asksIPs(Name(EXAMPLE_CO_UK), 2, 3));
+ EXPECT_EQ(Fetchable::IN_PROGRESS, entry->getState());
+ resolver->answer(0, Name(EXAMPLE_CO_UK), RRType::A(),
+ rdata::in::A("192.0.2.1"));
+ resolver->answer(1, Name(EXAMPLE_CO_UK), RRType::AAAA(),
+ rdata::in::AAAA("2001:db8::1"));
+ // We should get the new addresses and they should not expire,
+ // so we should get them without accepting expired
+ addresses.clear();
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V4_ONLY));
+ ASSERT_EQ(1, addresses.size());
+ EXPECT_EQ("192.0.2.1", addresses[0].getAddress().toText());
+ EXPECT_EQ(Fetchable::READY, entry->getAddresses(addresses, V6_ONLY));
+ ASSERT_EQ(2, addresses.size());
+ EXPECT_EQ("2001:db8::1", addresses[1].getAddress().toText());
+ // They should have the RTT we set to them
+ BOOST_FOREACH(NameserverAddress& address, addresses) {
+ EXPECT_EQ(123, address.getAddressEntry().getRTT());
+ }
}
// Test the RTT is updated smoothly
TEST_F(NameserverEntryTest, UpdateRTT) {
- NameserverEntry ns(&rrv4_, &rrv6_);
+ shared_ptr<NameserverEntry> ns(new NameserverEntry(EXAMPLE_CO_UK,
+ RRClass::IN()));
+ fillNSEntry(ns, rrv4_, rrv6_);
NameserverEntry::AddressVector vec;
- ns.getAddresses(vec);
+ ns->getAddresses(vec);
// Initialize the rtt with a small value
uint32_t init_rtt = 1;
- ns.setAddressRTT(vec[0].getAddress(), init_rtt);
+ ns->setAddressRTT(vec[0].getAddress(), init_rtt);
// The rtt will be stablized to a large value
uint32_t stable_rtt = 100;
// Update the rtt
- ns.updateAddressRTTAtIndex(stable_rtt, 0, AF_INET);
+ vec[0].updateRTT(stable_rtt);
vec.clear();
- ns.getAddresses(vec);
- uint32_t new_rtt = vec[0].getRTT();
+ ns->getAddresses(vec);
+ uint32_t new_rtt = vec[0].getAddressEntry().getRTT();
// The rtt should not close to new rtt immediately
- ASSERT_TRUE((stable_rtt - new_rtt) > (new_rtt - init_rtt));
+ EXPECT_TRUE((stable_rtt - new_rtt) > (new_rtt - init_rtt));
// Update the rtt for enough times
for(int i = 0; i < 10000; ++i){
- ns.updateAddressRTTAtIndex(stable_rtt, 0, AF_INET);
+ vec[0].updateRTT(stable_rtt);
}
vec.clear();
- ns.getAddresses(vec);
- new_rtt = vec[0].getRTT();
+ ns->getAddresses(vec);
+ new_rtt = vec[0].getAddressEntry().getRTT();
// The rtt should be close to stable rtt value
- ASSERT_TRUE((stable_rtt - new_rtt) < (new_rtt - init_rtt));
-}
-
-} // namespace nsas
-} // namespace isc
+ EXPECT_TRUE((stable_rtt - new_rtt) < (new_rtt - init_rtt));
+}
+
+} // namespace
Modified: branches/trac356/src/lib/nsas/tests/nsas_entry_compare_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/nsas_entry_compare_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/nsas_entry_compare_unittest.cc Mon Dec 13 15:25:09 2010
@@ -21,8 +21,8 @@
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
-#include "hash_key.h"
-#include "nsas_entry_compare.h"
+#include "../hash_key.h"
+#include "../nsas_entry_compare.h"
#include "nsas_test.h"
using namespace std;
@@ -39,9 +39,9 @@
TEST_F(NsasEntryCompareTest, Compare) {
// Construct a couple of different objects
- TestEntry entry1("test1", 42);
- TestEntry entry2("test1", 24);
- TestEntry entry3("test2", 42);
+ TestEntry entry1("test1", RRClass(42));
+ TestEntry entry2("test1", RRClass(24));
+ TestEntry entry3("test2", RRClass(42));
// Create corresponding hash key objects
HashKey key1(entry1.getName(), entry1.getClass());
Modified: branches/trac356/src/lib/nsas/tests/nsas_test.h
==============================================================================
--- branches/trac356/src/lib/nsas/tests/nsas_test.h (original)
+++ branches/trac356/src/lib/nsas/tests/nsas_test.h Mon Dec 13 15:25:09 2010
@@ -17,20 +17,24 @@
#ifndef __NSAS_TEST_H
#define __NSAS_TEST_H
-/// \file test_utilities.h
+/// \file nsas_test.h
///
/// Contains miscellaneous classes and other stuff to help with the nameserver
/// address store tests.
#include <string>
-
-#include "config.h"
-
-#include "buffer.h"
-#include "rdata.h"
-#include "rrtype.h"
-#include "messagerenderer.h"
-#include "nsas_entry.h"
+#include <vector>
+
+#include <config.h>
+
+#include <dns/buffer.h>
+#include <dns/rdata.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdataclass.h>
+#include "../nsas_entry.h"
+#include "../resolver_interface.h"
using namespace isc::dns::rdata;
using namespace isc::dns;
@@ -53,12 +57,6 @@
public:
uint16_t getType() const
{return RRType::AAAA().getCode();}
-};
-
-class NS {
-public:
- uint16_t getType() const
- {return RRType::NS().getCode();}
};
class MX {
@@ -155,7 +153,7 @@
/// \param name Name that will be used for the object. This will form
/// part of the key.
/// \param class_code Class associated with the object.
- TestEntry(std::string name, uint16_t class_code) :
+ TestEntry(std::string name, const isc::dns::RRClass& class_code) :
name_(name), class_code_(class_code)
{}
@@ -188,20 +186,20 @@
/// \brief Get the Class
///
/// \return Class code assigned to this object
- virtual uint16_t getClass() const {
+ virtual const isc::dns::RRClass& getClass() const {
return class_code_;
}
/// \brief Set the Class
///
/// \param class_code New class code of the object
- virtual void setClass(uint16_t class_code) {
+ virtual void setClass(const isc::dns::RRClass& class_code) {
class_code_ = class_code;
}
private:
std::string name_; ///< Name of the object
- uint16_t class_code_; ///< Class of the object
+ isc::dns::RRClass class_code_; ///< Class of the object
};
/// \brief isc::nsas Constants
@@ -213,4 +211,201 @@
} // namespace nsas
} // namespace isc
+namespace {
+
+using namespace std;
+
+/*
+ * This pretends to be a resolver. It stores the queries and
+ * they can be answered.
+ */
+class TestResolver : public isc::nsas::ResolverInterface {
+ private:
+ bool checkIndex(size_t index) {
+ return (requests.size() > index);
+ }
+
+ typedef std::map<isc::dns::Question, boost::shared_ptr<AbstractRRset> >
+ PresetAnswers;
+ PresetAnswers answers_;
+ public:
+ typedef pair<QuestionPtr, CallbackPtr> Request;
+ vector<Request> requests;
+ 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(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,
+ boost::shared_ptr<AbstractRRset> 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.
+ * Provide index of a query and the address to pass.
+ */
+ void answer(size_t index, const Name& name, const RRType& type,
+ const rdata::Rdata& rdata, size_t TTL = 100)
+ {
+ if (index >= requests.size()) {
+ throw NoSuchRequest();
+ }
+ RRsetPtr set(new RRset(name, RRClass::IN(),
+ type, RRTTL(TTL)));
+ set->addRdata(rdata);
+ requests[index].second->success(set);
+ }
+
+ void provideNS(size_t index,
+ boost::shared_ptr<AbstractRRset> 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(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.");
+static const std::string MIXED_EXAMPLE_CO_UK("EXAmple.co.uk.");
+
+class TestWithRdata : public ::testing::Test {
+protected:
+ typedef boost::shared_ptr<RRset> RRsetPtr;
+ /// \brief Constructor
+ ///
+ /// Initializes the RRsets used in the tests. The RRsets themselves have to
+ /// be initialized with the basic data on their construction. The Rdata for
+ /// them is added in SetUp().
+ TestWithRdata() :
+ rrv4_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::A(),
+ RRTTL(1200))),
+ rrcase_(new RRset(Name(MIXED_EXAMPLE_CO_UK), RRClass::IN(),
+ RRType::A(), RRTTL(1200))),
+ rrch_(new RRset(Name(EXAMPLE_CO_UK), RRClass::CH(), RRType::A(),
+ RRTTL(1200))),
+ rrns_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(), RRType::NS(),
+ RRTTL(1200))),
+ rr_single_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
+ RRType::NS(), RRTTL(600))),
+ rr_empty_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
+ RRType::NS(), RRTTL(600))),
+ rrv6_(new RRset(Name(EXAMPLE_CO_UK), RRClass::IN(),
+ RRType::AAAA(), RRTTL(900))),
+ rrnet_(new RRset(Name(EXAMPLE_NET), RRClass::IN(), RRType::A(),
+ RRTTL(600))),
+ ns_name_("ns.example.net.")
+ {}
+
+ /// \brief Add Rdata to RRsets
+ ///
+ /// The data are added as const pointers to avoid the stricter type checking
+ /// applied by the Rdata code. There is no need for it in these tests.
+ virtual void SetUp() {
+
+ // A records
+ rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("1.2.3.4")));
+ rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("5.6.7.8")));
+ rrv4_->addRdata(ConstRdataPtr(new RdataTest<A>("9.10.11.12")));
+
+ // A records
+ rrcase_->addRdata(ConstRdataPtr(new RdataTest<A>("13.14.15.16")));
+
+ // No idea what Chaosnet address look like other than they are 16 bits
+ // The fact that they are type A is probably also incorrect.
+ rrch_->addRdata(ConstRdataPtr(new RdataTest<A>("1324")));
+
+ // NS records take a single name
+ rrns_->addRdata(rdata::generic::NS("example.fr"));
+ rrns_->addRdata(rdata::generic::NS("example.de"));
+
+ // Single NS record with 0 TTL
+ rr_single_->addRdata(rdata::generic::NS(ns_name_));
+
+ // AAAA records
+ rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("2001::1002")));
+ rrv6_->addRdata(ConstRdataPtr(new RdataTest<AAAA>("dead:beef:feed::")));
+
+ // A record for example.net
+ rrnet_->addRdata(ConstRdataPtr(new RdataTest<A>("17.18.18.20")));
+ }
+
+ /// \brief Data for the tests
+ RRsetPtr rrv4_; ///< Standard RRSet - IN, A, lowercase name
+ RRsetPtr rrcase_; ///< Mixed-case name
+ RRsetPtr rrch_; ///< Non-IN RRset (Chaos in this case)
+ RRsetPtr rrns_; ///< NS RRset
+ RRsetPtr rr_single_; ///< NS RRset with single NS
+ RRsetPtr rr_empty_; ///< NS RRset without any nameservers
+ RRsetPtr rrv6_; ///< Standard RRset, IN, AAAA, lowercase name
+ RRsetPtr rrnet_; ///< example.net A RRset
+ Name ns_name_; ///< Nameserver name of ns.example.net
+};
+
+} // Empty namespace
+
#endif // __NSAS_TEST_H
Modified: branches/trac356/src/lib/nsas/tests/zone_entry_unittest.cc
==============================================================================
--- branches/trac356/src/lib/nsas/tests/zone_entry_unittest.cc (original)
+++ branches/trac356/src/lib/nsas/tests/zone_entry_unittest.cc Mon Dec 13 15:25:09 2010
@@ -15,38 +15,739 @@
// $Id$
#include <gtest/gtest.h>
-
-#include "rrclass.h"
-
-#include "asiolink.h"
-#include "zone_entry.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 boost;
using namespace isc::dns;
-namespace isc {
-namespace nsas {
-
-// String constants. These should end in a dot.
-static const std::string EXAMPLE_CO_UK("example.co.uk.");
+namespace {
+
+/// \brief Inherited version with access into its internals for tests
+class InheritedZoneEntry : public ZoneEntry {
+ public:
+ InheritedZoneEntry(shared_ptr<ResolverInterface> resolver,
+ const std::string& name, const RRClass& class_code,
+ shared_ptr<HashTable<NameserverEntry> > nameserver_table,
+ 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 ::testing::Test {
+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
+ shared_ptr<HashTable<NameserverEntry> > nameserver_table_;
+ shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
+ /// \brief The resolver
+ 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);
+ }
+ };
+ shared_ptr<Callback> callback_;
+
+ // Empty callback to pass to nameserver entry, to do injection of them
+ struct NseCallback : public NameserverEntry::Callback {
+ virtual void operator()(shared_ptr<NameserverEntry>) { }
+ };
+
+ shared_ptr<NseCallback> nseCallback() {
+ return (shared_ptr<NseCallback>(new NseCallback));
+ }
+
+ /**
+ * \short Function returning a new zone.
+ *
+ * Convenience funcion, just creating a new zone, to shorten the code.
+ */
+ shared_ptr<InheritedZoneEntry> getZone() {
+ return (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.
+ */
+ shared_ptr<NameserverEntry> injectEntry() {
+ 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
+ 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
- ZoneEntry alpha(EXAMPLE_CO_UK, RRClass::IN().getCode());
+ InheritedZoneEntry alpha(resolver_, EXAMPLE_CO_UK,
+ RRClass::IN(), nameserver_table_, nameserver_lru_);
EXPECT_EQ(EXAMPLE_CO_UK, alpha.getName());
- EXPECT_EQ(RRClass::IN().getCode(), alpha.getClass());
-}
-
-} // namespace nsas
-} // namespace isc
+ 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) {
+ 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));
+ 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) {
+ 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) {
+ 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) {
+ 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)
+ 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) {
+ 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()), shared_ptr<AbstractRRset>());
+ 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()),
+ shared_ptr<AbstractRRset>());
+ resolver_->addPresetAnswer(Question(ns_name, RRClass::IN(),
+ RRType::AAAA()), shared_ptr<AbstractRRset>());
+ // 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) {
+ 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
+ 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
+ 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) {
+ 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) {
+ 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)
+ 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();
+
+ 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
Modified: branches/trac356/src/lib/nsas/zone_entry.h
==============================================================================
--- branches/trac356/src/lib/nsas/zone_entry.h (original)
+++ branches/trac356/src/lib/nsas/zone_entry.h Mon Dec 13 15:25:09 2010
@@ -19,19 +19,26 @@
#include <string>
#include <vector>
+#include <set>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
-#include "rrset.h"
+#include <dns/rrset.h>
#include "hash_key.h"
#include "nsas_entry.h"
#include "asiolink.h"
+#include "fetchable.h"
+#include "resolver_interface.h"
+#include "nsas_types.h"
+#include "random_number_generator.h"
namespace isc {
namespace nsas {
class NameserverEntry;
+class AddressRequestCallback;
/// \brief Zone Entry
///
@@ -41,59 +48,128 @@
/// Although the interface is simple, the internal processing is fairly
/// complicated, in that the class takes account of triggering fetches for
/// addresses of nameservers when the address records expire.
+///
+/// It uses shared_from_this in its methods. It must live inside a shared_ptr.
-class ZoneEntry : public NsasEntry<ZoneEntry> {
+class ZoneEntry : public NsasEntry<ZoneEntry>, public Fetchable {
public:
- /// \brief Constructor where no NS records are supplied
- ///
- /// \param name Name of the zone
- /// \param class_code Class of this zone (zones of different classes have
- /// different objects.
- ZoneEntry(const std::string& name, uint16_t class_code) :
- name_(name), classCode_(class_code)
- {}
-
- /// \brief Constructor
- ///
- /// Creates a zone entry object with an RRset representing the nameservers,
- /// plus possibly additional RRsets holding address information.
- //ZoneEntry(isc::dns::AbstractRRset* nsrrset,
- // const std::vector<isc::dns::AbstractRRset*>& additional);
-
- /// \brief Destructor
- virtual ~ZoneEntry()
- {}
+ /**
+ * \brief Constructor.
+ *
+ * It asks the resolver any needed questions to get the nameservers.
+ *
+ * \param resolver The resolver used to ask for IP addresses
+ * \param name Name of the zone
+ * \param class_code Class of this zone (zones of different classes have
+ * different objects.
+ * \todo Move to cc file, include the lookup (if NSAS uses resolver for
+ * everything)
+ */
+ ZoneEntry(boost::shared_ptr<ResolverInterface> resolver,
+ const std::string& name, const isc::dns::RRClass& class_code,
+ boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table,
+ boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru);
/// \return Name of the zone
- virtual std::string getName() const {
+ std::string getName() const {
return name_;
}
/// \return Class of zone
- virtual short getClass() const {
- return classCode_;
+ const isc::dns::RRClass& getClass() const {
+ return class_code_;
}
/// \return Return Hash Key
virtual HashKey hashKey() const {
- return HashKey(name_, classCode_);
+ return HashKey(name_, class_code_);
}
- /// \brief Lookup Address
- ///
- /// Returns the address with the lowest RTT.
- //virtual asiolink::IOAddress getAddress() const;
+ /**
+ * \short Put another callback inside.
+ *
+ * This callback is either executed right away, if it is possible,
+ * or queued for later.
+ *
+ * \param callback The callback itself.
+ * \param family Which address family is acceptable as an answer?
+ */
+ void addCallback(boost::shared_ptr<AddressRequestCallback>
+ callback, AddressFamily family);
+ /// \short Protected members, so they can be accessed by tests.
+ //@{
+protected:
+ // TODO Read-Write lock?
+ typedef boost::shared_ptr<NameserverEntry> NameserverPtr;
+ typedef std::vector<NameserverPtr> NameserverVector;
+ NameserverVector nameservers_; ///< Nameservers
+ // Which nameservers didn't have any of our callbacks yet
+ std::set<NameserverPtr> nameservers_not_asked_;
+ /*
+ * Callbacks. For each fimily type one vector, so we can process
+ * them separately.
+ */
+ std::vector<boost::shared_ptr<AddressRequestCallback> >
+ callbacks_[ADDR_REQ_MAX];
+ time_t expiry_; ///< Expiry time of this entry, 0 means not set
+ //}@
private:
- boost::mutex mutex_; ///< Mutex protecting this zone entry
+ mutable boost::recursive_mutex mutex_; ///< Mutex protecting this zone entry
std::string name_; ///< Canonical zone name
- uint16_t classCode_; ///< Class code
- std::vector<boost::shared_ptr<NameserverEntry> > nameservers_; ///< Nameservers
- time_t expiry_; ///< Expiry time of this entry
+ isc::dns::RRClass class_code_; ///< Class code
+ /**
+ * \short Process all the callbacks that can be processed
+ *
+ * The purpose of this funtion is to ask all nameservers for their IP
+ * addresses and execute all callbacks that can be executed. It is
+ * called whenever new callback appears and there's a chance it could
+ * be answered or when new information is available (list of nameservers,
+ * nameserver is unreachable or has an address).
+ * \param family Which is the interesting address family where the change
+ * happened. ADDR_REQ_MAX means it could be any of them and it will
+ * trigger processing of all callbacks no matter what their family
+ * was.
+ * \param nameserver Pass a nameserver if the change was triggered by
+ * the nameserver (if it wasn't triggered by a nameserver, pass empty
+ * pointer). This one will be accepted even with 0 TTL, the information
+ * just arrived and we are allowed to use it just now.
+ * \todo With the recursive locks now, we might want to simplify executing
+ * callbacks (here and other functions as well);
+ */
+ void process(AddressFamily family,
+ const boost::shared_ptr<NameserverEntry>& nameserver);
+ // Resolver we use
+ boost::shared_ptr<ResolverInterface> resolver_;
+ // We store the nameserver table and lru, so we can look up when there's
+ // update
+ boost::shared_ptr<HashTable<NameserverEntry> > nameserver_table_;
+ boost::shared_ptr<LruList<NameserverEntry> > nameserver_lru_;
+ // Resolver callback class, documentation with the class declaration
+ class ResolverCallback;
+ // It has direct access to us
+ friend class ResolverCallback;
+ // Guard class to eliminate missing finally
+ class ProcessGuard;
+ friend class ProcessGuard;
+ // Are we in the process method?
+ bool in_process_[ADDR_REQ_MAX];
+ // Callback from nameserver entry (documented with the class)
+ class NameserverCallback;
+ // And it can get into our internals as well (call process)
+ friend class NameserverCallback;
+ // This dispatches callbacks of given family with failures
+ void dispatchFailures(AddressFamily family);
+ // Put a callback into the nameserver entry. Same ADDR_REQ_MAX means for
+ // all families
+ void insertCallback(NameserverPtr nameserver, AddressFamily family);
+ // A random generator for this zone entry
+ // TODO: A more global one? Per thread one?
+ WeightedRandomIntegerGenerator address_selector;
};
} // namespace nsas
} // namespace isc
-
+
#endif // __ZONE_ENTRY_H
More information about the bind10-changes
mailing list