Parcourir la source

First block of Nameserver Address Store code for review

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac356@3272 e5f2f494-b856-4b98-b285-d166d9295462
Stephen Morris il y a 14 ans
Parent
commit
db1f12078c

+ 2 - 0
configure.ac

@@ -506,6 +506,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/xfr/Makefile
+                 src/lib/nsas/Makefile
+                 src/lib/nsas/tests/Makefile
                ])
 AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/tests/b10-cfgmgr_test.py

+ 1 - 1
src/lib/Makefile.am

@@ -1 +1 @@
-SUBDIRS = exceptions dns cc config datasrc python xfr bench
+SUBDIRS = exceptions dns cc config datasrc python xfr bench nsas

+ 17 - 0
src/lib/nsas/Makefile.am

@@ -0,0 +1,17 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
+AM_CPPFLAGS += $(SQLITE_CFLAGS)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libnsas.la
+libnsas_la_SOURCES  = address_entry.h address_entry.cc
+libnsas_la_SOURCES += hash.cc hash.h
+libnsas_la_SOURCES += hash_table.h
+libnsas_la_SOURCES += lru_list.h
+libnsas_la_SOURCES += nameserver_entry.cc nameserver_entry.h
+libnsas_la_SOURCES += asiolink.h
+
+CLEANFILES = *.gcno *.gcda

+ 7 - 0
src/lib/nsas/README

@@ -0,0 +1,7 @@
+For an overview of the Nameserver Address Store, see the requirements and design
+documents at http://bind10.isc.org/wiki/Resolver.
+
+At the time of writing (19 October 2010), the file asiolink.h is present in this
+directory only for the purposes of development.  When the recursor's
+asynchronous I/O code has been finished, this will be removed and the NSAS will
+use the "real" code.

+ 44 - 0
src/lib/nsas/address_entry.cc

@@ -0,0 +1,44 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+/// \file address_entry.cc
+///
+/// This file exists to define the single constant \c AddressEntry::UNREACHABLE,
+/// equal to the value \c UINT32_MAX.
+///
+/// Ideally we could use \c UINT32_MAX directly in the header file, but this
+/// constant is defined in \c stdint.h only if the macro \c __STDC_LIMIT_MACROS
+/// is defined first. (This apparently is the C89 standard.)  Defining the
+/// macro in \c address_entry.h before including \c stdint.h doesn't work as
+/// it is possible that in a source file, \c stdint.h will be included before
+/// \c address_entry.h.  In that case, the \c stdint.h include sentinel will
+/// prevent \c stdint.h being included a second time and the value won't be
+/// defined.
+///
+/// The easiest solution is the one presented here: declare the value as a
+/// static class constant, and define it in this source file.  As we can control
+/// the order of include files, this ensures that the value is defined.
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#include "address_entry.h"
+
+namespace isc {
+namespace nsas {
+const uint32_t AddressEntry::UNREACHABLE = UINT32_MAX;
+}
+}

+ 94 - 0
src/lib/nsas/address_entry.h

@@ -0,0 +1,94 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __ADDRESS_ENTRY_H
+#define __ADDRESS_ENTRY_H
+
+/// \brief Address Entry
+///
+/// Lightweight class that couples an address with a RTT and provides some
+/// convenience methods for accessing and updating the information.
+
+#include <stdint.h>
+#include "asiolink.h"
+
+namespace isc {
+namespace nsas {
+
+class AddressEntry {
+public:
+    /// Creates an address entry given IOAddress entry and RTT
+    /// This is the only constructor; the default copy constructor and
+    /// assignment operator are valid for this object.
+    ///
+    /// \param address Address object representing this address
+    /// \param rtt Initial round-trip time
+    AddressEntry(const IOAddress& address, uint32_t rtt = 0) :
+        address_(address), rtt_(rtt)
+    {}
+
+    /// \return Address object
+    IOAddress getAddress() const {
+        return address_;
+    }
+
+    /// \return Current round-trip time
+    uint32_t getRTT() const {
+        return rtt_;
+    }
+
+    /// Set current RTT
+    ///
+    /// \param rtt New RTT to be associated with this address
+    void setRTT(uint32_t rtt) {
+        rtt_ = rtt;     // TODO: Modify to use weighting formula
+    }
+
+    /// Mark address as unreachable.
+    void setUnreachable() {
+        setRTT(UNREACHABLE);   // Largest long number is code for unreachable
+    }
+
+    /// Check if address is unreachable
+    ///
+    /// \return true if the address is unreachable, false if not
+    bool isUnreachable() const {
+        return (rtt_ == UNREACHABLE);
+    }
+
+    /// \return true if the object is a V4 address
+    bool isV4() const {
+        return (address_.getFamily() == AF_INET);
+    }
+
+    /// \return true if the object is a V6 address
+    bool isV6() const {
+        return (address_.getFamily() == AF_INET6);
+    }
+
+    // Next element is defined public for testing
+    static const uint32_t UNREACHABLE;  ///< RTT indicating unreachable address
+
+private:
+    IOAddress       address_;           ///< Address
+    uint32_t        rtt_;               ///< Round-trip time
+};
+
+}   // namespace dns
+}   // namespace isc
+
+
+#endif // __ADDRESS_ENTRY_H

+ 53 - 0
src/lib/nsas/asiolink.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __ASIOLINK_H
+#define __ASIOLINK_H
+
+#include <string>
+#include <sys/socket.h>
+
+/// \brief IO Address Dummy Class
+///
+/// As part of ther recursor, Evan has written the asiolink.h file, which
+/// encapsulates some of the boost::asio classes.  Until these are checked
+/// into trunk and merged with this branch, these dummy classes should fulfill
+/// their function.
+
+class IOAddress {
+public:
+    /// \param address_str String representing the address
+    IOAddress(const std::string& address_str) : address_(address_str)
+    {}
+
+    /// \return Textual representation of the address
+    std::string toText() const
+    {return address_;}
+
+    /// \return Address family of the address
+    virtual short getFamily() const {
+        return ((address_.find(".") != std::string::npos) ? AF_INET : AF_INET6);
+    }
+
+    /// \return true if two addresses are equal
+    bool equal(const IOAddress& address)
+    {return (toText() == address.toText());}
+
+private:
+    std::string     address_;       ///< Address represented
+};
+
+#endif // __ASIOLINK_H

+ 148 - 0
src/lib/nsas/hash.cc

@@ -0,0 +1,148 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+/*! \file
+ * Some parts of this code were copied from BIND-9, which in turn was derived
+ * from universal hash function libraries of Rice University.
+
+\section license UH Universal Hashing Library
+
+Copyright ((c)) 2002, Rice University
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+    copyright notice, this list of conditions and the following
+    disclaimer in the documentation and/or other materials provided
+    with the distribution.
+
+    * Neither the name of Rice University (RICE) nor the names of its
+    contributors may be used to endorse or promote products derived
+    from this software without specific prior written permission.
+
+
+This software is provided by RICE and the contributors on an "as is"
+basis, without any representations or warranties of any kind, express
+or implied including, but not limited to, representations or
+warranties of non-infringement, merchantability or fitness for a
+particular purpose. In no event shall RICE or contributors be liable
+for any direct, indirect, incidental, special, exemplary, or
+consequential damages (including, but not limited to, procurement of
+substitute goods or services; loss of use, data, or profits; or
+business interruption) however caused and on any theory of liability,
+whether in contract, strict liability, or tort (including negligence
+or otherwise) arising in any way out of the use of this software, even
+if advised of the possibility of such damage.
+*/
+
+#include <stdlib.h>
+#include <algorithm>
+#include <cassert>
+#include <string>
+
+#include "config.h"
+
+#include "hash.h"
+
+namespace isc {
+namespace nsas {
+
+// Constructor.
+
+Hash::Hash(uint32_t tablesize, uint32_t maxkeylen, bool randomise) :
+    tablesize_(tablesize), maxkeylen_(maxkeylen)
+{
+    // Check to see that we can cope with the maximum key length.
+    // (This code adapted from BIND-9)
+    //
+    // Overflow check.  Since our implementation only does a modulo
+    // operation at the last stage of hash calculation, the accumulator
+    // must not overflow.
+    hash_accum_t overflow_limit =
+        1 << (((sizeof(hash_accum_t) - sizeof(hash_random_t))) * 8);
+    if (overflow_limit < (maxkeylen + 1) * 0xff) {
+        isc_throw(KeyLengthTooLong, "Hash key length too long for Hash class");
+    }
+
+    // Initialize the random number generator with the current time.
+    // TODO: Use something other than pseudo-random numbers.
+    union {
+        unsigned int    seed;
+        time_t          curtime;
+    } init_value;
+
+    if (randomise) {
+        init_value.curtime = time(NULL);
+    }
+    else {
+        init_value.seed = 0;
+    }
+    srandom(init_value.seed);
+
+    // Fill in the random vector.
+    randvec_.reserve(maxkeylen + 1);
+    for (uint32_t i = 0; i < (maxkeylen + 1); ++i) {
+        randvec_.push_back(static_cast<hash_random_t>(random() & 0xffff));
+    }
+    assert(sizeof(hash_random_t) == 2); // So that the "& 0xffff" is valid
+
+    // Finally, initialize the mapping table for uppercase to lowercase
+    // characters.  A table is used as indexing a table is faster than calling
+    // the tolower() function.
+    casemap_.reserve(256);
+    for (int i = 0; i < 256; ++i) {
+        casemap_.push_back(i);
+    }
+    for (int i = 'A'; i <= 'Z'; ++i) {
+        casemap_[i] += ('a' - 'A');
+    }
+}
+
+
+uint32_t Hash::operator()(const char* key, uint32_t keylen, bool ignorecase) {
+
+    // Calculation as given in BIND-9.
+    hash_accum_t partial_sum = 0;
+    uint32_t i = 0;                 // Used after the end of the loop
+
+    // Perform the hashing.  If the key length if more than the maximum we set
+    // up this hash for, ignore the excess.
+    if (ignorecase) {
+        for (i = 0; i < min(keylen, maxkeylen_); ++i) {
+            partial_sum += mapLower(key[i]) * randvec_[i];
+        }
+    } else {
+        for (i = 0; i < min(keylen, maxkeylen_); ++i) {
+            partial_sum += key[i] * randvec_[i];
+        }
+    }
+    partial_sum += randvec_[i];
+
+    // Determine the hash value
+    uint32_t value = partial_sum % prime32_;
+
+    // ... and round it to fit the table size
+    return (value % tablesize_);
+}
+
+} // namespace nsas
+} // namespace isc

+ 124 - 0
src/lib/nsas/hash.h

@@ -0,0 +1,124 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __HASH_H
+#define __HASH_H
+
+#include <stdint.h>
+#include <vector>
+
+#include "exceptions/exceptions.h"
+
+using namespace std;
+
+namespace isc {
+namespace nsas {
+
+/// \brief Too Long Key Length
+///
+/// Thrown if the expected maximum key length is too long for the data types
+/// declared in the class.
+class KeyLengthTooLong : public isc::Exception {
+public:
+    KeyLengthTooLong(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+
+/// \brief Hash Calculation
+///
+/// Class abstracting the mechanics of the hash calculation.
+class Hash {
+public:
+
+    /// \brief Constructor
+    ///
+    /// Constructs the hash table and initialises all data structures needed
+    /// for the hashing.
+    ///
+    /// \param tablesize Size of the hash table.  For best performance, this
+    /// should be a prime number.
+    /// \param maxkeylen Maximum length (in bytes) of a key to be hashed.
+    /// calculation will return a value between 0 and N-1.  The default
+    /// value of 255 is the maximum size of a DNS name.
+    /// \param randomise If true (the default), the pseudo-random number generator
+    /// is seeded with the current time.  Otherwise it is initialised to a known
+    /// sequence.  This is principally for unit tests, where a random sequence
+    /// could lead to problems in checking results.
+    Hash(uint32_t tablesize, uint32_t maxkeylen = 255, bool randomise = true);
+
+    /// \brief Return Size
+    ///
+    /// \return The hash table size with which this object was initialized
+    virtual uint32_t tableSize() const {
+        return tablesize_;
+    }
+
+    /// \brief Return Key Length
+    ///
+    /// \return Maximum length of a key that this class can cope with.
+    virtual uint32_t maxKeyLength() const {
+        return maxkeylen_;
+    }
+
+    /// \brief Hash Value
+    ///
+    /// \param key String value or array of bytes for which a hash is to be
+    /// calculated.
+    /// \param ketlen Number of characters in the string
+    /// \param ignorecase true for case to be ignored when calculating the
+    /// hash value, false for it to be taken into account.
+    ///
+    /// \return Hash value, a number between 0 and N-1.
+    virtual uint32_t operator()(const char* key, uint32_t keylen, bool ignorecase = true);
+
+    /// \brief Map Lower Case to Upper Case
+    ///
+    /// Equivalent of tolower(), but using a table lookup instead of a
+    /// function call.  This should make the mapping faster.
+    ///
+    /// \param inchar Input character
+    ///
+    /// \return Mapped character
+    unsigned char mapLower(unsigned char inchar) {
+        return casemap_[inchar];
+    }
+
+private:
+
+    ///  \name Local Typedefs
+    ///
+    /// Typedefs for use in the code.  This makes it easier to compare the
+    /// hashing code with that in BIND-9.
+    //@{
+    typedef uint32_t hash_accum_t;  ///< Accumulator
+    typedef uint16_t hash_random_t; ///< Random number used in hash
+    //@}
+
+    uint32_t        tablesize_;     ///< Size of the hash table
+    uint32_t        maxkeylen_;     ///< Maximum key length
+    vector<unsigned char> casemap_; ///< Case mapping table
+    vector<hash_random_t> randvec_; ///< Vector of random numbers
+
+    static const uint32_t prime32_ = 0xfffffffb;    ///< 2^32 - 5
+                                    ///< Specifies range of hash output
+};
+
+} // namspace nsas
+} // namespace isc
+
+#endif // __HASH_H

+ 284 - 0
src/lib/nsas/hash_table.h

@@ -0,0 +1,284 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __HASH_TABLE_H
+#define __HASH_TABLE_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+#include <boost/interprocess/sync/sharable_lock.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
+#include <list>
+
+#include "config.h"
+
+#include "hash.h"
+
+// Maximum key length if the maximum size of a DNS name
+#define HASHTABLE_SIZE 1009
+#define MAX_KEY_LENGTH 255
+
+using namespace std;
+using namespace boost::interprocess;
+
+namespace isc {
+namespace nsas {
+
+/// \brief Hash Table Slot
+///
+/// Describes the entry for the hash table.  This is non-copyable (because
+/// the mutex is non-copyable), but we need to be able to copy it to initialize
+/// a vector of hash table slots.  As the copy is only needed for
+/// initialization, and as there is no need to copy state when this happens, we
+/// cheat: the copy constructor constructs a newly initialized HashTableSlot and
+/// does not copy its argument.
+template <typename T>
+struct HashTableSlot {
+    typedef boost::interprocess::interprocess_upgradable_mutex mutex_type;
+
+    mutex_type                          mutex_;     ///< Protection mutex
+    std::list<boost::shared_ptr<T> >    list_;      ///< List head
+
+    /// \brief Default Constructor
+    HashTableSlot()
+    {}
+
+    /// \brief Copy Constructor
+    ///
+    /// ... which as noted in the class description does not copy.  It is
+    /// defined outside the class to allow for use of the UNUSED_PARAM macro.
+    HashTableSlot(const HashTableSlot<T>& unused);
+
+    /// ... and a couple of external definitions
+    typedef typename std::list<boost::shared_ptr<T> >::iterator  iterator;
+};
+
+// (Non)Copy Constructor
+
+template <typename T>
+HashTableSlot<T>::HashTableSlot(const HashTableSlot<T>& unused UNUSED_PARAM) :
+    mutex_(), list_()
+{}
+
+
+/// \brief Comparison Object Class
+///
+/// The base class for a comparison object; this object is used to compare
+/// an object in the hash table with a key, and indicates whether the two
+/// match.  All objects used for comparison in hash tables should be derived
+/// from this class.
+template <typename T>
+class HashTableCompare {
+public:
+
+    /// \brief Comparison Function
+    ///
+    /// Compares an object against a name in the hash table and reports if the
+    /// object's name is the same.
+    ///
+    /// \param object Pointer to the object
+    /// \param key Pointer to the name of the object
+    /// \param keylen Length of the key
+    ///
+    /// \return bool true of the name of the object is equal to the name given.
+    virtual bool operator()(T* object, const char* key, uint32_t keylen) = 0;
+};
+
+
+/// \brief Hash Table
+///
+/// This class is an implementation of a hash table in which the zones and
+/// nameservers of the Nameserver Address Store are held.
+///
+/// A special class has been written (rather than use an existing hash table
+/// class) to improve concurrency.  Rather than lock the entire hash table when
+/// an object is added/removed/looked up, only the entry for a particular hash
+/// value is locked.  To do this, each entry in the hash table is a pair of
+/// mutex/STL List; the mutex protects that particular list.
+///
+/// \param T Class of object to be stored in the table.
+template <typename T>
+class HashTable {
+public:
+
+    /// \brief Constructor
+    ///
+    /// Initialises the hash table.
+    ///
+    /// \param CmpFn Compare function (or object) used to compare an object with
+    /// to get the name to be used as a key in the table.  The object should be
+    /// created via a "new" as ownership passes to the hash table.  The hash
+    /// table will take the responsibility of deleting it.
+    /// \param size Size of the hash table.  For best result, this should be a
+    /// prime although that is not checked.  The default value is the size used
+    /// in BIND-9 for its address database.
+    HashTable(HashTableCompare<T>* cmp, uint32_t size = 1009);
+
+    /// \brief Get Entry
+    ///
+    /// Returns a shared_ptr object pointing to the table entry
+    ///
+    /// \param key Name of the object.  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 char* key, uint32_t keylen);
+
+    /// \brief Remove Entry
+    ///
+    /// Remove the specified entry.  The shared pointer to the object is
+    /// destroyed, so if this is the last pointer, the object itself is also
+    /// destroyed.
+    ///
+    /// \param key Name of the object.  The hash of this is calculated and
+    /// used to index the table.
+    /// \param keylen Length of the key
+    ///
+    /// \return true if the object was deleted, false if it was not found.
+    virtual bool remove(const char* key, uint32_t keylen);
+
+    /// \brief Add Entry
+    ///
+    /// Adds the specified entry to the table.  If there is an entry already
+    /// there, it is either replaced or the addition fails, depending on the
+    /// setting of the "replace" parameter.
+    ///
+    /// \param object Pointer to the object to be added.  If the addition is
+    /// successful, this object will have a shared pointer pointing to it; it
+    /// should not be deleted by the caller.
+    /// \param key Key to use to calculate the hash.
+    /// \patam keylen Length of "key"
+    /// \param replace If true, when an object is added and an object with the
+    /// 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 char* key,
+        uint32_t keylen, bool replace = false);
+
+    /// \brief Returns Size of Hash Table
+    ///
+    /// \return Size of hash table
+    virtual uint32_t tableSize() const {
+        return table_.size();
+    }
+
+private:
+    Hash                        hash_;  ///< Hashing function
+    vector<HashTableSlot<T> >   table_; ///< The hash table itself
+    boost::shared_ptr<HashTableCompare<T> > compare_;  ///< Compare object
+};
+
+
+// Constructor
+template <typename T>
+HashTable<T>::HashTable(HashTableCompare<T>* compare, uint32_t size) :
+    hash_(size, MAX_KEY_LENGTH), table_(size), compare_(compare)
+{}
+
+// Lookup an object in the table
+template <typename T>
+boost::shared_ptr<T> HashTable<T>::get(const char* key, uint32_t keylen) {
+
+    // Calculate the hash value
+    uint32_t index = hash_(key, keylen);
+
+    // Take out a read lock on this hash slot.  The lock is released when this
+    // object goes out of scope.
+    sharable_lock<typename HashTableSlot<T>::mutex_type> lock(table_[index].mutex_);
+
+    // Locate the object.
+    typename HashTableSlot<T>::iterator i;
+    for (i = table_[index].list_.begin(); i != table_[index].list_.end(); ++i) {
+        if ((*compare_)(i->get(), key, keylen)) {
+
+            // Found it, so return the shared pointer object
+            return (*i);
+        }
+    }
+
+    // Did not find it, return an empty shared pointer object.
+    return boost::shared_ptr<T>();
+}
+
+// Remove an entry from the hash table
+template <typename T>
+bool HashTable<T>::remove(const char* key, uint32_t keylen) {
+
+    // Calculate the hash value
+    uint32_t index = hash_(key, keylen);
+
+    // Access to the elements of this hash slot are accessed under a mutex.
+    // The mutex will be released when this object goes out of scope and is
+    // destroyed.
+    scoped_lock<typename HashTableSlot<T>::mutex_type> lock(table_[index].mutex_);
+
+    // Now 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, keylen)) {
+
+            // Object found so delete it.
+            table_[index].list_.erase(i);
+            return true;;
+        }
+    }
+
+    // When we get here, we know that there is no element with the key in the
+    // list, so tell the caller.
+    return false;
+}
+
+// Add an entry to the hash table
+template <typename T>
+bool HashTable<T>::add(boost::shared_ptr<T>& object, const char* key,
+    uint32_t keylen, bool replace) {
+
+    // Calculate the hash value
+    uint32_t index = hash_(key, keylen);
+
+    // Access to the elements of this hash slot are accessed under a mutex.
+    scoped_lock<typename HashTableSlot<T>::mutex_type> lock(table_[index].mutex_);
+
+    // Now 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, keylen)) {
+
+            // Object found.  If we are not allowed to replace the element,
+            // return an error.  Otherwise erase it from the list and exit the
+            // loop.
+            if (replace) {
+                table_[index].list_.erase(i);
+                break;
+            }
+            else {
+                return false;
+            }
+        }
+    }
+
+    // When we get here, we know that there is no element with the key in the
+    // list - in which case, add the new object.
+    table_[index].list_.push_back(object);
+
+    return true;
+}
+
+}   // namespace nsas
+}   // namespace isc
+
+#endif // __HASH_TABLE_H

+ 256 - 0
src/lib/nsas/lru_list.h

@@ -0,0 +1,256 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __LRU_LIST_H
+#define __LRU_LIST_H
+
+#include <list>
+#include <string>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+
+#include "config.h"
+
+
+namespace isc {
+namespace nsas {
+
+/// \brief LRU List
+///
+/// Provides the LRU list for the zone and nameserver objects.  The list is
+/// created with a specific size.  Entries are added to the back of the list
+/// and removed from the front.  It is also possible to pull an element out
+/// of the middle of the list and add it to the end of the list, an action that
+/// should be done when the element is referenced.
+///
+/// It is not intended that the class be copied, and the derivation from
+/// boost::noncopyable enforces this.
+template <typename T>
+class LruList : boost::noncopyable {
+public:
+    typedef typename std::list<boost::shared_ptr<T> >::iterator iterator;
+
+    /// \brief Expired Operation
+    ///
+    /// When an object is removed from the LRU list because it has not been
+    /// accessed for some time, it is possible that the action should trigger
+    /// some other functions.  For this reason, it is possible to register
+    /// a list-wide functor object to execute in this casee.
+    ///
+    /// Note that the function does not execute as the result of a call to
+    /// remove() - that is an explicit call and it is assumed that the caller
+    /// will handle any additional operations needed.
+    class Expired {
+    public:
+
+        /// \brief Expired Object Handler
+        ///
+        /// Function object called when the object expires from the LRU list.
+        ///
+        /// \param expired Expired object.
+        virtual void operator()(boost::shared_ptr<T>& element) = 0;
+    };
+
+    /// \brief Element of an LRU List
+    ///
+    /// This defines an element of the LRU List.  All objects stored in one
+    /// of these lists MUST be derived from this element.
+    ///
+    /// The class provides the storage for a pointer into the LRU list,
+    /// used to quickly locate the element when it is being "touched".
+    ///
+    /// Although it would be possible to require classes stored in the list
+    /// to have particular methods (and so eliminate the inheritance), this
+    /// would require the implementor to know something about the list and to
+    /// provide the appropriate logic.
+    ///
+    /// Unfortunately, using a base class does not simplify the definition of
+    /// the LRU list list class (by allowing the list to be defined as a list
+    /// of base class objects), as the list is a list of shared pointers to
+    /// objects, not a list of pointers to object.  Arguments are shared
+    /// pointers, but a shared pointer to a base class is not a subclass of a
+    /// shared pointer to a derived class.  For this reason, the list type
+    /// is a template parameter.
+    struct Element {
+        typename LruList<T>::iterator  handle_;    ///< Handle into the LRU List
+        bool                           valid_;     ///< true if handle is valid
+
+        Element() : valid_(false)
+        {}
+    };
+
+    /// \brief Constructor
+    ///
+    /// \param max_size Maximum size of the list before elements are dropped.
+    /// \param expired Pointer to a function object that will get called as
+    /// elements are dropped.  This object will be stored using a shared_ptr,
+    /// so should be allocated with new().
+    LruList(uint32_t max_size = 1000, Expired* expired = NULL) :
+        max_size_(max_size), count_(0), expired_(expired)
+    {}
+
+    /// \brief Add Element
+    ///
+    /// Add a new element to the end of the list.
+    ///
+    /// \param element Reference to the element to add.
+    ///
+    /// \return Handle describing the element in the LRU list.
+    virtual void add(boost::shared_ptr<T>& element);
+
+    /// \brief Remove Element
+    ///
+    /// Removes an element from the list.  If the element is not present (i.e.
+    /// its internal list pointer is invalid), this is a no-op.
+    ///
+    /// \param element Reference to the element to remove.
+    virtual void remove(boost::shared_ptr<T>& element);
+
+    /// \brief Touch Element
+    ///
+    /// The name comes from the Unix "touch" command.  All this does is to
+    /// move the specified entry from the middle of the list to the end of
+    /// the list.
+    ///
+    /// \param element Reference to the element to touch.
+    virtual void touch(boost::shared_ptr<T>& element);
+
+    /// \brief Return Size of the List
+    ///
+    /// An independent count is kept of the list size, as list.size() may take
+    /// some time if the list is big.
+    ///
+    /// \return Number of elements in the list
+    virtual uint32_t size() {
+
+        // Don't bother to lock the mutex.  If an update is in progress, we
+        // receive either the value just before the update or just after it.
+        // Either way, this call could have come just before or just after
+        // that operation, so the value would have been just as uncertain.
+        return count_;
+    }
+
+    /// \brief Return Maximum Size
+    ///
+    /// \return Maximum size of the list
+    virtual uint32_t getMaxSize() {
+        return max_size_;
+    }
+
+    /// \brief Set Maximum Size
+    ///
+    /// \param new_size New maximum list size
+    virtual void setMaxSize(uint32_t max_size) {
+        max_size_ = max_size;
+    }
+
+private:
+    boost::mutex                        mutex_;     ///< List protection
+    std::list<boost::shared_ptr<T> >    lru_;       ///< The LRU list itself
+    uint32_t                            max_size_;  ///< Max size of the list
+    uint32_t                            count_;     ///< Count of elements
+    boost::shared_ptr<Expired>          expired_;   ///< Expired object
+};
+
+// Add entry to the list
+template <typename T>
+void LruList<T>::add(boost::shared_ptr<T>& element) {
+
+    // Protect list against concurrent access
+    boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
+
+    // Add the entry and set its pointer field to point into the list.
+    // insert() is used to get the pointer.
+    element.get()->handle_ = lru_.insert(lru_.end(), element);
+    element.get()->valid_ = true;
+
+    // ... and update the count while we have the mutex.
+    ++count_;
+
+    // If the count takes us above the maximum size of the list, remove elements
+    // from the front.  The current list size could be more than one above the
+    // maximum size of the list if the maximum size was changed after
+    // construction.
+    while (count_ > max_size_) {
+        if (!lru_.empty()) {
+
+            // Run the expiration handler (if there is one) on the
+            // to-be-expired object.
+            if (expired_) {
+                (*expired_)(*lru_.begin());
+            }
+
+            // ... and get rid of it from the list
+            lru_.pop_front();
+            --count_;
+        }
+        else {
+
+            // TODO: Log this condition (count_ > 0 when list empty) -
+            // it should not happen
+            count_ = 0;
+            break;
+        }
+    }
+}
+
+// Remove an element from the list
+template <typename T>
+void LruList<T>::remove(boost::shared_ptr<T>& element) {
+
+    // An element can only be removed it its internal pointer is valid.
+    // If it is, the pointer can be used to access the list because no matter
+    // what other elements are added or removed, the pointer remains valid.
+    //
+    // If the pointer is not valid, this is a no-op.
+    if (element.get()->valid_) {
+
+        // Is valid, so protect list against concurrent access
+        boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
+
+        element.get()->valid_ = false;      // Invalidate element
+        lru_.erase(element.get()->handle_); // Remove element from list
+        --count_;                           // One less element
+    }
+}
+
+// Touch an element - remove it from the list and add to the end
+template <typename T>
+void LruList<T>::touch(boost::shared_ptr<T>& element) {
+
+    // As before, if the pointer is not valid, this is a no-op.
+    if (element.get()->valid_) {
+
+        // Protect list against concurrent access
+        boost::interprocess::scoped_lock<boost::mutex> lock(mutex_);
+
+        // Move the element to the end of the list.
+        lru_.splice(lru_.end(), lru_, element.get()->handle_);
+
+        // Update the iterator in the element to point to it.  We can
+        // offset from end() as a list has a bidirectional iterator.
+        iterator i = lru_.end();
+        element.get()->handle_ = --i;
+    }
+}
+
+}   // namespace nsas
+}   // namespace isc
+
+#endif // __LRU_LIST_H

+ 161 - 0
src/lib/nsas/nameserver_entry.cc

@@ -0,0 +1,161 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <algorithm>
+#include <functional>
+#include <cassert>
+#include <iostream>
+
+#include <ctype.h>
+#include <strings.h>
+
+#include "config.h"
+
+#include "exceptions/exceptions.h"
+#include "name.h"
+#include "rrclass.h"
+#include "rrttl.h"
+
+#include "address_entry.h"
+#include "nameserver_entry.h"
+
+using namespace isc::nsas;
+
+namespace isc {
+namespace nsas {
+
+
+// Constructor, initialized with the list of addresses associated with this
+// nameserver.
+NameserverEntry::NameserverEntry(AbstractRRset* v4Set, AbstractRRset* v6Set,
+    time_t curtime) : expiration_(0)
+{
+    // TODO: Use pseudo-random RTT
+    uint32_t rtt = 0;       // 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()) {
+            address_.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();
+    }
+
+    // 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()) {
+            address_.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();
+    }
+
+    // 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(address_.begin(), address_.end(), back_inserter(addresses),
+        bind1st(AddressSelection(), family));
+}
+
+// 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 = address_.begin(); i != address_.end(); ++i) {
+        if (i->getAddress().equal(address)) {
+            i->setRTT(rtt);
+        }
+    }
+}
+
+// Sets the address to be unreachable
+void NameserverEntry::setAddressUnreachable(const IOAddress& address) {
+    setAddressRTT(address, AddressEntry::UNREACHABLE);
+}
+
+
+} // namespace dns
+} // namespace isc

+ 183 - 0
src/lib/nsas/nameserver_entry.h

@@ -0,0 +1,183 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __NAMESERVER_ENTRY_H
+#define __NAMESERVER_ENTRY_H
+
+#include <string>
+#include <vector>
+#include <boost/thread.hpp>
+
+#include "address_entry.h"
+#include "asiolink.h"
+#include "exceptions/exceptions.h"
+#include "rrset.h"
+
+using namespace std;
+using namespace isc::dns;
+
+namespace isc {
+namespace nsas {
+
+/// \brief Inconsistent Owner Names
+///
+/// Thrown if a NameserverEntry is constructed from both an A and AAAA RRset
+/// where the owner names do not match.
+class InconsistentOwnerNames : public Exception {
+public:
+    InconsistentOwnerNames(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+/// \brief Inconsistent Class
+///
+/// Thrown if a NameserverEntry is constructed from both an A and AAAA RRset
+/// where the classes do not match.
+class InconsistentClass : public Exception {
+public:
+    InconsistentClass(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+
+
+/// \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
+/// different TTLs) there is one expiration time for all address records.
+/// When that is reached, all records are declared expired and new fetches
+/// started for the information.
+
+
+class NameserverEntry {
+public:
+    /// List of addresses associated with this nameserver
+    typedef std::vector<AddressEntry>   AddressVector;
+    typedef AddressVector::iterator     AddressVectorIterator;
+
+    /// Constructor where no A records are supplied.
+    ///
+    /// \param name Name of the nameserver,
+    /// \param classCode class of the nameserver
+    NameserverEntry(const std::string& name, uint16_t classCode) :
+        name_(name), classCode_(classCode)
+    {}
+
+    /// 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(AbstractRRset* v4Set, AbstractRRset* v6Set, time_t curtime = 0);
+
+    /// \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 Update RTT
+    ///
+    /// Updates the RTT for a particular address
+    ///
+    /// \param address Address to update
+    /// \param RTT New RTT for the address
+    virtual void setAddressRTT(const IOAddress& address, uint32_t rtt);
+
+    /// \brief Set Address Unreachable
+    ///
+    /// Sets the specified address to be unreachable
+    ///
+    /// \param address Address to update
+    virtual void setAddressUnreachable(const IOAddress& address);
+
+    /// \return Owner Name of RRset
+    virtual string getName() const {
+        return name_;
+    }
+
+    /// \return Class of RRset
+    virtual short getClass() const {
+        return classCode_;
+    }
+
+    /// \return Expiration Time of Data
+    ///
+    /// 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 {
+        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:
+        result_type operator()(short family, const AddressEntry& entry) const {
+            bool result = ((family != 0) && (entry.getAddress().getFamily() != family));
+            return result;
+        }
+    };
+
+private:
+    boost::mutex    mutex_;             ///< Mutex protecting this object
+    string          name_;              ///< Canonical name of the nameserver
+    uint16_t        classCode_;         ///< Class of the nameserver
+    std::vector<AddressEntry> address_; ///< Set of V4/V6 addresses
+    time_t          expiration_;        ///< Summary expiration time
+    time_t          last_access_;       ///< Last access time to the structure
+};
+
+}   // namespace dns
+}   // namespace isc
+
+#endif // __NAMESERVER_ENTRY_H

+ 32 - 0
src/lib/nsas/nsas_types.h

@@ -0,0 +1,32 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#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.
+
+/// \brief Array of nameserver addresses
+typedef std::vector<ip::address>    NasAddress
+
+
+#endif // __NSAS_TYPES_H

+ 36 - 0
src/lib/nsas/tests/Makefile.am

@@ -0,0 +1,36 @@
+SUBDIRS = .
+
+AM_CPPFLAGS  = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/nsas -I$(top_builddir)/src/lib/nsas
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+AM_LDFLAGS =
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+AM_LDFLAGS = -lboost_thread
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += nsas_test_utilities.h 
+run_unittests_SOURCES += address_entry_unittest.cc
+run_unittests_SOURCES += hash_unittest.cc
+run_unittests_SOURCES += hash_table_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += nameserver_entry_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+run_unittests_LDADD = $(GTEST_LDADD)
+
+run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 115 - 0
src/lib/nsas/tests/address_entry_unittest.cc

@@ -0,0 +1,115 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <limits.h>
+#include <gtest/gtest.h>
+
+// Need to define the following macro to get UINT32_MAX
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.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");
+static std::string V6A_TEXT("2001:dead:beef::0");
+static std::string V6B_TEXT("1984:1985::1986:1987");
+
+using namespace std;
+using namespace isc::nsas;
+
+/// \brief Test Fixture Class
+///
+/// Constructs four IOAddress objects, two V4 addresses and two V6 addresses.
+/// They are initialised in the constructor owing to the restrictions of
+/// the IOAddress class.
+
+class AddressEntryTest : public ::testing::Test {
+protected:
+    AddressEntryTest() :
+        v4a_(V4A_TEXT), v4b_(V4B_TEXT),
+        v6a_(V6A_TEXT), v6b_(V6B_TEXT)
+    {}
+
+    IOAddress       v4a_;       ///< First V4 address
+    IOAddress       v4b_;       ///< Second V4 address
+    IOAddress       v6a_;       ///< First V6 address
+    IOAddress       v6b_;       ///< Second V6 address
+};
+
+/// Tests of the various constructors
+TEST_F(AddressEntryTest, Constructor) {
+
+    // Basic constructor with V4 address
+    AddressEntry alpha(v4a_);
+    EXPECT_EQ(V4A_TEXT, alpha.getAddress().toText());
+    EXPECT_EQ(0, alpha.getRTT());
+
+    // General constructor with V4 address
+    AddressEntry beta(v4b_, 42);
+    EXPECT_EQ(V4B_TEXT, beta.getAddress().toText());
+    EXPECT_EQ(42, beta.getRTT());
+
+    // Basic constructor with V6 address
+    AddressEntry gamma(v6a_);
+    EXPECT_EQ(V6A_TEXT, gamma.getAddress().toText());
+    EXPECT_EQ(0, gamma.getRTT());
+
+    // General constructor with V6 address
+    AddressEntry delta(v6b_, 75);
+    EXPECT_EQ(V6B_TEXT, delta.getAddress().toText());
+    EXPECT_EQ(75, delta.getRTT());
+}
+
+/// Setting and getting the round-trip time.  Also includes unreachable
+/// checks.
+TEST_F(AddressEntryTest, RTT) {
+
+    AddressEntry alpha(v4a_);
+    EXPECT_EQ(0, alpha.getRTT());
+
+    // Check set and get of RTT
+    long rtt = 1276;
+    alpha.setRTT(rtt);
+    EXPECT_EQ(rtt, alpha.getRTT());
+
+    // Check unreachability
+    alpha.setUnreachable();
+    EXPECT_TRUE(alpha.isUnreachable());
+
+    alpha.setRTT(27);
+    EXPECT_FALSE(alpha.isUnreachable());
+
+    // ... and check the implementation of unreachability
+    alpha.setUnreachable();
+    EXPECT_EQ(AddressEntry::UNREACHABLE, alpha.getRTT());
+}
+
+/// Checking the address type.
+TEST_F(AddressEntryTest, AddressType) {
+
+    AddressEntry alpha(v4a_);
+    EXPECT_TRUE(alpha.isV4());
+    EXPECT_FALSE(alpha.isV6());
+
+    AddressEntry beta(v6a_);
+    EXPECT_FALSE(beta.isV4());
+    EXPECT_TRUE(beta.isV6());
+}

+ 235 - 0
src/lib/nsas/tests/hash_table_unittest.cc

@@ -0,0 +1,235 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <gtest/gtest.h>
+#include <boost/shared_ptr.hpp>
+
+#include <string.h>
+#include <iostream>
+
+#include "hash_table.h"
+
+
+namespace isc {
+namespace nsas {
+
+
+/// \brief Dummy Class for Use in Testing
+///
+/// A class that has a name and a value.  The name is used as a key in the
+/// hash table, and the integer value used to check for the add/replace option.
+class DummyObject {
+public:
+
+    /// Constructor
+    ///
+    /// \param name Name of the object
+    /// \param value Integer value
+    DummyObject(const char* name, int value) : name_(name), value_(value)
+    {}
+
+    /// \return Name of the object
+    string getName() const {
+        return name_;
+    }
+
+    /// \return Value of the Object
+    int getValue() const {
+        return value_;
+    }
+
+private:
+    string  name_;      ///< Object name
+    int     value_;     ///< Object value
+};
+
+/// \brief Comparison Class
+///
+/// Class to encapsulate the comparison of a DummyObject with a key and
+/// key length.
+class DummyObjectCompare : public HashTableCompare<DummyObject> {
+public:
+
+    /// \brief Comparison Operator
+    ///
+    /// Used to compare the object with a key name/length.
+    ///
+    /// \param object Pointer to the object being tested
+    /// \param key Key string
+    /// \param keylen Length of the key string
+    bool operator()(DummyObject* object, const char* key, uint32_t keylen) {
+        // Do a quick check on size first
+        if (keylen != object->getName().size()) {
+            return false;
+        }
+        return (memcmp(object->getName().c_str(), key, keylen) == 0);
+    }
+};
+
+
+
+/// \brief Text Fixture Class
+class HashTableTest : public ::testing::Test {
+protected:
+
+    // Constructor - initialize the objects
+    HashTableTest() :
+        table_(new DummyObjectCompare()),
+        dummy1_(new DummyObject("test", 42)),
+        dummy2_(new DummyObject("test", 47)),
+        dummy3_(new DummyObject("Something_Else", 1332))
+    {}
+
+    // Members
+    HashTable<DummyObject> table_;
+    boost::shared_ptr<DummyObject> dummy1_;
+    boost::shared_ptr<DummyObject> dummy2_;
+    boost::shared_ptr<DummyObject> dummy3_;
+};
+
+
+// Test of the constructor
+TEST_F(HashTableTest, Constructor) {
+    
+    // Default constructor
+    HashTable<DummyObject> table1(new DummyObjectCompare());
+    EXPECT_EQ(1009, table1.tableSize());
+
+    // Non default constructor
+    HashTable<DummyObject> table2(new DummyObjectCompare(), 97);
+    EXPECT_EQ(97, table2.tableSize());
+}
+
+
+// Basic test of addition
+TEST_F(HashTableTest, AddTest) {
+
+    // Using two objects with the same key.
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(1, dummy2_.use_count());
+
+    // Add first one to the hash table_
+    bool result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(2, dummy1_.use_count());
+    EXPECT_EQ(1, dummy2_.use_count());
+
+    // Attempt to add the second object with the same name should fail.
+    result = table_.add(dummy2_, dummy2_.get()->getName().c_str(),
+        dummy2_.get()->getName().size());
+    EXPECT_FALSE(result);
+    EXPECT_EQ(2, dummy1_.use_count());
+    EXPECT_EQ(1, dummy2_.use_count());
+
+    // Replacing an entry should work though
+    result = table_.add(dummy2_, dummy2_.get()->getName().c_str(),
+        dummy2_.get()->getName().size(), true);
+    EXPECT_TRUE(result);
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(2, dummy2_.use_count());
+}
+
+// Test the remove functionality
+TEST_F(HashTableTest, RemoveTest) {
+
+    // Using two objects with different keys
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(1, dummy3_.use_count());
+
+    // Add first one to the hash table_
+    bool result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(2, dummy1_.use_count());
+    EXPECT_EQ(1, dummy3_.use_count());
+
+    // Now remove it.
+    result = table_.remove(dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(1, dummy3_.use_count());
+
+    // Attempt to remove it again.
+    result = table_.remove(dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_FALSE(result);
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(1, dummy3_.use_count());
+
+    // Add both entries to the table_, then remove one (checks that it will
+    // remove the correct one).
+    result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+    result = table_.add(dummy3_, dummy3_.get()->getName().c_str(),
+        dummy3_.get()->getName().size());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(2, dummy1_.use_count());
+    EXPECT_EQ(2, dummy3_.use_count());
+
+    result = table_.remove(dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(2, dummy3_.use_count());
+}            
+
+// Test the "get" functionality
+TEST_F(HashTableTest, GetTest) {
+
+    // Using two objects with different keys
+    EXPECT_EQ(1, dummy1_.use_count());
+    EXPECT_EQ(1, dummy3_.use_count());
+
+    // Add both to the hash table
+    bool result = table_.add(dummy1_, dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+    result = table_.add(dummy3_, dummy3_.get()->getName().c_str(),
+        dummy3_.get()->getName().size());
+    EXPECT_TRUE(result);
+
+    // Lookup the first
+    boost::shared_ptr<DummyObject> value =
+        table_.get(dummy1_.get()->getName().c_str(), 
+        dummy1_.get()->getName().size());
+    EXPECT_EQ(value.get(), dummy1_.get());
+
+    // ... and the second
+    value = table_.get(dummy3_.get()->getName().c_str(), 
+        dummy3_.get()->getName().size());
+    EXPECT_EQ(value.get(), dummy3_.get());
+
+    // Remove the first
+    result = table_.remove(dummy1_.get()->getName().c_str(),
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(result);
+
+    // ... and a lookup should return empty
+    value = table_.get(dummy1_.get()->getName().c_str(), 
+        dummy1_.get()->getName().size());
+    EXPECT_TRUE(value.get() == NULL);
+
+}
+
+
+
+
+
+} // namespace nsas
+} // namespace isc

+ 146 - 0
src/lib/nsas/tests/hash_unittest.cc

@@ -0,0 +1,146 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+
+#include "hash.h"
+
+using namespace std;
+
+namespace isc {
+namespace nsas {
+
+
+/// \brief Text Fixture Class
+class HashTest : public ::testing::Test {
+};
+
+
+// Test of the constructor
+TEST_F(HashTest, Constructor) {
+    
+    // Default constructor
+    Hash hash1(1009, 250);
+    EXPECT_EQ(1009, hash1.tableSize());
+    EXPECT_EQ(250, hash1.maxKeyLength());
+}
+
+// Test of the hash algorithm.  Without duplicating the code for the algorithm
+// here, testing is a bit awkward.  So the tests will check that a series of
+// names get hashed to different values.  (Choosing a 1009 element array should
+// give minimal overlap; we'll allow for a maximum of 2 collisions with 50
+// similar names.  If there are more, perhaps the algorithm is at fault.
+
+TEST_F(HashTest, Algorithm) {
+
+    const int size = 1009;      // Size of the hash table
+    Hash hash(size, 255, false);// Hashing algorithm object with seed
+                                // randomisation disabled
+    string base = "alphabeta";  // Base of the names to behashed
+    vector<uint32_t> values;    // Results stored here
+
+    // Generate hash values
+    for (int i = 0; i < 50; ++i) {
+        string name = base + boost::lexical_cast<string>(i);
+        uint32_t hashval = hash(name.c_str(), name.size());
+        EXPECT_LT(hashval, size);
+        values.push_back(hashval);
+    }
+    uint32_t cursize = values.size();
+
+    // Check to see if they are unique.  Do this by sorting the values into
+    // ascending order, removing duplicates, and checking the size again.
+    //
+    // Note that unique only shifts elements around - it does not remove non-
+    // unique values, so it does change the size of the array.  The call to
+    // erase removes the elements left at the end of the array.
+    sort(values.begin(), values.end());
+    values.erase(unique(values.begin(), values.end()), values.end());
+
+    uint32_t newsize = values.size();
+    EXPECT_GE(newsize + 2, cursize);
+}
+
+// Test the case mapping function.
+
+TEST_F(HashTest, CaseMapping) {
+
+    Hash hash(1009, 255);
+    
+    // Check all unsigned characters
+    for (int i = 0; i < 255; ++i) {
+        if ((i >= 'A') && (i <= 'Z')) {
+            EXPECT_EQ(static_cast<unsigned char>(i - 'A' + 'a'),
+                hash.mapLower((unsigned char)i));
+        }
+        else {
+            EXPECT_EQ(i, hash.mapLower(i));
+        }
+    }
+}
+
+// Test the mapping of mixed-case names
+
+TEST_F(HashTest, MixedCase) {
+
+    std::string test1 = "example1234.co.uk.";
+    std::string test2 = "EXAmple1234.co.uk.";
+
+    Hash hash(1009, 255);
+
+    // Case not ignored, hashes should be different
+    uint32_t value1 = hash(test1.c_str(), test1.size(), false);
+    uint32_t value2 = hash(test2.c_str(), test2.size(), false);
+    EXPECT_NE(value1, value2);
+
+    // Case ignored, hashes should be the same
+    uint32_t value3 = hash(test1.c_str(), test1.size(), true);
+    uint32_t value4 = hash(test2.c_str(), test2.size(), true);
+    EXPECT_EQ(value3, value4);
+
+    // Check the default setting.
+    uint32_t value5 = hash(test1.c_str(), test1.size());
+    uint32_t value6 = hash(test2.c_str(), test2.size());
+    EXPECT_EQ(value5, value6);
+
+    // ... and just for good measure
+    EXPECT_EQ(value1, value3);
+    EXPECT_EQ(value1, value5);
+}
+
+// Test that the code performs when the length of the key is excessive
+TEST_F(HashTest, Overlong) {
+
+    // String 1 is a suitable prefix, string 2 is the same prefix plus 4096
+    // repetions of the character 'x'.
+    std::string string1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
+    std::string string2 = string1 + string(4096, 'x');
+
+    Hash hash(1009, string1.size());
+
+    // Do two hashes
+    uint32_t value1 = hash(string1.c_str(), string1.size());
+    uint32_t value2 = hash(string2.c_str(), string2.size());
+    EXPECT_EQ(value1, value2);
+}
+
+} // namespace nsas
+} // namespace isc

+ 289 - 0
src/lib/nsas/tests/lru_list_unittest.cc

@@ -0,0 +1,289 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+
+#include "lru_list.h"
+
+using namespace std;
+
+namespace isc {
+namespace nsas {
+
+/// \brief Element Class
+///
+/// This is an object that will be stored in the list.
+struct DataStore : public LruList<DataStore>::Element {
+    int         value_;
+    std::string text_;
+
+    /// \brief Constructor
+    DataStore(uint32_t value = 0, string text = "") : value_(value), text_(text)
+    {}
+};
+
+/// \brief Expired Functor Class
+///
+/// Functor object is called when an object in the list becomes expired.
+/// To prove that it has run, this function does nothing more than negate the
+/// the value of the data store.
+class Expired : public LruList<DataStore>::Expired {
+public:
+    virtual void operator()(boost::shared_ptr<DataStore>& element) {
+        element.get()->value_ = -element.get()->value_;
+    }
+};
+
+
+/// \brief Text Fixture Class
+class LruListTest : public ::testing::Test {
+protected:
+    LruListTest() :
+        element1_(new DataStore(1, "alpha")),
+        element2_(new DataStore(2, "beta")),
+        element3_(new DataStore(3, "gamma")),
+        element4_(new DataStore(4, "delta")),
+        element5_(new DataStore(5, "epsilon")),
+        element6_(new DataStore(6, "zeta")),
+        element7_(new DataStore(7, "eta"))
+    {}
+
+
+    boost::shared_ptr<DataStore>    element1_;
+    boost::shared_ptr<DataStore>    element2_;
+    boost::shared_ptr<DataStore>    element3_;
+    boost::shared_ptr<DataStore>    element4_;
+    boost::shared_ptr<DataStore>    element5_;
+    boost::shared_ptr<DataStore>    element6_;
+    boost::shared_ptr<DataStore>    element7_;
+};
+
+
+// Test of the constructor
+TEST_F(LruListTest, Constructor) {
+    LruList<DataStore>  lru(100);
+    EXPECT_EQ(100, lru.getMaxSize());
+    EXPECT_EQ(0, lru.size());
+}
+
+// Test of Get/Set the maximum number of elements
+TEST_F(LruListTest, GetSet) {
+    LruList<DataStore>  lru(100);
+    EXPECT_EQ(100, lru.getMaxSize());
+
+    lru.setMaxSize(42);
+    EXPECT_EQ(42, lru.getMaxSize());
+}
+
+// Test that adding an element really does add an element
+TEST_F(LruListTest, Add) {
+    LruList<DataStore>  lru(100);
+    EXPECT_EQ(0, lru.size());
+
+    lru.add(element1_);
+    EXPECT_EQ(1, lru.size());
+
+    lru.add(element2_);
+    EXPECT_EQ(2, lru.size());
+}
+
+// Test that removing an element really does remove it.
+TEST_F(LruListTest, Remove) {
+    LruList<DataStore>  lru(100);
+    EXPECT_EQ(0, lru.size());
+
+    lru.add(element1_);
+    EXPECT_EQ(1, lru.size());
+
+    lru.add(element2_);
+    EXPECT_EQ(2, lru.size());
+
+    lru.remove(element1_);
+    EXPECT_EQ(1, lru.size());
+}
+
+// Check that adding a new element to a limited size list does delete the
+// oldest element from the list.
+TEST_F(LruListTest, SizeLimit) {
+    LruList<DataStore>  lru(3);
+    EXPECT_EQ(0, lru.size());
+
+    // Add first element and check that the shared pointer's reference count
+    // has increased.  There will be two references: one from the "element1_"
+    // member in the test fixture class, and one from the list.
+    EXPECT_EQ(1, element1_.use_count());
+    lru.add(element1_);
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(1, lru.size());
+
+    // Same for element 2.
+    EXPECT_EQ(1, element2_.use_count());
+    lru.add(element2_);
+    EXPECT_EQ(2, element2_.use_count());
+    EXPECT_EQ(2, lru.size());
+
+    // Same for element 3.
+    EXPECT_EQ(1, element3_.use_count());
+    lru.add(element3_);
+    EXPECT_EQ(2, element3_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // Adding element 4 should remove element 1 from the list.  This will
+    // delete the list's shared pointer to the element and will therefore
+    // drop the reference count back to one (from the "element1_" member in
+    // the text fixture class).
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(1, element4_.use_count());
+    lru.add(element4_);
+    EXPECT_EQ(1, element1_.use_count());
+    EXPECT_EQ(2, element4_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // Adding element 5 should remove element 2 from the list.
+    EXPECT_EQ(2, element2_.use_count());
+    EXPECT_EQ(1, element5_.use_count());
+    lru.add(element5_);
+    EXPECT_EQ(1, element2_.use_count());
+    EXPECT_EQ(2, element5_.use_count());
+    EXPECT_EQ(3, lru.size());
+}
+
+// Check that "touching" an entry adds it to the back of the list.
+TEST_F(LruListTest, Touch) {
+
+    // Create the list
+    LruList<DataStore>  lru(3);
+    EXPECT_EQ(0, lru.size());
+    lru.add(element1_);
+    lru.add(element2_);
+    lru.add(element3_);
+
+    // Check the reference counts of the elements and the list size
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(2, element2_.use_count());
+    EXPECT_EQ(2, element3_.use_count());
+    EXPECT_EQ(1, element4_.use_count());
+    EXPECT_EQ(1, element5_.use_count());
+    EXPECT_EQ(1, element6_.use_count());
+    EXPECT_EQ(1, element7_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // "Touch" the first element
+    lru.touch(element1_);
+
+    // Adding two more entries should not remove the touched element.
+    lru.add(element4_);
+    lru.add(element5_);
+
+    // Check the status of the elements and the list.
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(1, element2_.use_count());
+    EXPECT_EQ(1, element3_.use_count());
+    EXPECT_EQ(2, element4_.use_count());
+    EXPECT_EQ(2, element5_.use_count());
+    EXPECT_EQ(1, element6_.use_count());
+    EXPECT_EQ(1, element7_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // Now touch the element agin to move it to the back of the list.
+    // This checks that the iterator stored in the element as a result of the
+    // last touch operation is valid.
+    lru.touch(element1_);
+
+    // Check this by adding two more elements and checking reference counts
+    // to see what is stored.
+    lru.add(element6_);
+    lru.add(element7_);
+
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(1, element2_.use_count());
+    EXPECT_EQ(1, element3_.use_count());
+    EXPECT_EQ(1, element4_.use_count());
+    EXPECT_EQ(1, element5_.use_count());
+    EXPECT_EQ(2, element6_.use_count());
+    EXPECT_EQ(2, element7_.use_count());
+    EXPECT_EQ(3, lru.size());
+}
+
+// Expired functor tests: tests that the function object is called when an
+// object expires from the list.
+TEST_F(LruListTest, Expired) {
+
+    // Create an object with an expiration handler.
+    LruList<DataStore> lru(3, new Expired());
+
+    // Fill the list
+    lru.add(element1_);
+    lru.add(element2_);
+    lru.add(element3_);
+
+    EXPECT_EQ(1, element1_.get()->value_);
+    EXPECT_EQ(2, element2_.get()->value_);
+
+    // Add another element and check that the handler runs.
+    lru.add(element4_);
+    EXPECT_EQ(-1, element1_.get()->value_);
+
+    lru.add(element5_);
+    EXPECT_EQ(-2, element2_.get()->value_);
+
+    // Delete an element and check that the handler does not run. 
+    EXPECT_EQ(3, element3_.get()->value_);
+    lru.remove(element3_);
+    EXPECT_EQ(3, element3_.get()->value_);
+}
+
+// Miscellaneous tests - pathological conditions
+TEST_F(LruListTest, Miscellaneous) {
+
+    // Zero size list should not allow elements to be added
+    LruList<DataStore> lru_1(0);
+    lru_1.add(element1_);
+    EXPECT_EQ(0, lru_1.size());
+    EXPECT_EQ(1, element1_.use_count());
+
+    // Removing an uninserted element should not affect the list.
+    LruList<DataStore> lru_2(100);
+    lru_2.add(element1_);
+    lru_2.add(element2_);
+    lru_2.add(element3_);
+    EXPECT_EQ(3, lru_2.size());
+
+    lru_2.remove(element4_);
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(2, element2_.use_count());
+    EXPECT_EQ(2, element3_.use_count());
+    EXPECT_EQ(1, element4_.use_count());
+    EXPECT_EQ(1, element5_.use_count());
+    EXPECT_EQ(3, lru_2.size());
+
+    // Touching an uninserted element should not affect the list.
+    lru_2.touch(element5_);
+    EXPECT_EQ(2, element1_.use_count());
+    EXPECT_EQ(2, element2_.use_count());
+    EXPECT_EQ(2, element3_.use_count());
+    EXPECT_EQ(1, element4_.use_count());
+    EXPECT_EQ(1, element5_.use_count());
+    EXPECT_EQ(3, lru_2.size());
+}
+
+}   // namespace nsas
+}   // namespace isc

+ 455 - 0
src/lib/nsas/tests/nameserver_entry_unittest.cc

@@ -0,0 +1,455 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#include <iostream>
+#include <algorithm>
+
+#include <limits.h>
+#include <boost/foreach.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_entry.h"
+
+#include "nsas_test_utilities.h"
+
+
+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.");
+
+/// \brief Test Fixture Class
+class NameserverEntryTest : public ::testing::Test {
+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_;           ///< Non-NS RRset (MX in this case)
+    BasicRRset rrv6_;           ///< Standard RRset, IN, AAAA, lowercase name
+    BasicRRset rrnet_;          ///< example.net A RRset
+};
+
+/// \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());
+    EXPECT_EQ(EXAMPLE_CO_UK, alpha.getName());
+    EXPECT_EQ(RRClass::IN().getCode(), alpha.getClass());
+
+    // Also check that no addresses have been created.
+    NameserverEntry::AddressVector addresses;
+    alpha.getAddresses(addresses);
+    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
+    NameserverEntry alpha(&rrv4_, &rrv6_);
+    NameserverEntry::AddressVector vec;
+    alpha.getAddresses(vec);
+
+    // Copy into a vector of time_t.
+    vector<uint32_t> rtt;
+    for (NameserverEntry::AddressVectorIterator i = vec.begin();
+        i != vec.end(); ++i) {
+        rtt.push_back(i->getRTT());
+    }
+
+    // Ensure that the addresses are sorted and note how many RTTs we have.
+    sort(rtt.begin(), rtt.end());
+    int oldcount = rtt.size();
+
+    // Remove duplicates and notw the new size.
+    vector<uint32_t>::iterator newend = unique(rtt.begin(), rtt.end());
+    rtt.erase(newend, rtt.end());
+    int newcount = rtt.size();
+
+    // .. and we don't expect to have lost anything.
+    EXPECT_EQ(oldcount, newcount);
+}
+
+// Set an address RTT to a given value
+TEST_F(NameserverEntryTest, SetRTT) {
+
+    // Get the RTT for the different addresses
+    NameserverEntry alpha(&rrv4_, &rrv6_);
+    NameserverEntry::AddressVector vec;
+    alpha.getAddresses(vec);
+
+    EXPECT_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 new_rtt = first_rtt + 42;
+    alpha.setAddressRTT(first_address, new_rtt);
+
+    // Now see if it has changed
+    NameserverEntry::AddressVector 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);
+        }
+    }
+
+    // ... and make sure there was only one match in the set we retrieved
+    EXPECT_EQ(1, matchcount);
+}
+
+// Set an address RTT to be unreachable
+TEST_F(NameserverEntryTest, Unreachable) {
+
+    // Get the RTT for the different addresses
+    NameserverEntry alpha(&rrv4_, &rrv6_);
+    NameserverEntry::AddressVector vec;
+    alpha.getAddresses(vec);
+
+    EXPECT_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);
+
+    // Now see if it has changed
+    NameserverEntry::AddressVector 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());
+        }
+    }
+
+    // ... and make sure there was only one match in the set we retrieved
+    EXPECT_EQ(1, matchcount);
+}
+
+// Test that the expiration time of records is set correctly.
+//
+// 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.
+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());
+
+    // 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_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());
+}
+
+
+// Test that the name of this nameserver is set correctly.
+TEST_F(NameserverEntryTest, CheckName) {
+
+    // Default constructor
+    NameserverEntry alpha(EXAMPLE_CO_UK, RRClass::IN().getCode());
+    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);
+
+}
+
+
+}   // namespace nsas
+}   // namespace isc

+ 143 - 0
src/lib/nsas/tests/nsas_test_utilities.h

@@ -0,0 +1,143 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __NSAS_TEST_UTILITIES_H
+#define __NSAS_TEST_UTILITIES_H
+
+/// \file test_utilities.h
+///
+/// Contains miscellaneous classes 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"
+
+using namespace isc::dns::rdata;
+using namespace isc::dns;
+using namespace std;
+namespace isc {
+namespace dns {
+
+/// \brief Class Types
+///
+/// Very simple classes to provide a type for the RdataTest class below.
+/// All they do is return the type code associated with the record type.
+
+class A {
+public:
+    uint16_t getType() const
+    {return RRType::A().getCode();}
+};
+
+class AAAA {
+public:
+    uint16_t getType() const
+    {return RRType::AAAA().getCode();}
+};
+
+class NS {
+public:
+    uint16_t getType() const
+    {return RRType::NS().getCode();}
+};
+
+class MX {
+public:
+    uint16_t getType() const
+    {return RRType::MX().getCode();}
+};
+
+/// \brief Hold Rdata
+///
+/// A concrete implementation of the Rdata class, this holds data for the
+/// tests.  All RRs in the tests are either A, AAAA, NS or MX records, and
+/// as a result the text form of the Rdata is a single uninterpreted string.
+/// For this reason, a single class definition
+
+template <typename T>
+class RdataTest: public Rdata {
+public:
+
+    /// \brief Constructor
+    ///
+    /// Set the data in the object.
+    ///
+    /// \param v4address IPV4 address to store.  (The format of this address is
+    /// not checked.)
+    RdataTest(const string& data) : data_(data)
+    {}
+
+    /// \brief Convert Rdata to string
+    ///
+    /// This is the convenient interface to extract the information stored
+    /// in this object into a form that can be used by the tests.
+    virtual std::string toText() const {
+        return (data_);
+    }
+
+    /// \brief Return type of Rdata
+    ///
+    /// Returns the type of the data.  May be useful in the tests, although this
+    /// will not appear in the main code as this interface is not defined.
+    virtual uint16_t getType() const {
+        return (type_.getType());
+    }
+
+    /// \name Unused Methods
+    ///
+    /// These methods are not used in the tests.
+    ///
+    //@{
+    /// \brief Render the \c Rdata in the wire format to a buffer
+    virtual void toWire(OutputBuffer& buffer) const;
+
+    /// \brief render the \Rdata in the wire format to a \c MessageRenderer
+    virtual void toWire(MessageRenderer& renderer) const;
+    
+    /// \brief Comparison Method
+    virtual int compare(const Rdata& other) const;
+    //@}
+
+private:
+    string      data_;          ///< Rdata itself
+    T           type_;          ///< Identifies type of the Rdata
+};
+
+template <typename T>
+void RdataTest<T>::toWire(OutputBuffer& buffer UNUSED_PARAM) const {
+}
+
+template <typename T>
+void RdataTest<T>::toWire(MessageRenderer& buffer UNUSED_PARAM) const {
+}
+
+template <typename T>
+int RdataTest<T>::compare(const Rdata& other UNUSED_PARAM) const {
+    return 0;
+}
+
+
+} // namespace nsas
+} // namespace isc
+
+
+#endif // __NSAS_TEST_UTILITIES_H

+ 26 - 0
src/lib/nsas/tests/run_unittests.cc

@@ -0,0 +1,26 @@
+// Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id: run_unittests.cc 3020 2010-09-26 03:47:26Z jinmei $
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    return (RUN_ALL_TESTS());
+}

+ 66 - 0
src/lib/nsas/zone_entry.h

@@ -0,0 +1,66 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// $Id$
+
+#ifndef __ZONE_ENTRY_H
+#define __ZONE_ENTRY_H
+
+
+#include <string>
+#include <boost/thread.h>
+#include <boost/shared_ptr.h>
+
+using namespace std;
+
+class NameserverEntry;
+
+/// \brief Zone Entry
+///
+/// The zone entry object describes a zone for which nameserver address
+/// information is held.
+///
+/// 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.
+
+class ZoneEntry {
+public:
+
+    /// \brief Constructor
+    ///
+    /// Creates a zone entry object with an RRset representing the nameservers,
+    /// plus possibly additional RRsets holding address information.
+    ZoneEntry(AbstractRRset* nsrrset, vector<AbstractRRSet*> additional);
+
+    /// \brief Lookup Address
+    ///
+    /// Returns the address with the lowest RTT.
+    IOAddress getAddress() const;
+
+public:
+    void updateNS
+
+private:
+    boost::mutex    mutex_;     ///< Mutex protecting this zone entry
+    string          name_;      ///< Canonical zone name
+    short           class_;     ///< Class code
+    vector<boost::shared_ptr<NameserverEntry> > nameserver_; ///< Nameservers
+    time_t          expiry_;    ///< Expiry time of this entry
+};
+ 
+
+
+
+#endif // __ZONE_ENTRY_H