Browse Source

[trac749] commit directory src/lib/util

chenzhengzhang 14 years ago
parent
commit
ecfc86a4af
41 changed files with 6034 additions and 0 deletions
  1. 27 0
      src/lib/util/Makefile.am
  2. 31 0
      src/lib/util/README
  3. 1 0
      src/lib/util/compat/.deps/libutil_compat_la-base_n.Plo
  4. 1 0
      src/lib/util/compat/.deps/libutil_compat_la-sha1.Plo
  5. 108 0
      src/lib/util/compat/base16_from_binary.h
  6. 64 0
      src/lib/util/compat/base32hex.h
  7. 110 0
      src/lib/util/compat/base32hex_from_binary.h
  8. 79 0
      src/lib/util/compat/base64.h
  9. 398 0
      src/lib/util/compat/base_n.cc
  10. 120 0
      src/lib/util/compat/binary_from_base16.h
  11. 123 0
      src/lib/util/compat/binary_from_base32hex.h
  12. 65 0
      src/lib/util/compat/hex.h
  13. 484 0
      src/lib/util/compat/sha1.cc
  14. 84 0
      src/lib/util/compat/sha1.h
  15. 212 0
      src/lib/util/compat/time_utilities.cc
  16. 147 0
      src/lib/util/compat/time_utilities.h
  17. 137 0
      src/lib/util/filename.cc
  18. 161 0
      src/lib/util/filename.h
  19. 448 0
      src/lib/util/io/buffer.h
  20. 65 0
      src/lib/util/io/io_utilities.h
  21. 116 0
      src/lib/util/locks.h
  22. 260 0
      src/lib/util/lru_list.h
  23. 57 0
      src/lib/util/random/qid_gen.cc
  24. 87 0
      src/lib/util/random/qid_gen.h
  25. 208 0
      src/lib/util/random/random_number_generator.h
  26. 135 0
      src/lib/util/strutil.cc
  27. 145 0
      src/lib/util/strutil.h
  28. 41 0
      src/lib/util/tests/Makefile.am
  29. 159 0
      src/lib/util/tests/base32hex_unittest.cc
  30. 93 0
      src/lib/util/tests/base64_unittest.cc
  31. 193 0
      src/lib/util/tests/buffer_unittest.cc
  32. 179 0
      src/lib/util/tests/filename_unittest.cc
  33. 120 0
      src/lib/util/tests/hex_unittest.cc
  34. 73 0
      src/lib/util/tests/io_utilities_unittest.cc
  35. 427 0
      src/lib/util/tests/lru_list_unittest.cc
  36. 60 0
      src/lib/util/tests/qid_gen_unittest.cc
  37. 311 0
      src/lib/util/tests/random_number_generator_unittest.cc
  38. 21 0
      src/lib/util/tests/run_unittests.cc
  39. 107 0
      src/lib/util/tests/sha1_unittest.cc
  40. 214 0
      src/lib/util/tests/strutil_unittest.cc
  41. 163 0
      src/lib/util/tests/time_utilities_unittest.cc

+ 27 - 0
src/lib/util/Makefile.am

@@ -0,0 +1,27 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil.la
+libutil_la_SOURCES  = filename.h filename.cc
+libutil_la_SOURCES += locks.h lru_list.h
+libutil_la_SOURCES += strutil.h strutil.cc
+libutil_la_SOURCES += compat/base16_from_binary.h
+libutil_la_SOURCES += compat/base32hex.h compat/base64.h
+libutil_la_SOURCES += compat/base32hex_from_binary.h
+libutil_la_SOURCES += compat/base_n.cc compat/hex.h
+libutil_la_SOURCES += compat/binary_from_base32hex.h
+libutil_la_SOURCES += compat/binary_from_base16.h
+libutil_la_SOURCES += compat/sha1.h compat/sha1.cc
+libutil_la_SOURCES += compat/time_utilities.h
+libutil_la_SOURCES += compat/time_utilities.cc
+libutil_la_SOURCES += io/buffer.h io/io_utilities.h
+libutil_la_SOURCES += random/qid_gen.h random/qid_gen.cc
+libutil_la_SOURCES += random/random_number_generator.h
+
+libutil_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+CLEANFILES = *.gcno *.gcda

+ 31 - 0
src/lib/util/README

@@ -0,0 +1,31 @@
+This "util" directory is provided for utility header files and
+implementations.
+.
+
+The functionality provided in these tools is generally available in
+other external or perhaps system supplied libraries.  The basic
+development policy of BIND 10 is to avoid "reinventing wheels" unless
+they belong to the exact technology area that BIND 10 targets (e.g.,
+DNS).  However, libdns++ is a very core part of BIND 10, and is also
+intended to be used as a public library, so dependency from libdns++
+to external libraries should be minimized.  The utilities in this
+directory are provided balancing two policies and as a kind of
+compromise.
+
+The header files in this directory are therefore not intended to be
+installed.  Likewise, classes and public functions defined in this
+directory are not intended to be used outside libdns++, although we
+cannot prohibit it at the language level.
+
+They are not even expected to be used in other modules of BIND 10 than
+libdns++ based on the basic policy explained above.  Other modules
+should only rely on the DNS specific interface that may internally
+rely on these utility interfaces, or should use external libraries if
+the other module really needs to use the utility feature directly.
+There seem to be some violations as of this writing, but we should
+eventually fix it.  A notable example is the SHA1 interfaces.  They
+are defined here in the context of NSEC3 processing, but, in fact,
+they are not even used from any of the other libdns++ classes or
+functions.  The SHA1 related interfaces should be moved to the
+application that needs it or the application should only access it
+through DNS specific interfaces defined in libdns++.

+ 1 - 0
src/lib/util/compat/.deps/libutil_compat_la-base_n.Plo

@@ -0,0 +1 @@
+# dummy

+ 1 - 0
src/lib/util/compat/.deps/libutil_compat_la-sha1.Plo

@@ -0,0 +1 @@
+# dummy

+ 108 - 0
src/lib/util/compat/base16_from_binary.h

@@ -0,0 +1,108 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base16_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//  See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{ 
+    using ::size_t; 
+} // namespace std
+#endif
+
+// See base32hex_from_binary.h for why we need base64_from...hpp here.
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+namespace boost { 
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base16 characters
+
+namespace detail {
+
+template<class CharType>
+struct from_4_bit {
+    typedef CharType result_type;
+    CharType operator()(CharType t) const{
+        const char * lookup_table = 
+            "0123456789"
+            "ABCDEF";
+        assert(t < 16);
+        return (lookup_table[static_cast<size_t>(t)]);
+    }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+//  typedef transform_iterator<
+//      from_4_bit<CharType>,
+//      transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
+//  > base16_from_binary;
+// but C++ won't accept this.  Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor.  This makes it incompatible with the dataflow
+// ideal.  This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+    class Base, 
+    class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base16_from_binary : 
+    public transform_iterator<
+        detail::from_4_bit<CharType>,
+        Base
+    >
+{
+    friend class boost::iterator_core_access;
+    typedef transform_iterator<
+        BOOST_DEDUCED_TYPENAME detail::from_4_bit<CharType>,
+        Base
+    > super_t;
+
+public:
+    // make composible buy using templated constructor
+    template<class T>
+    base16_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+        super_t(
+            Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+            detail::from_4_bit<CharType>()
+        )
+    {}
+    // intel 7.1 doesn't like default copy constructor
+    base16_from_binary(const base16_from_binary & rhs) : 
+        super_t(
+            Base(rhs.base_reference()),
+            detail::from_4_bit<CharType>()
+        )
+    {}
+//    base16_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP

+ 64 - 0
src/lib/util/compat/base32hex.h

@@ -0,0 +1,64 @@
+// 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.
+
+#ifndef __BASE32HEX_H
+#define __BASE32HEX_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+
+/// \brief Encode binary data in the base32hex format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param binary A vector object storing the data to be encoded. 
+/// \return A newly created string that stores base32hex encoded value for
+/// binary.
+std::string encodeBase32Hex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base32hex format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param input A text encoded in the base32hex format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase32Hex(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace compat
+} // namespace util
+} // namespace isc
+
+#endif  // __BASE32HEX_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 110 - 0
src/lib/util/compat/base32hex_from_binary.h

@@ -0,0 +1,110 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base32hex_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//  See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{ 
+    using ::size_t; 
+} // namespace std
+#endif
+
+// We use the same boost header files used in "base64_from_".  Since the
+// precise path to these headers may vary depending on the boost version we
+// simply include the base64 header here.
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+namespace boost { 
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base32hex characters
+
+namespace detail {
+
+template<class CharType>
+struct from_5_bit {
+    typedef CharType result_type;
+    CharType operator()(CharType t) const{
+        const char * lookup_table = 
+            "0123456789"
+            "ABCDEFGHIJKLMNOPQRSTUV";
+        assert(t < 32);
+        return (lookup_table[static_cast<size_t>(t)]);
+    }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+//  typedef transform_iterator<
+//      from_5_bit<CharType>,
+//      transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+//  > base32hex_from_binary;
+// but C++ won't accept this.  Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor.  This makes it incompatible with the dataflow
+// ideal.  This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+    class Base, 
+    class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base32hex_from_binary : 
+    public transform_iterator<
+        detail::from_5_bit<CharType>,
+        Base
+    >
+{
+    friend class boost::iterator_core_access;
+    typedef transform_iterator<
+        BOOST_DEDUCED_TYPENAME detail::from_5_bit<CharType>,
+        Base
+    > super_t;
+
+public:
+    // make composible buy using templated constructor
+    template<class T>
+    base32hex_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+        super_t(
+            Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+            detail::from_5_bit<CharType>()
+        )
+    {}
+    // intel 7.1 doesn't like default copy constructor
+    base32hex_from_binary(const base32hex_from_binary & rhs) : 
+        super_t(
+            Base(rhs.base_reference()),
+            detail::from_5_bit<CharType>()
+        )
+    {}
+//    base32hex_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP

+ 79 - 0
src/lib/util/compat/base64.h

@@ -0,0 +1,79 @@
+// 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.
+
+#ifndef __BASE64_H
+#define __BASE64_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+
+/// \brief Encode binary data in the base64 format.
+///
+/// This function returns a new \c std::string object that stores a text
+/// encoded in the base64 format for the given \c binary %data.
+/// The resulting string will be a valid, canonical form of base64
+/// representation as specified in RFC4648.
+///
+/// If memory allocation for the returned string fails, a corresponding
+/// standard exception will be thrown.  This function never throws exceptions
+/// otherwise.
+///
+/// \param binary A vector object storing the data to be encoded. 
+/// \return A newly created string that stores base64 encoded value for binary.
+std::string encodeBase64(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base64 format into the original %data.
+///
+/// The \c input argument must be a valid string represented in the base64
+/// format as specified in RFC4648.  Space characters (spaces, tabs, newlines)
+/// can be included in \c input and will be ignored.  Without spaces, the
+/// length of string must be a multiple of 4 bytes with necessary paddings.
+/// Also it must be encoded using the canonical encoding (see RFC4648).
+/// If any of these conditions is not met, an exception of class
+/// \c isc::BadValue will be thrown.
+///
+/// If \c result doesn't have sufficient capacity to store all decoded %data
+/// and memory allocation fails, a corresponding standard exception will be
+/// thrown.  If the caller knows the necessary length (which can in theory
+/// be calculated from the input string), this situation can be avoided by
+/// reserving sufficient space for \c result beforehand.
+///
+/// Any existing %data in \c result will be removed.  This is the case in some
+/// of the cases where an exception is thrown; that is, this function only
+/// provides the basic exception guarantee.
+///
+/// \param input A text encoded in the base64 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase64(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace compat
+} // namespace util
+} // namespace isc
+
+#endif  // __BASE64_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 398 - 0
src/lib/util/compat/base_n.cc

@@ -0,0 +1,398 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <cassert>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/math/common_factor.hpp>
+
+#include <util/compat/base32hex_from_binary.h>
+#include <util/compat/binary_from_base32hex.h>
+#include <util/compat/base16_from_binary.h>
+#include <util/compat/binary_from_base16.h>
+#include <util/compat/base32hex.h>
+#include <util/compat/base64.h>
+
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace boost::archive::iterators;
+
+namespace isc {
+namespace util {
+namespace compat {
+
+// In the following anonymous namespace, we provide a generic framework
+// to encode/decode baseN format.  We use the following tools:
+// - boost base64_from_binary/binary_from_base64: provide mapping table for
+//   base64.
+//   These classes take another iterator (Base) as a template argument, and
+//   their dereference operator (operator*()) first retrieves an input value
+//   from Base via Base::operator* and converts the value using their mapping
+//   table.  The converted value is returned as their own operator*.
+// - base{32hex,16}_from_binary/binary_from_base{32hex,16}: provide mapping
+//   table for base32hex and base16.  A straightforward variation of their
+//   base64 counterparts.
+// - EncodeNormalizer/DecodeNormalizer: supplemental filter handling baseN
+//   padding characters (=)
+// - boost transform_width: an iterator framework for handling data stream
+//   per bit-group.  It takes another iterator (Base) and output/input bit
+//   numbers (BitsOut/BitsIn) template arguments.  A transform_width object
+//   internally maintains a bit stream, which can be retrieved per BitsOut
+//   bits via its dereference operator (operator*()).  It builds the stream
+//   by internally iterating over the Base object via Base::operator++ and
+//   Base::operator*, using the least BitsIn bits of the result of
+//   Base::operator*.  In our usage BitsIn for encoding and BitsOut for
+//   decoding are always 8 (# of bits for one byte).
+//
+// Its dereference operator
+//   retrieves BitsIn bits from the result of "*Base" (if necessary it
+//   internally calls ++Base) 
+//
+// A conceptual description of how the encoding and decoding work is as
+// follows:
+// Encoding:
+//   input binary data => Normalizer (append sufficient number of 0 bits)
+//                     => transform_width (extract bit groups from the original
+//                                         stream)
+//                     => baseXX_from_binary (convert each bit group to an
+//                                            encoded byte using the mapping)
+// Decoding:
+//   input baseXX text => Normalizer (convert '='s to the encoded characters
+//                                    corresponding to 0, e.g. 'A's in base64)
+//                     => binary_from_baseXX (convert each encoded byte into
+//                                            the original group bit)
+//                     => transform_width (build original byte stream by
+//                                         concatenating the decoded bit
+//                                         stream)
+//
+// Below, we define a set of templated classes to handle different parameters
+// for different encoding algorithms.
+namespace {
+// Common constants used for all baseN encoding.
+const char BASE_PADDING_CHAR = '=';
+const uint8_t BINARY_ZERO_CODE = 0;
+  
+// EncodeNormalizer is an input iterator intended to be used as a filter
+// between the binary stream and baseXX_from_binary translator (via
+// transform_width).  An EncodeNormalizer object is configured with two
+// iterators (base and base_end), specifying the head and end of the input
+// stream.  It internally iterators over the original stream, and return
+// each byte of the stream intact via its dereference operator until it
+// reaches the end of the stream.  After that the EncodeNormalizer object
+// will return 0 no matter how many times it is subsequently incremented.
+// This is necessary because the input binary stream may not contain
+// sufficient bits for a full encoded text while baseXX_from_binary expects
+// a sufficient length of input.
+// Note: this class is intended to be used within this implementation file,
+// and assumes "base < base_end" on construction without validating the
+// arguments.  The behavior is undefined if this assumption doesn't hold.
+class EncodeNormalizer : public iterator<input_iterator_tag, uint8_t> {
+public:
+    EncodeNormalizer(const vector<uint8_t>::const_iterator& base,
+                     const vector<uint8_t>::const_iterator& base_end) :
+        base_(base), base_end_(base_end), in_pad_(false)
+    {}
+    EncodeNormalizer& operator++() {
+        if (!in_pad_) {
+            ++base_;
+        }
+        if (base_ == base_end_) {
+            in_pad_ = true;
+        }
+        return (*this);
+    }
+    const uint8_t& operator*() const {
+        if (in_pad_) {
+            return (BINARY_ZERO_CODE);
+        } else {
+            return (*base_);
+        }
+    }
+    bool operator==(const EncodeNormalizer& other) const {
+        return (base_ == other.base_);
+    }
+private:
+    vector<uint8_t>::const_iterator base_;
+    const vector<uint8_t>::const_iterator base_end_;
+    bool in_pad_;
+};
+
+// DecodeNormalizer is an input iterator intended to be used as a filter
+// between the encoded baseX stream and binary_from_baseXX.
+// A DecodeNormalizer object is configured with three string iterators
+// (base, base_beinpad, and base_beginpad), specifying the head of the string,
+// the beginning position of baseX padding (when there's padding), and
+// end of the string, respectively.  It internally iterators over the original
+// stream, and return each character of the encoded string via its dereference
+// operator until it reaches base_beginpad.  After that the DecodeNormalizer
+// will return the encoding character corresponding to the all-0 value
+// (which is specified on construction via base_zero_code.  see also
+// BaseZeroCode below).  This translation is necessary because
+// binary_from_baseXX doesn't accept the padding character (i.e. '=').
+// Note: this class is intended to be used within this implementation file,
+// and for simplicity assumes "base < base_beginpad <= base_end" on
+// construction without validating the arguments.  The behavior is undefined
+// if this assumption doesn't hold.
+class DecodeNormalizer : public iterator<input_iterator_tag, char> {
+public:
+    DecodeNormalizer(const char base_zero_code,
+                     const string::const_iterator& base,
+                     const string::const_iterator& base_beginpad,
+                     const string::const_iterator& base_end) :
+        base_zero_code_(base_zero_code),
+        base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
+        in_pad_(false)
+    {}
+    DecodeNormalizer& operator++() {
+        ++base_;
+        while (base_ != base_end_ && isspace(*base_)) {
+            ++base_;
+        }
+        if (base_ == base_beginpad_) {
+            in_pad_ = true;
+        }
+        return (*this);
+    }
+    const char& operator*() const {
+        if (in_pad_ && *base_ == BASE_PADDING_CHAR) {
+            return (base_zero_code_);
+        } else {
+            return (*base_);
+        }
+    }
+    bool operator==(const DecodeNormalizer& other) const {
+        return (base_ == other.base_);
+    }
+private:
+    const char base_zero_code_;
+    string::const_iterator base_;
+    const string::const_iterator base_beginpad_;
+    const string::const_iterator base_end_;
+    bool in_pad_;
+};
+
+// BitsPerChunk: number of bits to be converted using the baseN mapping table.
+//               e.g. 6 for base64.
+// BaseZeroCode: the byte character that represents a value of 0 in
+//               the corresponding encoding.  e.g. 'A' for base64.
+// Encoder: baseX_from_binary<transform_width<EncodeNormalizer,
+//                                            BitsPerChunk, 8> >
+// Decoder: transform_width<binary_from_baseX<DecodeNormalizer>,
+//                          8, BitsPerChunk>
+template <int BitsPerChunk, char BaseZeroCode,
+          typename Encoder, typename Decoder>
+struct BaseNTransformer {
+    static string encode(const vector<uint8_t>& binary);
+    static void decode(const char* algorithm,
+                       const string& base64, vector<uint8_t>& result);
+
+    // BITS_PER_GROUP is the number of bits for the smallest possible (non
+    // empty) bit string that can be converted to a valid baseN encoded text
+    // without padding.  It's the least common multiple of 8 and BitsPerChunk,
+    // e.g. 24 for base64.
+    static const int BITS_PER_GROUP =
+        boost::math::static_lcm<BitsPerChunk, 8>::value;
+
+    // MAX_PADDING_CHARS is the maximum number of padding characters
+    // that can appear in a valid baseN encoded text.
+    // It's group_len - chars_for_byte, where group_len is the number of
+    // encoded characters to represent BITS_PER_GROUP bits, and
+    // chars_for_byte is the number of encoded character that is needed to
+    // represent a single byte, which is ceil(8 / BitsPerChunk).
+    // For example, for base64 we need two encoded characters to represent a
+    // byte, and each group consists of 4 encoded characters, so
+    // MAX_PADDING_CHARS is 4 - 2 = 2.
+    static const int MAX_PADDING_CHARS =
+        BITS_PER_GROUP / BitsPerChunk -
+        (8 / BitsPerChunk + ((8 % BitsPerChunk) == 0 ? 0 : 1));
+}; 
+
+template <int BitsPerChunk, char BaseZeroCode,
+          typename Encoder, typename Decoder>
+string
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::encode(
+    const vector<uint8_t>& binary)
+{
+    // calculate the resulting length.
+    size_t bits = binary.size() * 8;
+    if (bits % BITS_PER_GROUP > 0) {
+        bits += (BITS_PER_GROUP - (bits % BITS_PER_GROUP));
+    }
+    const size_t len = bits / BitsPerChunk;
+
+    string result;
+    result.reserve(len);
+    result.assign(Encoder(EncodeNormalizer(binary.begin(), binary.end())),
+                  Encoder(EncodeNormalizer(binary.end(), binary.end())));
+    assert(len >= result.length());
+    result.append(len - result.length(), BASE_PADDING_CHAR);
+    return (result);
+}
+
+template <int BitsPerChunk, char BaseZeroCode,
+          typename Encoder, typename Decoder>
+void
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
+    const char* const algorithm,
+    const string& input,
+    vector<uint8_t>& result)
+{
+    // enumerate the number of trailing padding characters (=), ignoring
+    // white spaces.  since baseN_from_binary doesn't accept padding,
+    // we handle it explicitly.
+    size_t padchars = 0;
+    string::const_reverse_iterator srit = input.rbegin();
+    string::const_reverse_iterator srit_end = input.rend();
+    while (srit != srit_end) {
+        char ch = *srit;
+        if (ch == BASE_PADDING_CHAR) {
+            if (++padchars > MAX_PADDING_CHARS) {
+                isc_throw(BadValue, "Too many " << algorithm
+                          << " padding characters: " << input);
+            }
+        } else if (!isspace(ch)) {
+            break;
+        }
+        ++srit;
+    }
+    // then calculate the number of padding bits corresponding to the padding
+    // characters.  In general, the padding bits consist of all-zero
+    // trailing bits of the last encoded character followed by zero bits
+    // represented by the padding characters:
+    // 1st pad  2nd pad  3rd pad...
+    // +++===== =======  ===...    (+: from encoded chars, =: from pad chars)
+    // 0000...0 0......0 000...
+    // 0      7 8     15 16.... (bits)
+    // The number of bits for the '==...' part is padchars * BitsPerChunk.
+    // So the total number of padding bits is the smallest multiple of 8 
+    // that is >= padchars * BitsPerChunk.
+    // (Below, note the common idiom of the bitwise AND with ~7.  It clears the
+    // lowest three bits, so has the effect of rounding the result down to the
+    // nearest multiple of 8)
+    const size_t padbits = (padchars * BitsPerChunk + 7) & ~7;
+
+    // In some encoding algorithm, it could happen that a padding byte would
+    // contain a full set of encoded bits, which is not allowed by definition
+    // of padding.  For example, if BitsPerChunk is 5, the following
+    // representation could happen:
+    // ++00000= (+: from encoded chars, 0: encoded char for '0', =: pad chars)
+    // 0      7 (bits)
+    // This must actually be encoded as follows:
+    // ++======
+    // 0      7 (bits)
+    // The following check rejects this type of invalid encoding.
+    if (padbits > BitsPerChunk * (padchars + 1)) {
+        isc_throw(BadValue, "Invalid " << algorithm << "padding: " << input);
+    }
+
+    // convert the number of bits in bytes for convenience.
+    const size_t padbytes = padbits / 8;
+
+    try {
+        result.assign(Decoder(DecodeNormalizer(BaseZeroCode, input.begin(),
+                                               srit.base(), input.end())),
+                      Decoder(DecodeNormalizer(BaseZeroCode, input.end(),
+                                               input.end(), input.end())));
+    } catch (const dataflow_exception& ex) {
+        // convert any boost exceptions into our local one.
+        isc_throw(BadValue, ex.what());
+    }
+
+    // Confirm the original BaseX text is the canonical encoding of the
+    // data, that is, that the first byte of padding is indeed 0.
+    // (DecodeNormalizer and binary_from_baseXX ensure that the rest of the
+    // padding is all zero).
+    assert(result.size() >= padbytes);
+    if (padbytes > 0 && *(result.end() - padbytes) != 0) {
+            isc_throw(BadValue, "Non 0 bits included in " << algorithm
+                      << " padding: " << input);
+    }
+
+    // strip the padded zero-bit fields
+    result.resize(result.size() - padbytes);
+}
+
+//
+// Instantiation for BASE-64
+//
+typedef
+base64_from_binary<transform_width<EncodeNormalizer, 6, 8> > base64_encoder;
+typedef
+transform_width<binary_from_base64<DecodeNormalizer>, 8, 6> base64_decoder;
+typedef BaseNTransformer<6, 'A', base64_encoder, base64_decoder>
+Base64Transformer;
+
+//
+// Instantiation for BASE-32HEX
+//
+typedef
+base32hex_from_binary<transform_width<EncodeNormalizer, 5, 8> >
+base32hex_encoder;
+typedef
+transform_width<binary_from_base32hex<DecodeNormalizer>, 8, 5>
+base32hex_decoder;
+typedef BaseNTransformer<5, '0', base32hex_encoder, base32hex_decoder>
+Base32HexTransformer;
+
+//
+// Instantiation for BASE-16 (HEX)
+//
+typedef
+base16_from_binary<transform_width<EncodeNormalizer, 4, 8> > base16_encoder;
+typedef
+transform_width<binary_from_base16<DecodeNormalizer>, 8, 4> base16_decoder;
+typedef BaseNTransformer<4, '0', base16_encoder, base16_decoder>
+Base16Transformer;
+}
+
+string
+encodeBase64(const vector<uint8_t>& binary) {
+    return (Base64Transformer::encode(binary));
+}
+
+void
+decodeBase64(const string& input, vector<uint8_t>& result) {
+    Base64Transformer::decode("base64", input, result);
+}
+
+string
+encodeBase32Hex(const vector<uint8_t>& binary) {
+    return (Base32HexTransformer::encode(binary));
+}
+
+void
+decodeBase32Hex(const string& input, vector<uint8_t>& result) {
+    Base32HexTransformer::decode("base32hex", input, result);
+}
+
+string
+encodeHex(const vector<uint8_t>& binary) {
+    return (Base16Transformer::encode(binary));
+}
+
+void
+decodeHex(const string& input, vector<uint8_t>& result) {
+    Base16Transformer::decode("base16", input, result);
+}
+
+} // namespace compat
+} // namespace util
+} // namespace isc

+ 120 - 0
src/lib/util/compat/binary_from_base16.h

@@ -0,0 +1,120 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base16.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//  See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+// See binary_from_base32hex.h for why we need _from_base64.hpp here.
+#include <boost/archive/iterators/binary_from_base64.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost { 
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base16 characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_4_bit {
+    typedef CharType result_type;
+    CharType operator()(CharType t) const{
+        const char lookup_table[] = {
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+             0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+            -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 40-4f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+            -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1  // 60-6f
+        };
+        // metrowerks trips this assertion - how come?
+        #if ! defined(__MWERKS__)
+        BOOST_STATIC_ASSERT(0x70 == sizeof(lookup_table));
+        #endif
+        signed char value = -1;
+        if((unsigned)t < sizeof(lookup_table))
+            value = lookup_table[(unsigned)t];
+        if(-1 == value) { 
+            isc_throw(isc::BadValue,
+                      "attempt to decode a value not in base16 char set");
+        }
+        return (value);
+    }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+//  typedef transform_iterator<
+//      from_4_bit<CharType>,
+//      transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
+//  > base16_from_binary;
+// but C++ won't accept this.  Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor.  This makes it incompatible with the dataflow
+// ideal.  This is also addressed here.
+
+template<
+    class Base, 
+    class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base16 : public
+    transform_iterator<
+        detail::to_4_bit<CharType>,
+        Base
+    >
+{
+    friend class boost::iterator_core_access;
+    typedef transform_iterator<
+        detail::to_4_bit<CharType>,
+        Base
+    > super_t;
+public:
+    // make composible buy using templated constructor
+    template<class T>
+    binary_from_base16(BOOST_PFTO_WRAPPER(T)  start) :
+        super_t(
+            Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))), 
+            detail::to_4_bit<CharType>()
+        )
+    {}
+    // intel 7.1 doesn't like default copy constructor
+    binary_from_base16(const binary_from_base16 & rhs) : 
+        super_t(
+            Base(rhs.base_reference()),
+            detail::to_4_bit<CharType>()
+        )
+    {}
+//    binary_from_base16(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 123 - 0
src/lib/util/compat/binary_from_base32hex.h

@@ -0,0 +1,123 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base32hex.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com . 
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+//  See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+// We use the same boost header files used in "_from_base64".  Since the
+// precise path to these headers may vary depending on the boost version we
+// simply include the base64 header here.
+#include <boost/archive/iterators/binary_from_base64.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost { 
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base32hex characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_5_bit {
+    typedef CharType result_type;
+    CharType operator()(CharType t) const{
+        const char lookup_table[] = {
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+            -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+             0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+            -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 40-4f
+            25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+            -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 60-6f
+            25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1  // 70-7f
+        };
+        // metrowerks trips this assertion - how come?
+        #if ! defined(__MWERKS__)
+        BOOST_STATIC_ASSERT(0x80 == sizeof(lookup_table));
+        #endif
+        signed char value = -1;
+        if((unsigned)t < sizeof(lookup_table))
+            value = lookup_table[(unsigned)t];
+        if(-1 == value) { 
+            isc_throw(isc::BadValue,
+                      "attempt to decode a value not in base32hex char set");
+        }
+        return (value);
+    }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+//  typedef transform_iterator<
+//      from_5_bit<CharType>,
+//      transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+//  > base32hex_from_binary;
+// but C++ won't accept this.  Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor.  This makes it incompatible with the dataflow
+// ideal.  This is also addressed here.
+
+template<
+    class Base, 
+    class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base32hex : public
+    transform_iterator<
+        detail::to_5_bit<CharType>,
+        Base
+    >
+{
+    friend class boost::iterator_core_access;
+    typedef transform_iterator<
+        detail::to_5_bit<CharType>,
+        Base
+    > super_t;
+public:
+    // make composible buy using templated constructor
+    template<class T>
+    binary_from_base32hex(BOOST_PFTO_WRAPPER(T)  start) :
+        super_t(
+            Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))), 
+            detail::to_5_bit<CharType>()
+        )
+    {}
+    // intel 7.1 doesn't like default copy constructor
+    binary_from_base32hex(const binary_from_base32hex & rhs) : 
+        super_t(
+            Base(rhs.base_reference()),
+            detail::to_5_bit<CharType>()
+        )
+    {}
+//    binary_from_base32hex(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 65 - 0
src/lib/util/compat/hex.h

@@ -0,0 +1,65 @@
+// 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.
+
+#ifndef __HEX_H
+#define __HEX_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+/// \brief Encode binary data in the base16 ('hex') format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and most of
+/// the description except the format (base16) equally applies.
+/// Another notable exception is that the base16 encoding doesn't require
+/// padding, so padding related considerations and the notion of canonical
+/// encoding don't apply.
+///
+/// \param binary A vector object storing the data to be encoded. 
+/// \return A newly created string that stores base16 encoded value for
+/// binary.
+std::string encodeHex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base16 ('hex') format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and most
+/// of the description except the format (base16) equally applies.
+/// Another notable exception is that the base16 encoding doesn't require
+/// padding, so padding related considerations and the notion of canonical
+/// encoding don't apply.
+///
+/// \param input A text encoded in the base16 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeHex(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace compat
+} // namespace util
+} // namespace isc
+
+#endif  // __HEX_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 484 - 0
src/lib/util/compat/sha1.cc

@@ -0,0 +1,484 @@
+/*
+ *  Description:
+ *      This file implements the Secure Hash Signature Standard
+ *      algorithms as defined in the National Institute of Standards
+ *      and Technology Federal Information Processing Standards
+ *      Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
+ *      published on August 1, 2002, and the FIPS PUB 180-2 Change
+ *      Notice published on February 28, 2004.
+ *
+ *      A combined document showing all algorithms is available at
+ *              http://csrc.nist.gov/publications/fips/
+ *              fips180-2/fips180-2withchangenotice.pdf
+ *
+ *      The SHA-1 algorithm produces a 160-bit message digest for a
+ *      given data stream.  It should take about 2**n steps to find a
+ *      message with the same digest as a given message and
+ *      2**(n/2) to find any two messages with the same digest,
+ *      when n is the digest size in bits.  Therefore, this
+ *      algorithm can serve as a means of providing a
+ *      "fingerprint" for a message.
+ *
+ *  Portability Issues:
+ *      SHA-1 is defined in terms of 32-bit "words".  This code
+ *      uses <stdint.h> (included via "sha.h") to define 32 and 8
+ *      bit unsigned integer types.  If your C compiler does not
+ *      support 32 bit unsigned integers, this code is not
+ *      appropriate.
+ *
+ *  Caveats:
+ *      SHA-1 is designed to work with messages less than 2^64 bits
+ *      long. This implementation uses SHA1Input() to hash the bits
+ *      that are a multiple of the size of an 8-bit character, and then
+ *      uses SHA1FinalBits() to hash the final few bits of the input.
+ *
+ *  Authorship:
+ *      This file is adapted from RFC 4634, by D. Eastlake et al.
+ *      Copyright (C) The Internet Society (2006).
+ *
+ *      Permission is granted for all uses, commercial and non-commercial,
+ *      of the sample code found in Section 8.  Royalty free license to
+ *      use, copy, modify and distribute the software found in Section 8 is
+ *      granted, provided that this document is identified in all material
+ *      mentioning or referencing this software, and provided that
+ *      redistributed derivative works do not contain misleading author or
+ *      version information.
+ *
+ *      The authors make no representations concerning either the
+ *      merchantability of this software or the suitability of this
+ *      software for any particular purpose.  It is provided "as is"
+ *      without express or implied warranty of any kind.
+ *      
+ */
+#include <util/compat/sha1.h>
+
+/* Local Function Prototyptes */
+static void SHA1Finalize(SHA1Context *, uint8_t Pad_Byte);
+static void SHA1PadMessage(SHA1Context *, uint8_t Pad_Byte);
+static void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ * Define functions used by SHA1 hash
+ */
+static inline uint32_t
+SHA_Ch(const uint32_t x, const uint32_t y, const uint32_t z) {
+    return (((x) & ((y) ^ (z))) ^ (z));
+}
+
+static inline uint32_t
+SHA_Maj(const uint32_t x, const uint32_t y, const uint32_t z) {
+    return (((x) & ((y) | (z))) | ((y) & (z)));
+}
+
+static inline uint32_t
+SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) {
+    return ((x) ^ (y) ^ (z));
+}
+
+static inline int 
+SHA1CircularShift(uint8_t bits, uint32_t word) {
+    return ((word << bits) | (word >> (32 - bits)));
+}
+
+static inline bool
+SHA1AddLength(SHA1Context *context, uint32_t length) {
+    uint32_t addTemp = context->Length_Low;
+    context->Length_Low += length;
+    if (context->Length_Low < addTemp && ++context->Length_High == 0) {
+        return (true);
+    } else {
+        return (false);
+    }
+}
+
+/*
+ *  SHA1Reset
+ *
+ *  Description:
+ *      This function will initialize the SHA1Context in preparation
+ *      for computing a new SHA1 message digest.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to reset.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int
+SHA1Reset(SHA1Context *context) {
+    if (!context) {
+        return (SHA_NULL);
+    }
+
+    context->Length_Low             = 0;
+    context->Length_High            = 0;
+    context->Message_Block_Index    = 0;
+
+    context->Intermediate_Hash[0]   = 0x67452301;
+    context->Intermediate_Hash[1]   = 0xEFCDAB89;
+    context->Intermediate_Hash[2]   = 0x98BADCFE;
+    context->Intermediate_Hash[3]   = 0x10325476;
+    context->Intermediate_Hash[4]   = 0xC3D2E1F0;
+
+    context->Computed   = 0;
+    context->Corrupted  = 0;
+    return (SHA_SUCCESS);
+}
+
+
+/*
+ *  SHA1Input
+ *
+ *  Description:
+ *      This function accepts an array of octets as the next portion
+ *      of the message.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The SHA context to update
+ *      message_array: [in]
+ *          An array of characters representing the next portion of
+ *          the message.
+ *      length: [in]
+ *          The length of the message in message_array
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int
+SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length) {
+    if (!length) {
+        return (SHA_SUCCESS);
+    }
+
+    if (!context || !message_array) {
+        return (SHA_NULL);
+    }
+
+    if (context->Computed) {
+        context->Corrupted = SHA_STATEERROR;
+        return (SHA_STATEERROR);
+    }
+
+    if (context->Corrupted) {
+         return (context->Corrupted);
+    }
+
+    while(length-- && !context->Corrupted) {
+        context->Message_Block[context->Message_Block_Index++] =
+                                                    (*message_array & 0xFF);
+
+        if (!SHA1AddLength(context, 8) &&
+            (context->Message_Block_Index == SHA1_BLOCKSIZE))
+        {
+            SHA1ProcessMessageBlock(context);
+        }
+
+        message_array++;
+    }
+
+    return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1FinalBits
+ *
+ * Description:
+ *   This function will add in any final bits of the message.
+ *
+ * Parameters:
+ *   context: [in/out]
+ *     The SHA context to update
+ *   message_bits: [in]
+ *     The final bits of the message, in the upper portion of the
+ *     byte. (Use 0b###00000 instead of 0b00000### to input the
+ *     three bits ###.)
+ *   length: [in]
+ *     The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ *   sha Error Code.
+ */
+int SHA1FinalBits(SHA1Context *context, const uint8_t message_bits,
+                  unsigned int length)
+{
+    uint8_t masks[8] = {
+        /* 0 0b00000000 */ 0x00,
+        /* 1 0b10000000 */ 0x80,
+        /* 2 0b11000000 */ 0xC0,
+        /* 3 0b11100000 */ 0xE0,
+        /* 4 0b11110000 */ 0xF0,
+        /* 5 0b11111000 */ 0xF8,
+        /* 6 0b11111100 */ 0xFC,
+        /* 7 0b11111110 */ 0xFE
+    };
+    uint8_t markbit[8] = {
+        /* 0 0b10000000 */ 0x80,
+        /* 1 0b01000000 */ 0x40,
+        /* 2 0b00100000 */ 0x20,
+        /* 3 0b00010000 */ 0x10,
+        /* 4 0b00001000 */ 0x08,
+        /* 5 0b00000100 */ 0x04,
+        /* 6 0b00000010 */ 0x02,
+        /* 7 0b00000001 */ 0x01
+    };
+
+    if (!length) {
+        return (SHA_SUCCESS);
+    }
+
+    if (!context) {
+        return (SHA_NULL);
+    }
+
+    if (context->Computed || (length >= 8) || (length == 0)) {
+        context->Corrupted = SHA_STATEERROR;
+        return (SHA_STATEERROR);
+    }
+
+    if (context->Corrupted) {
+        return (context->Corrupted);
+    }
+
+    SHA1AddLength(context, length);
+    SHA1Finalize(context,
+                 (uint8_t) ((message_bits & masks[length]) | markbit[length]));
+
+    return (SHA_SUCCESS);
+}
+
+/*
+ *  SHA1Result
+ *
+ *  Description:
+ *      This function will return the 160-bit message digest into the
+ *      Message_Digest array  provided by the caller.
+ *      NOTE: The first octet of hash is stored in the 0th element,
+ *            the last octet of hash in the 19th element.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to use to calculate the SHA-1 hash.
+ *      Message_Digest: [out]
+ *          Where the digest is returned.
+ *
+ *  Returns:
+ *      sha Error Code.
+ *
+ */
+int
+SHA1Result(SHA1Context *context, uint8_t Message_Digest[SHA1_HASHSIZE]) {
+    int i;
+
+    if (!context || !Message_Digest) {
+        return (SHA_NULL);
+    }
+
+    if (context->Corrupted) {
+        return (context->Corrupted);
+    }
+
+    if (!context->Computed) {
+        SHA1Finalize(context, 0x80);
+    }
+
+    for(i = 0; i < SHA1_HASHSIZE; ++i) {
+        Message_Digest[i] = context->Intermediate_Hash[i>>2]
+                            >> 8 * (3 - (i & 0x03));
+    }
+
+    return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1Finalize
+ *
+ * Description:
+ *   This helper function finishes off the digest calculations.
+ *
+ * Parameters:
+ *   context: [in/out]
+ *     The SHA context to update
+ *   Pad_Byte: [in]
+ *     The last byte to add to the digest before the 0-padding
+ *     and length. This will contain the last bits of the message
+ *     followed by another single bit. If the message was an
+ *     exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ *   sha Error Code.
+ *
+ */
+static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte) {
+    int i;
+    SHA1PadMessage(context, Pad_Byte);
+    /* message may be sensitive, clear it out */
+    for (i = 0; i < SHA1_BLOCKSIZE; ++i)
+        context->Message_Block[i] = 0;
+    context->Length_Low = 0;  /* and clear length */
+    context->Length_High = 0;
+    context->Computed = 1;
+}
+
+/*
+ *  SHA1PadMessage
+ *
+ *  Description:
+ *      According to the standard, the message must be padded to an even
+ *      512 bits.  The first padding bit must be a '1'.  The last 64
+ *      bits represent the length of the original message.  All bits in
+ *      between should be 0.  This function will pad the message
+ *      according to those rules by filling the Message_Block array
+ *      accordingly.  It will also call the ProcessMessageBlock function
+ *      provided appropriately.  When it returns, it can be assumed that
+ *      the message digest has been computed.
+ *
+ *  Parameters:
+ *      context: [in/out]
+ *          The context to pad
+ *      Pad_Byte: [in]
+ *          The last byte to add to the digest before the 0-padding
+ *          and length. This will contain the last bits of the message
+ *          followed by another single bit. If the message was an
+ *          exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ */
+static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte) {
+    /*
+     * Check to see if the current message block is too small to hold
+     * the initial padding bits and length. If so, we will pad the
+     * block, process it, and then continue padding into a second
+     * block.
+     */
+    if (context->Message_Block_Index >= (SHA1_BLOCKSIZE - 8)) {
+        context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+        while (context->Message_Block_Index < SHA1_BLOCKSIZE) {
+            context->Message_Block[context->Message_Block_Index++] = 0;
+        }
+
+        SHA1ProcessMessageBlock(context);
+    } else
+        context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+
+    while (context->Message_Block_Index < (SHA1_BLOCKSIZE - 8))
+        context->Message_Block[context->Message_Block_Index++] = 0;
+
+    /*
+     * Store the message length as the last 8 octets
+     */
+    context->Message_Block[56] = (uint8_t) (context->Length_High >> 24);
+    context->Message_Block[57] = (uint8_t) (context->Length_High >> 16);
+    context->Message_Block[58] = (uint8_t) (context->Length_High >> 8);
+    context->Message_Block[59] = (uint8_t) (context->Length_High);
+    context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24);
+    context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16);
+    context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8);
+    context->Message_Block[63] = (uint8_t) (context->Length_Low);
+
+    SHA1ProcessMessageBlock(context);
+}
+
+/*
+ *  SHA1ProcessMessageBlock
+ *
+ *  Description:
+ *      This helper function will process the next 512 bits of the
+ *      message stored in the Message_Block array.
+ *
+ *  Parameters:
+ *      None.
+ *
+ *  Returns:
+ *      Nothing.
+ *
+ *  Comments:
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the
+ *      names used in the publication.
+ *
+ *
+ */
+static void
+SHA1ProcessMessageBlock(SHA1Context *context) {
+    /* Constants defined in FIPS-180-2, section 4.2.1 */
+    const uint32_t K[] = {
+        0x5A827999,
+        0x6ED9EBA1,
+        0x8F1BBCDC,
+        0xCA62C1D6
+    };
+    int           t;                 /* Loop counter                */
+    uint32_t      temp;              /* Temporary word value        */
+    uint32_t      W[80];             /* Word sequence               */
+    uint32_t      A, B, C, D, E;     /* Word buffers                */
+
+    /*
+     * Initialize the first 16 words in the array W
+     */
+    for (t = 0; t < 16; t++) {
+        W[t]  = ((uint32_t)context->Message_Block[t * 4]) << 24;
+        W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
+        W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
+        W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
+    }
+
+    for (t = 16; t < 80; t++) {
+        W[t] = SHA1CircularShift(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+    }
+
+    A = context->Intermediate_Hash[0];
+    B = context->Intermediate_Hash[1];
+    C = context->Intermediate_Hash[2];
+    D = context->Intermediate_Hash[3];
+    E = context->Intermediate_Hash[4];
+
+    for (t = 0; t < 20; t++) {
+        temp = SHA1CircularShift(5,A) + SHA_Ch(B, C, D) + E + W[t] + K[0];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for (t = 20; t < 40; t++) {
+        temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[1];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for (t = 40; t < 60; t++) {
+        temp = SHA1CircularShift(5,A) + SHA_Maj(B, C, D) + E + W[t] + K[2];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    for (t = 60; t < 80; t++) {
+        temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[3];
+        E = D;
+        D = C;
+        C = SHA1CircularShift(30,B);
+        B = A;
+        A = temp;
+    }
+
+    context->Intermediate_Hash[0] += A;
+    context->Intermediate_Hash[1] += B;
+    context->Intermediate_Hash[2] += C;
+    context->Intermediate_Hash[3] += D;
+    context->Intermediate_Hash[4] += E;
+
+    context->Message_Block_Index = 0;
+}

+ 84 - 0
src/lib/util/compat/sha1.h

@@ -0,0 +1,84 @@
+/*
+ *  sha1.h
+ *
+ *  Description:
+ *      This is the header file for code which implements the Secure
+ *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ *      April 17, 1995.
+ *
+ *      Many of the variable names in this code, especially the
+ *      single character names, were used because those were the names
+ *      used in the publication.
+ *
+ *      Please read the file sha1.cc for more information.
+ *
+ *  Authorship:
+ *      This file is adapted from RFC 4634, by D. Eastlake et al.
+ *      Copyright (C) The Internet Society (2006).
+ *
+ *      Permission is granted for all uses, commercial and non-commercial,
+ *      of the sample code found in Section 8.  Royalty free license to
+ *      use, copy, modify and distribute the software found in Section 8 is
+ *      granted, provided that this document is identified in all material
+ *      mentioning or referencing this software, and provided that
+ *      redistributed derivative works do not contain misleading author or
+ *      version information.
+ *
+ *      The authors make no representations concerning either the
+ *      merchantability of this software or the suitability of this
+ *      software for any particular purpose.  It is provided "as is"
+ *      without express or implied warranty of any kind.
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include <stdint.h>
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ *    name              meaning
+ *  uint32_t         unsigned 32 bit integer
+ *  uint8_t          unsigned 8 bit integer (i.e., unsigned char)
+ *  int_least16_t    integer of >= 16 bits
+ *
+ */
+
+enum {
+    SHA_SUCCESS = 0,
+    SHA_NULL,            /* Null pointer parameter */
+    SHA_STATEERROR       /* called Input after Result */
+};
+
+enum {
+    SHA1_HASHSIZE = 20,
+    SHA1_HASHBITS = 20,
+    SHA1_BLOCKSIZE = 64
+};
+
+/*
+ *  This structure will hold context information for the SHA-1
+ *  hashing operation
+ */
+typedef struct SHA1Context
+{
+    uint32_t Intermediate_Hash[SHA1_HASHSIZE/4]; /* Message Digest */
+    uint32_t Length_Low;                /* Message length in bits */
+    uint32_t Length_High;               /* Message length in bits */
+    int_least16_t Message_Block_Index;  /* Index into message block array */
+    uint8_t Message_Block[64];          /* 512-bit message blocks */
+    int Computed;                       /* Is the digest computed? */
+    int Corrupted;                      /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ *  Function Prototypes
+ */
+extern int SHA1Reset(SHA1Context *);
+extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
+                     unsigned int bytecount);
+extern int SHA1FinalBits(SHA1Context *, const uint8_t bits,
+                         unsigned int bitcount);
+extern int SHA1Result(SHA1Context *, uint8_t Message_Digest[SHA1_HASHSIZE]);
+
+#endif

+ 212 - 0
src/lib/util/compat/time_utilities.cc

@@ -0,0 +1,212 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <sys/time.h>
+
+#include <string>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <stdio.h>
+#include <time.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/time_utilities.h>
+
+using namespace std;
+
+namespace {
+int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+inline bool
+isLeap(const int y) {
+    return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
+}
+
+unsigned int
+yearSecs(const int year) {
+    return ((isLeap(year) ? 366 : 365 ) * 86400);
+}
+
+unsigned int
+monthSecs(const int month, const int year) {
+    return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
+}
+}
+
+namespace isc {
+namespace util {
+namespace compat {
+
+string
+timeToText64(uint64_t value) {
+    struct tm tm;
+    unsigned int secs;
+
+    // We cannot rely on gmtime() because time_t may not be of 64 bit
+    // integer.  The following conversion logic is borrowed from BIND 9.
+    tm.tm_year = 70;
+    while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
+        value -= secs;
+        ++tm.tm_year;
+        if (tm.tm_year + 1900 > 9999) {
+            isc_throw(InvalidTime,
+                      "Time value out of range (year > 9999): " <<
+                      tm.tm_year + 1900);
+        }
+    }
+    tm.tm_mon = 0;
+    while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
+        value -= secs;
+        tm.tm_mon++;
+    }
+    tm.tm_mday = 1;
+    while (86400 <= value) {
+        value -= 86400;
+        ++tm.tm_mday;
+    }
+    tm.tm_hour = 0;
+    while (3600 <= value) {
+        value -= 3600;
+        ++tm.tm_hour;
+    }
+    tm.tm_min = 0;
+    while (60 <= value) {
+        value -= 60;
+        ++tm.tm_min;
+    }
+    tm.tm_sec = value;    // now t < 60, so this substitution is safe.
+
+    ostringstream oss;
+    oss << setfill('0')
+        << setw(4) << tm.tm_year + 1900
+        << setw(2) << tm.tm_mon + 1
+        << setw(2) << tm.tm_mday
+        << setw(2) << tm.tm_hour
+        << setw(2) << tm.tm_min
+        << setw(2) << tm.tm_sec;
+    return (oss.str());
+}
+
+// timeToText32() below uses the current system time.  To test it with
+// unusual current time values we introduce the following function pointer;
+// when it's non NULL, we call it to get the (normally faked) current time.
+// Otherwise we use the standard gettimeofday(2).  This hook is specifically
+// intended for testing purposes, so, even if it's visible outside of this
+// library, it's not even declared in a header file.
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+}
+
+namespace {
+int64_t
+gettimeofdayWrapper() {
+    using namespace detail;
+    if (gettimeFunction != NULL) {
+        return (gettimeFunction());
+    }
+
+    struct timeval now;
+    gettimeofday(&now, NULL);
+
+    return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
+string
+timeToText32(const uint32_t value) {
+    // We first adjust the time to the closest epoch based on the current time.
+    // Note that the following variables must be signed in order to handle
+    // time until year 2038 correctly.
+    const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
+    int64_t base = 0;
+    int64_t t;
+    while ((t = (base + value)) < start) {
+        base += 0x100000000LL;
+    }
+
+    // Then convert it to text.
+    return (timeToText64(t));
+}
+
+namespace {
+const size_t DATE_LEN = 14;      // YYYYMMDDHHmmSS
+
+inline void
+checkRange(const int min, const int max, const int value,
+           const string& valname)
+{
+    if ((value >= min) && (value <= max)) {
+        return;
+    }
+    isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
+}
+}
+
+uint64_t
+timeFromText64(const string& time_txt) {
+    // Confirm the source only consists digits.  sscanf() allows some
+    // minor exceptions.
+    for (int i = 0; i < time_txt.length(); ++i) {
+        if (!isdigit(time_txt.at(i))) {
+            isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
+                      << time_txt);
+        }
+    }
+
+    int year, month, day, hour, minute, second;
+    if (time_txt.length() != DATE_LEN ||
+        sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
+               &year, &month, &day, &hour, &minute, &second) != 6)
+    {
+        isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
+    }
+
+    checkRange(1970, 9999, year, "year");
+    checkRange(1, 12, month, "month");
+    checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
+            day, "day");
+    checkRange(0, 23, hour, "hour");
+    checkRange(0, 59, minute, "minute");
+    checkRange(0, 60, second, "second"); // 60 == leap second.
+
+    uint64_t timeval = second + (60 * minute) + (3600 * hour) +
+        ((day - 1) * 86400);
+    for (int m = 0; m < (month - 1); ++m) {
+            timeval += days[m] * 86400;
+    }
+    if (isLeap(year) && month > 2) {
+            timeval += 86400;
+    }
+    for (int y = 1970; y < year; ++y) {
+        timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
+    }
+
+    return (timeval);
+}
+
+uint32_t
+timeFromText32(const string& time_txt) {
+    // The implicit conversion from uint64_t to uint32_t should just work here,
+    // because we only need to drop higher 32 bits.
+    return (timeFromText64(time_txt));
+}
+
+}
+}
+}

+ 147 - 0
src/lib/util/compat/time_utilities.h

@@ -0,0 +1,147 @@
+// 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.
+
+#ifndef __TIME_UTILITIES_H
+#define __TIME_UTILITIES_H 1
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <exceptions/exceptions.h>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+
+///
+/// \brief A standard DNS (or ISC) module exception that is thrown if 
+/// a time conversion function encounters bad input
+///
+class InvalidTime : public Exception {
+public:
+    InvalidTime(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+///
+/// \name DNSSEC time conversion functions.
+///
+/// These functions convert between times represented in seconds (in integer)
+/// since epoch and those in the textual form used in the RRSIG records.
+/// For integers we provide both 32-bit and 64-bit versions.
+/// The RRSIG expiration and inception fields are both 32-bit unsigned
+/// integers, so 32-bit versions would be more useful for protocol operations.
+/// However, with 32-bit integers we need to take into account wrap-around
+/// points and compare values using the serial number arithmetic as specified
+/// in RFC4034, which would be more error prone.  We therefore provide 64-bit
+/// versions, too.
+///
+/// The timezone is always UTC for these functions.
+//@{
+/// Convert textual DNSSEC time to integer, 64-bit version.
+///
+/// The textual form must only consist of digits and be in the form of
+/// YYYYMMDDHHmmSS, where:
+/// - YYYY must be between 1970 and 9999
+/// - MM must be between 01 and 12
+/// - DD must be between 01 and 31 and must be a valid day for the month
+///   represented in 'MM'.  For example, if MM is 04, DD cannot be 31.
+///   DD can be 29 when MM is 02 only when YYYY is a leap year.
+/// - HH must be between 00 and 23
+/// - mm must be between 00 and 59
+/// - SS must be between 00 and 60
+///
+/// For all fields the range includes the begin and end values.  Note that
+/// 60 is allowed for 'SS', intending a leap second, although in real operation
+/// it's unlikely to be specified.
+///
+/// If the given text is valid, this function converts it to an unsigned
+/// 64-bit number of seconds since epoch (1 January 1970 00:00:00) and returns
+/// the converted value.  64 bits are sufficient to represent all possible
+/// values for the valid format uniquely, so there is no overflow.
+///
+/// \note RFC4034 also defines the textual form of an unsigned decimal integer
+/// for the corresponding time in seconds.  This function doesn't support
+/// this form, and if given it throws an exception of class \c InvalidTime.
+///
+/// \exception InvalidTime The given textual representation is invalid.
+///
+/// \param time_txt Textual time in the form of YYYYMMDDHHmmSS
+/// \return Seconds since epoch corresponding to \c time_txt
+uint64_t
+timeFromText64(const std::string& time_txt);
+
+/// Convert textual DNSSEC time to integer, 32-bit version.
+///
+/// This version is the same as \c timeFromText64() except that the return
+/// value is wrapped around to an unsigned 32-bit integer, simply dropping
+/// the upper 32 bits.
+uint32_t
+timeFromText32(const std::string& time_txt);
+
+/// Convert integral DNSSEC time to textual form, 64-bit version.
+///
+/// This function takes an integer that would be seconds since epoch and
+/// converts it in the form of YYYYMMDDHHmmSS.  For example, if \c value is
+/// 0, it returns "19700101000000".  If the value corresponds to a point
+/// of time on and after year 10,000, which cannot be represented in the
+/// YYYY... form, an exception of class \c InvalidTime will be thrown.
+///
+/// \exception InvalidTime The given time specifies on or after year 10,000.
+/// \exception Other A standard exception, if resource allocation for the
+/// returned text fails.
+///
+/// \param value Seconds since epoch to be converted.
+/// \return Textual representation of \c value in the form of YYYYMMDDHHmmSS.
+std::string
+timeToText64(uint64_t value);
+
+/// Convert integral DNSSEC time to textual form, 32-bit version.
+///
+/// This version is the same as \c timeToText64(), but the time value
+/// is expected to be the lower 32 bits of the full 64-bit value.
+/// These two will be different on and after a certain point of time
+/// in year 2106, so this function internally resolves the ambiguity
+/// using the current system time at the time of function call;
+/// it first identifies the range of [N*2^32 - 2^31, N*2^32 + 2^31)
+/// that contains the current time, and interprets \c value in the context
+/// of that range.  It then applies the same process as \c timeToText64().
+///
+/// There is one important exception in this processing, however.
+/// Until 19 Jan 2038 03:14:08 (2^31 seconds since epoch), this range
+/// would contain time before epoch.  In order to ensure the returned
+/// value is also a valid input to \c timeFromText, this function uses
+/// a special range [0, 2^32) until that time.  As a result, all upper
+/// half of the 32-bit values are treated as a future time.  For example,
+/// 2^32-1 (the highest value in 32-bit unsigned integers) will be converted
+/// to "21060207062815", instead of "19691231235959".
+std::string
+timeToText32(const uint32_t value);
+
+//@}
+}
+}
+}
+
+#endif  // __DNSSECTIME_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 137 - 0
src/lib/util/filename.cc

@@ -0,0 +1,137 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <ctype.h>
+
+#include <util/filename.h>
+
+using namespace std;
+
+
+namespace isc {
+namespace util {
+
+// Split string into components.  Any backslashes are assumed to have
+// been replaced by forward slashes.
+
+void
+Filename::split(const string& full_name, string& directory,
+    string& name, string& extension) const
+{
+    directory = name = extension = "";
+    bool dir_present = false;
+    if (!full_name.empty()) {
+
+        // Find the directory.
+        size_t last_slash = full_name.find_last_of('/');
+        if (last_slash != string::npos) {
+
+            // Found the last slash, so extract directory component and
+            // set where the scan for the last_dot should terminate.
+            directory = full_name.substr(0, last_slash + 1);
+            if (last_slash == full_name.size()) {
+
+                // The entire string was a directory, so exit not and don't
+                // do any more searching.
+                return;
+            }
+
+            // Found a directory so note the fact.
+            dir_present = true;
+        }
+
+        // Now search backwards for the last ".".
+        size_t last_dot = full_name.find_last_of('.');
+        if ((last_dot == string::npos) ||
+            (dir_present && (last_dot < last_slash))) {
+
+            // Last "." either not found or it occurs to the left of the last
+            // slash if a directory was present (so it is part of a directory
+            // name).  In this case, the remainder of the string after the slash
+            // is the name part.
+            name = full_name.substr(last_slash + 1);
+            return;
+        }
+
+        // Did find a valid dot, so it and everything to the right is the
+        // extension...
+        extension = full_name.substr(last_dot);
+
+        // ... and the name of the file is everything in between.
+        if ((last_dot - last_slash) > 1) {
+            name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
+        }
+    }
+
+}
+
+// Expand the stored filename with the default.
+
+string
+Filename::expandWithDefault(const string& defname) const {
+
+    string def_directory("");
+    string def_name("");
+    string def_extension("");
+
+    // Normalize the input string.
+    string copy_defname = isc::strutil::trim(defname);
+#ifdef WIN32
+    isc::strutil::normalizeSlash(copy_defname);
+#endif
+
+    // Split into the components
+    split(copy_defname, def_directory, def_name, def_extension);
+
+    // Now construct the result.
+    string retstring =
+        (directory_.empty() ? def_directory : directory_) +
+        (name_.empty() ? def_name : name_) +
+        (extension_.empty() ? def_extension : extension_);
+    return (retstring);
+}
+
+// Use the stored name as default for a given name
+
+string
+Filename::useAsDefault(const string& name) const {
+
+    string name_directory("");
+    string name_name("");
+    string name_extension("");
+
+    // Normalize the input string.
+    string copy_name = isc::strutil::trim(name);
+#ifdef WIN32
+    isc::strutil::normalizeSlash(copy_name);
+#endif
+
+    // Split into the components
+    split(copy_name, name_directory, name_name, name_extension);
+
+    // Now construct the result.
+    string retstring =
+        (name_directory.empty() ? directory_ : name_directory) +
+        (name_name.empty() ? name_ : name_name) +
+        (name_extension.empty() ? extension_ : name_extension);
+    return (retstring);
+}
+
+
+} // namespace log
+} // namespace isc

+ 161 - 0
src/lib/util/filename.h

@@ -0,0 +1,161 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __FILENAME_H
+#define __FILENAME_H
+
+#include <string>
+
+#include <util/strutil.h>
+
+namespace isc {
+namespace util {
+
+/// \brief Class to Manipulate Filenames
+///
+/// This is a utility class to manipulate filenames.  It repeats some of the
+/// features found in the Boost filename class, but is self-contained so avoids
+/// the need to link in the Boost library.
+///
+/// A Unix-style filename comprises three parts:
+///
+/// Directory - everything up to and including the last "/".  If there is no
+/// "/" in the string, there is no directory component.  Note that the
+/// requirement of a trailing slash eliminates the ambiguity of whether a
+/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
+/// name of a directory or is could be a file.  The interpretation here is that
+/// "beta" is the name of a file (although that file could be a directory).
+///
+/// Note: Under Windows, the drive letter is considered to be part of the
+/// directory specification.  Unless this class becomes more widely-used on
+/// Windows, there is no point in adding redundant code.
+///
+/// Name - everthing from the character after the last "/" up to but not
+/// including the last ".".
+///
+/// Extension - everthing from the right-most "." (after the right-most "/") to
+/// the end of the string.  If there is no "." after the last "/", there is
+/// no file extension.
+///
+/// (Note that on Windows, this function will replace all "\" characters
+/// with "/" characters on input strings.)
+///
+/// This class provides functions for extracting the components and for
+/// substituting components.
+
+
+class Filename {
+public:
+
+    /// \brief Constructor
+    Filename(const std::string& name) :
+        full_name_(""), directory_(""), name_(""), extension_("")
+    {
+        setName(name);
+    }
+
+    /// \brief Sets Stored Filename
+    ///
+    /// \param name New name to replaced currently stored name
+    void setName(const std::string& name) {
+        full_name_ = isc::strutil::trim(name);
+#ifdef WIN32
+        isc::strutil::normalizeSlash(full_name_);
+#endif
+        split(full_name_, directory_, name_, extension_);
+    }
+
+    /// \return Stored Filename
+    std::string fullName() const {
+        return (full_name_);
+    }
+
+    /// \return Directory of Given File Name
+    std::string directory() const {
+        return (directory_);
+    }
+
+    /// \return Name of Given File Name
+    std::string name() const {
+        return (name_);
+    }
+
+    /// \return Extension of Given File Name
+    std::string extension() const {
+        return (extension_);
+    }
+
+    /// \brief Expand Name with Default
+    ///
+    /// A default file specified is supplied and used to fill in any missing
+    /// fields.  For example, if the name stored is "/a/b" and the supplied
+    /// name is "c.d", the result is "/a/b.d": the only field missing from the
+    /// stored name is the extension, which is supplied by the default.
+    /// Another example would be to store "a.b" and to supply a default of
+    /// "/c/d/" - the result is "/c/d/a.b".  (Note that if the supplied default
+    /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
+    /// a directory.)
+    ///
+    /// \param defname Default name
+    ///
+    /// \return Name expanded with defname.
+    std::string expandWithDefault(const std::string& defname) const;
+
+    /// \brief Use as Default and Substitute into String
+    ///
+    /// Does essentially the inverse of expand(); that filled in the stored
+    /// name with a default and returned the result.  This treats the stored
+    /// name as the default and uses it to fill in a given name.  In essence,
+    /// the code:
+    /// \code
+    ///       Filename f("/a/b");
+    ///       result = f.expandWithdefault("c.d");
+    /// \endcode
+    /// gives as a result "/a/b.d".  This is the same as:
+    /// \code
+    ///       Filename f("c.d");
+    ///       result = f.useAsDefault("/a/b");
+    /// \endcode
+    ///
+    /// \param name Name to expand
+    ///
+    /// \return Name expanded with stored name
+    std::string useAsDefault(const std::string& name) const;
+
+private:
+    /// \brief Split Name into Components
+    ///
+    /// Splits the file name into the directory, name and extension parts.
+    /// The name is assumed to have had back slashes replaced by forward
+    /// slashes (if appropriate).
+    ///
+    /// \param full_name Name to split
+    /// \param directory Returned directory part
+    /// \param name Returned name part
+    /// \param extension Returned extension part
+    void split(const std::string& full_name, std::string& directory,
+       std::string& name, std::string& extension) const;
+
+    // Members
+
+    std::string full_name_;     ///< Given name
+    std::string directory_;     ///< Directory part
+    std::string name_;          ///< Name part
+    std::string extension_;     ///< Extension part
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // __FILENAME_H

+ 448 - 0
src/lib/util/io/buffer.h

@@ -0,0 +1,448 @@
+// 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.
+
+#ifndef __BUFFER_H
+#define __BUFFER_H 1
+
+#include <vector>
+
+#include <string.h>
+
+#include <stdint.h>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace util {
+namespace io {
+
+///
+/// \brief A standard DNS module exception that is thrown if an out-of-range
+/// buffer operation is being performed.
+///
+class InvalidBufferPosition : public Exception {
+public:
+    InvalidBufferPosition(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+///\brief The \c InputBuffer class is a buffer abstraction for manipulating
+/// read-only data.
+///
+/// The main purpose of this class is to provide a safe placeholder for
+/// examining wire-format data received from a network.
+///
+/// Applications normally use this class only in a limited situation: as an
+/// interface between legacy I/O operation (such as receiving data from a BSD
+/// socket) and the rest of the BIND10 DNS library.  One common usage of this
+/// class for an application would therefore be something like this:
+///
+/// \code unsigned char buf[1024];
+/// struct sockaddr addr;
+/// socklen_t addrlen = sizeof(addr);
+/// int cc = recvfrom(s, buf, sizeof(buf), 0, &addr, &addrlen);
+/// InputBuffer buffer(buf, cc);
+/// // pass the buffer to a DNS message object to parse the message \endcode
+///
+/// Other BIND10 DNS classes will then use methods of this class to get access
+/// to the data, but the application normally doesn't have to care about the
+/// details.
+///
+/// An \c InputBuffer object internally holds a reference to the given data,
+/// rather than make a local copy of the data.  Also, it does not have an
+/// ownership of the given data.  It is application's responsibility to ensure
+/// the data remains valid throughout the lifetime of the \c InputBuffer
+/// object.  Likewise, this object generally assumes the data isn't modified
+/// throughout its lifetime; if the application modifies the data while this
+/// object retains a reference to it, the result is undefined.  The application
+/// will also be responsible for releasing the data when it's not needed if it
+/// was dynamically acquired.
+///
+/// This is a deliberate design choice: although it's safer to make a local
+/// copy of the given data on construction, it would cause unacceptable
+/// performance overhead, especially considering that a DNS message can be
+/// as large as a few KB.  Alternatively, we could allow the object to allocate
+/// memory internally and expose it to the application to store network data
+/// in it.  This is also a bad design, however, in that we would effectively
+/// break the abstraction employed in the class, and do so by publishing
+/// "read-only" stuff as a writable memory region.  Since there doesn't seem to
+/// be a perfect solution, we have adopted what we thought a "least bad" one.
+///
+/// Methods for reading data from the buffer generally work like an input
+/// stream: it begins with the head of the data, and once some length of data
+/// is read from the buffer, the next read operation will take place from the
+/// head of the unread data.  An object of this class internally holds (a
+/// notion of) where the next read operation should start.  We call it the
+/// <em>read position</em> in this document.
+class InputBuffer {
+public:
+    ///
+    /// \name Constructors and Destructor
+    //@{
+    /// \brief Constructor from variable length of data.
+    ///
+    /// It is caller's responsibility to ensure that the data is valid as long
+    /// as the buffer exists.
+    /// \param data A pointer to the data stored in the buffer.
+    /// \param len The length of the data in bytes.
+    InputBuffer(const void* data, size_t len) :
+        position_(0), data_(static_cast<const uint8_t*>(data)), len_(len) {}
+    //@}
+
+    ///
+    /// \name Getter Methods
+    //@{
+    /// \brief Return the length of the data stored in the buffer.
+    size_t getLength() const { return (len_); }
+    /// \brief Return the current read position.
+    size_t getPosition() const { return (position_); }
+    //@}
+
+    ///
+    /// \name Setter Methods
+    ///
+    //@{
+    /// \brief Set the read position of the buffer to the given value.
+    ///
+    /// The new position must be in the valid range of the buffer; otherwise
+    /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+    /// \param position The new position (offset from the beginning of the
+    /// buffer).
+    void setPosition(size_t position)
+    {
+        if (position > len_)
+            isc_throw(InvalidBufferPosition, "position is too large");
+        position_ = position;
+    }
+    //@}
+
+    ///
+    /// \name Methods for reading data from the buffer.
+    //@{
+    /// \brief Read an unsigned 8-bit integer from the buffer and return it.
+    ///
+    /// If the remaining length of the buffer is smaller than 8-bit, an
+    /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+    uint8_t readUint8()
+    {
+        if (position_ + sizeof(uint8_t) > len_) {
+            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+        }
+
+        return (data_[position_++]);
+    }
+    /// \brief Read an unsigned 16-bit integer in network byte order from the
+    /// buffer, convert it to host byte order, and return it.
+    ///
+    /// If the remaining length of the buffer is smaller than 16-bit, an
+    /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+    uint16_t readUint16()
+    {
+        uint16_t data;
+        const uint8_t* cp;
+
+        if (position_ + sizeof(data) > len_) {
+            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+        }
+
+        cp = &data_[position_];
+        data = ((unsigned int)(cp[0])) << 8;
+        data |= ((unsigned int)(cp[1]));
+        position_ += sizeof(data);
+
+        return (data);
+    }
+    /// \brief Read an unsigned 32-bit integer in network byte order from the
+    /// buffer, convert it to host byte order, and return it.
+    ///
+    /// If the remaining length of the buffer is smaller than 32-bit, an
+    /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+    uint32_t readUint32()
+    {
+        uint32_t data;
+        const uint8_t* cp;
+
+        if (position_ + sizeof(data) > len_) {
+            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+        }
+
+        cp = &data_[position_];
+        data = ((unsigned int)(cp[0])) << 24;
+        data |= ((unsigned int)(cp[1])) << 16;
+        data |= ((unsigned int)(cp[2])) << 8;
+        data |= ((unsigned int)(cp[3]));
+        position_ += sizeof(data);
+
+        return (data);
+    }
+    /// \brief Read data of the specified length from the buffer and copy it to
+    /// the caller supplied buffer.
+    ///
+    /// The data is copied as stored in the buffer; no conversion is performed.
+    /// If the remaining length of the buffer is smaller than the specified
+    /// length, an exception of class \c isc::dns::InvalidBufferPosition will
+    /// be thrown.
+    void readData(void* data, size_t len)
+    {
+        if (position_ + len > len_) {
+            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+        }
+
+        memcpy(data, &data_[position_], len);
+        position_ += len;
+    }
+    //@}
+
+private:
+    size_t position_;
+
+    // XXX: The following must be private, but for a short term workaround with
+    // Boost.Python binding, we changed it to protected.  We should soon
+    // revisit it.
+protected:
+    const uint8_t* data_;
+    size_t len_;
+};
+
+///
+///\brief The \c OutputBuffer class is a buffer abstraction for manipulating
+/// mutable data.
+///
+/// The main purpose of this class is to provide a safe workplace for
+/// constructing wire-format data to be sent out to a network.  Here,
+/// <em>safe</em> means that it automatically allocates necessary memory and
+/// avoid buffer overrun.
+///
+/// Like for the \c InputBuffer class, applications normally use this class only
+/// in a limited situation.  One common usage of this class for an application
+/// would be something like this:
+///
+/// \code OutputBuffer buffer(4096); // give a sufficiently large initial size
+/// // pass the buffer to a DNS message object to construct a wire-format
+/// // DNS message.
+/// struct sockaddr to;
+/// sendto(s, buffer.getData(), buffer.getLength(), 0, &to, sizeof(to));
+/// \endcode
+///
+/// where the \c getData() method gives a reference to the internal memory
+/// region stored in the \c buffer object.  This is a suboptimal design in that
+/// it exposes an encapsulated "handle" of an object to its user.
+/// Unfortunately, there is no easy way to avoid this without involving
+/// expensive data copy if we want to use this object with a legacy API such as
+/// a BSD socket interface.  And, indeed, this is one major purpose for this
+/// object.  Applications should use this method only under such a special
+/// circumstance.  It should also be noted that the memory region returned by
+/// \c getData() may be invalidated after a subsequent write operation.
+///
+/// An \c OutputBuffer class object automatically extends its memory region when
+/// data is written beyond the end of the current buffer.  However, it will
+/// involve performance overhead such as reallocating more memory and copying
+/// data.  It is therefore recommended to construct the buffer object with a
+/// sufficiently large initial size.
+/// The \c getCapacity() method provides the current maximum size of data
+/// (including the portion already written) that can be written into the buffer
+/// without causing memory reallocation.
+///
+/// Methods for writing data into the buffer generally work like an output
+/// stream: it begins with the head of the buffer, and once some length of data
+/// is written into the buffer, the next write operation will take place from
+/// the end of the buffer.  Other methods to emulate "random access" are also
+/// provided (e.g., \c writeUint16At()).  The normal write operations are
+/// normally exception-free as this class automatically extends the buffer
+/// when necessary.  However, in extreme cases such as an attempt of writing
+/// multi-GB data, a separate exception (e.g., \c std::bad_alloc) may be thrown
+/// by the system.  This also applies to the constructor with a very large
+/// initial size.
+///
+/// Note to developers: it may make more sense to introduce an abstract base
+/// class for the \c OutputBuffer and define the simple implementation as a
+/// a concrete derived class.  That way we can provide flexibility for future
+/// extension such as more efficient buffer implementation or allowing users
+/// to have their own customized version without modifying the source code.
+/// We in fact considered that option, but at the moment chose the simpler
+/// approach with a single concrete class because it may make the
+/// implementation unnecessarily complicated while we were still not certain
+/// if we really want that flexibility.  We may revisit the class design as
+/// we see more applications of the class.  The same considerations apply to
+/// the \c InputBuffer and \c MessageRenderer classes.
+class OutputBuffer {
+public:
+    ///
+    /// \name Constructors and Destructor
+    ///
+    //@{
+    /// \brief Constructor from the initial size of the buffer.
+    ///
+    /// \param len The initial length of the buffer in bytes.
+    OutputBuffer(size_t len) { data_.reserve(len); }
+    //@}
+
+    ///
+    /// \name Getter Methods
+    ///
+    //@{
+    /// \brief Return the current capacity of the buffer.
+    size_t getCapacity() const { return (data_.capacity()); }
+    /// \brief Return a pointer to the head of the data stored in the buffer.
+    ///
+    /// The caller can assume that the subsequent \c getLength() bytes are
+    /// identical to the stored data of the buffer.
+    ///
+    /// Note: The pointer returned by this method may be invalidated after a
+    /// subsequent write operation.
+    const void* getData() const { return (&data_[0]); }
+    /// \brief Return the length of data written in the buffer.
+    size_t getLength() const { return (data_.size()); }
+    /// \brief Return the value of the buffer at the specified position.
+    ///
+    /// \c pos must specify the valid position of the buffer; otherwise an
+    /// exception class of \c InvalidBufferPosition will be thrown.
+    ///
+    /// \param pos The position in the buffer to be returned.
+    const uint8_t& operator[](size_t pos) const
+    {
+        if (pos >= data_.size()) {
+            isc_throw(InvalidBufferPosition, "read at invalid position");
+        }
+        return (data_[pos]);
+    }
+    //@}
+
+    ///
+    /// \name Methods for writing data into the buffer.
+    ///
+    //@{
+    /// \brief Insert a specified length of gap at the end of the buffer.
+    ///
+    /// The caller should not assume any particular value to be inserted.
+    /// This method is provided as a shortcut to make a hole in the buffer
+    /// that is to be filled in later, e.g, by \ref writeUint16At().
+    /// \param len The length of the gap to be inserted in bytes.
+    void skip(size_t len) { data_.insert(data_.end(), len, 0); }
+
+    /// \brief Trim the specified length of data from the end of the buffer.
+    ///
+    /// The specified length must not exceed the current data size of the
+    /// buffer; otherwise an exception of class \c isc::OutOfRange will
+    /// be thrown.
+    ///
+    /// \param len The length of data that should be trimmed.
+    void trim(size_t len)
+    {
+        if (len > data_.size()) {
+            isc_throw(OutOfRange, "trimming too large from output buffer");
+        }
+        data_.resize(data_.size() - len);
+    }
+    /// \brief Clear buffer content.
+    ///
+    /// This method can be used to re-initialize and reuse the buffer without
+    /// constructing a new one.
+    void clear() { data_.clear(); }
+    /// \brief Write an unsigned 8-bit integer into the buffer.
+    ///
+    /// \param data The 8-bit integer to be written into the buffer.
+    void writeUint8(uint8_t data) { data_.push_back(data); }
+
+    /// \brief Write an unsigned 8-bit integer into the buffer.
+    ///
+    /// The position must be lower than the size of the buffer,
+    /// otherwise an exception of class \c isc::dns::InvalidBufferPosition
+    /// will be thrown.
+    ///
+    /// \param data The 8-bit integer to be written into the buffer.
+    /// \param pos The position in the buffer to write the data.
+    void writeUint8At(uint8_t data, size_t pos) {
+        if (pos + sizeof(data) > data_.size()) {
+            isc_throw(InvalidBufferPosition, "write at invalid position");
+        }
+        data_[pos] = data;
+    }
+
+    /// \brief Write an unsigned 16-bit integer in host byte order into the
+    /// buffer in network byte order.
+    ///
+    /// \param data The 16-bit integer to be written into the buffer.
+    void writeUint16(uint16_t data)
+    {
+        data_.push_back(static_cast<uint8_t>((data & 0xff00U) >> 8));
+        data_.push_back(static_cast<uint8_t>(data & 0x00ffU));
+    }
+    /// \brief Write an unsigned 16-bit integer in host byte order at the
+    /// specified position of the buffer in network byte order.
+    ///
+    /// The buffer must have a sufficient room to store the given data at the
+    /// given position, that is, <code>pos + 2 < getLength()</code>;
+    /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will
+    /// be thrown.
+    /// Note also that this method never extends the buffer.
+    ///
+    /// \param data The 16-bit integer to be written into the buffer.
+    /// \param pos The beginning position in the buffer to write the data.
+    void writeUint16At(uint16_t data, size_t pos)
+    {
+        if (pos + sizeof(data) > data_.size()) {
+            isc_throw(InvalidBufferPosition, "write at invalid position");
+        }
+
+        data_[pos] = static_cast<uint8_t>((data & 0xff00U) >> 8);
+        data_[pos + 1] = static_cast<uint8_t>(data & 0x00ffU);
+    }
+    /// \brief Write an unsigned 32-bit integer in host byte order
+    /// into the buffer in network byte order.
+    ///
+    /// \param data The 32-bit integer to be written into the buffer.
+    void writeUint32(uint32_t data)
+    {
+        data_.push_back(static_cast<uint8_t>((data & 0xff000000) >> 24));
+        data_.push_back(static_cast<uint8_t>((data & 0x00ff0000) >> 16));
+        data_.push_back(static_cast<uint8_t>((data & 0x0000ff00) >> 8));
+        data_.push_back(static_cast<uint8_t>(data & 0x000000ff));
+    }
+    /// \brief Copy an arbitrary length of data into the buffer.
+    ///
+    /// No conversion on the copied data is performed.
+    ///
+    /// \param data A pointer to the data to be copied into the buffer.
+    /// \param len The length of the data in bytes.
+    void writeData(const void *data, size_t len)
+    {
+        const uint8_t* cp = static_cast<const uint8_t*>(data);
+        data_.insert(data_.end(), cp, cp + len);
+    }
+    //@}
+    
+private:
+    std::vector<uint8_t> data_;
+};
+
+/// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer
+///
+/// These types are expected to be used as an argument in asynchronous
+/// callback functions.  The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<InputBuffer> InputBufferPtr;
+typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
+
+} // namespace io
+} // namespace util
+} // namespace isc
+#endif  // __BUFFER_H
+
+// Local Variables: 
+// mode: c++
+// End: 

+ 65 - 0
src/lib/util/io/io_utilities.h

@@ -0,0 +1,65 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __IO_UTILITIES_H
+#define __IO_UTILITIES_H
+
+#include <cstddef>
+
+namespace isc {
+namespace util {
+namespace io {
+
+/// \brief Read Unsigned 16-Bit Integer from Buffer
+///
+/// This is essentially a copy of the isc::util::InputBuffer::readUint16.  It
+/// should really be moved into a separate library.
+///
+/// \param buffer Data buffer at least two bytes long of which the first two
+///        bytes are assumed to represent a 16-bit integer in network-byte
+///        order.
+///
+/// \return Value of 16-bit integer
+inline uint16_t
+readUint16(const void* buffer) {
+    const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
+
+    uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
+    result |= static_cast<uint16_t>(byte_buffer[1]);
+
+    return (result);
+}
+
+/// \brief Write Unisgned 16-Bit Integer to Buffer
+///
+/// This is essentially a copy of isc::util::OutputBuffer::writeUint16.  It
+/// should really be moved into a separate library.
+///
+/// \param value 16-bit value to convert
+/// \param buffer Data buffer at least two bytes long into which the 16-bit
+///        value is written in network-byte order.
+
+inline void
+writeUint16(uint16_t value, void* buffer) {
+    uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
+
+    byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
+    byte_buffer[1] = static_cast<uint8_t>(value & 0x00ffU);
+}
+
+} // namespace io
+} // namespace util
+} // namespace isc
+
+#endif // __ASIOLINK_UTILITIES_H

+ 116 - 0
src/lib/util/locks.h

@@ -0,0 +1,116 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// This file (right now) provides dummy locks
+/// It also contains code to use boost/threads locks:
+///
+/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
+/// and derive from the relevant templates so our dummy locks are
+/// replaced by the boost locks (--enable-boost-threads)
+///
+/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
+/// that don't actually do anything. At this moment, only the very
+/// minimal set of methods that we actually use is defined.
+///
+/// Note that we need to include <config.h> in our .cc files for that
+/// to be set. we might want to enfore this at compile time with a check
+/// (TODO)
+/// Note that this also contains a workaround for Sunstudio; which
+/// probably won't completely work right now (that is, if the TODO
+/// above is completed), since that would also require some changes
+/// in most (at first glance unrelated) Makefiles
+/// (TODO2)
+
+#ifndef __LOCKS_
+#define __LOCKS_
+
+#ifndef USE_BOOST_THREADS
+
+namespace isc {
+namespace locks {
+
+class mutex {
+};
+
+class recursive_mutex {
+};
+
+class upgradable_mutex {
+};
+
+template <typename T>
+class sharable_lock {
+public:
+    sharable_lock(T) { }
+};
+
+template <typename T>
+class scoped_lock {
+public:
+    scoped_lock(T) { }
+
+    void lock() {}
+    void unlock() {}
+};
+
+}
+}
+
+#else // USE_BOOST_THREADS
+
+// Workaround for a problem with boost and sunstudio 5.10
+// There is a version check in there that appears wrong,
+// which makes including boost/thread.hpp fail
+// This will probably be fixed in a future version of boost,
+// in which case this part can be removed then
+#ifdef NEED_SUNPRO_WORKAROUND
+#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+#undef __SUNPRO_CC
+#define __SUNPRO_CC 0x5090
+#endif
+#endif // NEED_SUNPRO_WORKAROUND
+
+#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 <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
+
+namespace isc {
+namespace locks {
+
+typedef boost::mutex mutex;
+typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
+typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
+
+template <typename T>
+struct sharable_lock : public boost::interprocess::sharable_lock<T> {
+public:
+    sharable_lock(T&  mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
+};
+
+
+template <class T>
+struct scoped_lock : public boost::interprocess::scoped_lock<T> {
+public:
+    scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
+};
+
+}
+}
+
+
+#endif // USE_BOOST_THREADS
+
+#endif // __LOCKS_

+ 260 - 0
src/lib/util/lru_list.h

@@ -0,0 +1,260 @@
+// 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.
+
+#ifndef __LRU_LIST_H
+#define __LRU_LIST_H
+
+#include <list>
+#include <string>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <util/locks.h>
+
+namespace isc {
+namespace util {
+
+/// \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> > lru_list;
+    typedef typename lru_list::iterator               iterator;
+
+    /// \brief Dropped Operation
+    ///
+    /// When an object is dropped 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 Dropped {
+    public:
+        /// \brief Constructor
+        Dropped(){}
+
+        /// \brief Virtual Destructor
+        virtual ~Dropped(){}
+
+        /// \brief Dropped Object Handler
+        ///
+        /// Function object called when the object drops off the end of the
+        /// LRU list.
+        ///
+        /// \param drop Object being dropped.
+        virtual void operator()(T* drop) const = 0;
+    };
+
+    /// \brief Constructor
+    ///
+    /// \param max_size Maximum size of the list before elements are dropped.
+    /// \param dropped 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, Dropped* dropped = NULL) :
+        max_size_(max_size), count_(0), dropped_(dropped)
+    {}
+
+    /// \brief Virtual Destructor
+    virtual ~LruList()
+    {}
+
+    /// \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 Drop All the Elements in the List .
+    ///
+    /// All the elements will be dropped from the list container, and their
+    /// drop handler(if there is one) will be called, when done, the size of
+    /// of list will be 0.
+    virtual void clear();
+
+    /// \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() const {
+
+        // 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() const {
+        return max_size_;
+    }
+
+    /// \brief Set Maximum Size
+    ///
+    /// \param max_size New maximum list size
+    virtual void setMaxSize(uint32_t max_size) {
+        max_size_ = max_size;
+    }
+
+private:
+    isc::locks::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<Dropped>          dropped_;   ///< Dropped object
+};
+
+// Add entry to the list
+template <typename T>
+void LruList<T>::add(boost::shared_ptr<T>& element) {
+
+    // Protect list against concurrent access
+    isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+    // Add the entry and set its pointer field to point into the list.
+    // insert() is used to get the pointer.
+    element->setLruIterator(lru_.insert(lru_.end(), element));
+
+    // ... 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 drop handler (if there is one) on the
+
+            // to-be-dropped object.
+            if (dropped_) {
+                (*dropped_)(lru_.begin()->get());
+            }
+
+            // ... 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->iteratorValid()) {
+
+        // Is valid, so protect list against concurrent access
+        isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+        lru_.erase(element->getLruIterator());  // Remove element from list
+        element->invalidateIterator();          // Invalidate pointer
+        --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->iteratorValid()) {
+
+        // Protect list against concurrent access
+        isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+        // Move the element to the end of the list.
+        lru_.splice(lru_.end(), lru_, element->getLruIterator());
+
+        // 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->setLruIterator(--i);
+    }
+}
+
+// Clear the list-  when done, the size of list will be 0.
+template <typename T>
+void LruList<T>::clear() {
+    // Protect list against concurrent access
+    isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+    // ... and update the count while we have the mutex.
+    count_ = 0;
+    typename std::list<boost::shared_ptr<T> >::iterator iter;
+    if (dropped_) {
+        for (iter = lru_.begin(); iter != lru_.end(); ++iter) {
+            // Call the drop handler.
+            (*dropped_)(iter->get());
+        }
+    }
+
+    lru_.clear();
+}
+
+}   // namespace util
+}   // namespace isc
+
+#endif // __LRU_LIST_H

+ 57 - 0
src/lib/util/random/qid_gen.cc

@@ -0,0 +1,57 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#include <util/random/qid_gen.h>
+
+#include <sys/time.h>
+
+namespace isc {
+namespace util {
+namespace random {
+
+QidGenerator qid_generator_instance;
+
+QidGenerator&
+QidGenerator::getInstance() {
+    return (qid_generator_instance);
+}
+
+QidGenerator::QidGenerator() : dist_(0, 65535),
+                               vgen_(generator_, dist_)
+{
+    seed();
+}
+
+void
+QidGenerator::seed() {
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    generator_.seed((tv.tv_sec * 1000000) + tv.tv_usec);
+}
+
+uint16_t
+QidGenerator::generateQid() {
+    return (vgen_());
+}
+
+
+} // namespace random
+} // namespace util
+} // namespace isc

+ 87 - 0
src/lib/util/random/qid_gen.h

@@ -0,0 +1,87 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#ifndef __QID_GEN_H
+#define __QID_GEN_H
+
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+
+namespace isc {
+namespace util {
+namespace random {
+
+/// This class generates Qids for outgoing queries
+///
+/// It is implemented as a singleton; the public way to access it
+/// is to call getInstance()->generateQid().
+///
+/// It automatically seeds it with the current time when it is first
+/// used.
+class QidGenerator {
+public:
+    /// \brief Returns the singleton instance of the QidGenerator
+    ///
+    /// Returns a reference to the singleton instance of the generator
+    static QidGenerator& getInstance();
+
+    /// \brief Default constructor
+    ///
+    /// It is recommended that getInstance is used rather than creating
+    /// separate instances of this class.
+    ///
+    /// The constructor automatically seeds the generator with the
+    /// current time.
+    QidGenerator();
+
+    /// Generate a Qid
+    ///
+    /// \return A random Qid
+    uint16_t generateQid();
+
+    /// \brief Seeds the QidGenerator (based on the current time)
+    ///
+    /// This is automatically called by the constructor
+    void seed();
+
+private:
+    // "Mersenne Twister: A 623-dimensionally equidistributed
+    // uniform pseudo-random number generator", Makoto Matsumoto and
+    // Takuji Nishimura, ACM Transactions on Modeling and Computer
+    // Simulation: Special Issue on Uniform Random Number Generation,
+    // Vol. 8, No. 1, January 1998, pp. 3-30.
+    //
+    // mt19937 is an implementation of one of the pseudo random
+    // generators described in this paper.
+    boost::mt19937 generator_;
+
+    // For qid's we want a uniform distribution
+    boost::uniform_int<> dist_;
+
+    boost::variate_generator<boost::mt19937&, boost::uniform_int<> > vgen_;
+};
+
+
+} // namespace random
+} // namespace util
+} // namespace isc
+
+#endif // __QID_GEN_H

+ 208 - 0
src/lib/util/random/random_number_generator.h

@@ -0,0 +1,208 @@
+// 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.
+
+#ifndef __NSAS_RANDOM_NUMBER_GENERATOR_H
+#define __NSAS_RANDOM_NUMBER_GENERATOR_H
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/uniform_real.hpp>
+#include <boost/random/variate_generator.hpp>
+
+namespace isc {
+namespace util {
+namespace random {
+
+class InvalidLimits : public isc::BadValue {
+public:
+    InvalidLimits(const char* file, size_t line, const char* what) :
+        isc::BadValue(file, line, what) {}
+};
+
+class SumNotOne : public isc::BadValue {
+public:
+    SumNotOne(const char* file, size_t line, const char* what) :
+        isc::BadValue(file, line, what) {}
+};
+
+class InvalidProbValue : public isc::BadValue {
+public:
+    InvalidProbValue(const char* file, size_t line, const char* what) :
+        isc::BadValue(file, line, what) {}
+};
+
+
+
+/// \brief Uniform random integer generator
+///
+/// Generate uniformly distributed integers in range of [min, max]
+class UniformRandomIntegerGenerator{
+public:
+    /// \brief Constructor
+    ///
+    /// \param min The minimum number in the range
+    /// \param max The maximum number in the range
+    UniformRandomIntegerGenerator(int min, int max):
+        min_(std::min(min, max)), max_(std::max(min, max)),
+        dist_(min_, max_), generator_(rng_, dist_)
+    {
+        // To preserve the restriction of the underlying uniform_int class (and
+        // to retain compatibility with earlier versions of the class), we will
+        // abort if the minimum and maximum given are the wrong way round.
+        if (min > max) {
+            isc_throw(InvalidLimits, "minimum limit is greater than maximum "
+                      "when initializing UniformRandomIntegerGenerator");
+        }
+
+        // Init with the current time
+        rng_.seed(time(NULL));
+    }
+
+    /// \brief Generate uniformly distributed integer
+    int operator()() { return generator_(); }
+private:
+    /// Hide default and copy constructor
+    UniformRandomIntegerGenerator();///< Default constructor
+    UniformRandomIntegerGenerator(const UniformRandomIntegerGenerator&); ///< Copy constructor
+
+    int min_;                       ///< The minimum integer that can generate
+    int max_;                       ///< The maximum integer that can generate
+    boost::uniform_int<> dist_;     ///< Distribute uniformly.
+    boost::mt19937 rng_;            ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
+    boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_; ///< Uniform generator
+};
+
+/// \brief Weighted random integer generator
+///
+/// Generate random integers according different probabilities
+class WeightedRandomIntegerGenerator {
+public:
+    /// \brief Constructor
+    ///
+    /// \param probabilities The probabies for all the integers, the probability must be 
+    /// between 0 and 1.0, the sum of probabilities must be equal to 1.
+    /// For example, if the probabilities contains the following values:
+    /// 0.5 0.3 0.2, the 1st integer will be generated more frequently than the
+    /// other integers and the probability is proportional to its value.
+    /// \param min The minimum integer that generated, other integers will be 
+    /// min, min + 1, ..., min + probabilities.size() - 1
+    WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
+        size_t min = 0):
+        dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
+    {
+        // The probabilities must be valid.  Checking is quite an expensive
+        // operation, so is only done in a debug build.
+        assert(areProbabilitiesValid(probabilities));
+
+        // Calculate the partial sum of probabilities
+        std::partial_sum(probabilities.begin(), probabilities.end(),
+                                     std::back_inserter(cumulative_));
+        // Init with the current time
+        rng_.seed(time(NULL));
+    }
+
+    /// \brief Default constructor
+    ///
+    WeightedRandomIntegerGenerator():
+        dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(0)
+    {
+    }
+
+    /// \brief Reset the probabilities
+    ///
+    /// Change the weights of each integers
+    /// \param probabilities The probabies for all the integers
+    /// \param min The minimum integer that generated
+    void reset(const std::vector<double>& probabilities, size_t min = 0)
+    {
+        // The probabilities must be valid.
+        assert(areProbabilitiesValid(probabilities));
+
+        // Reset the cumulative sum
+        cumulative_.clear();
+
+        // Calculate the partial sum of probabilities
+        std::partial_sum(probabilities.begin(), probabilities.end(),
+                                     std::back_inserter(cumulative_));
+
+        // Reset the minimum integer
+        min_ = min;
+    }
+
+    /// \brief Generate weighted random integer
+    size_t operator()()
+    {
+        return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_()) 
+            - cumulative_.begin() + min_;
+    }
+
+private:
+    /// \brief Check the validation of probabilities vector
+    ///
+    /// The probability must be in range of [0, 1.0] and the sum must be equal
+    /// to 1.0.  Empty probabilities are also valid.
+    ///
+    /// Checking the probabilities is quite an expensive operation, so it is
+    /// only done during a debug build (via a call through assert()).  However,
+    /// instead of letting assert() call abort(), if this method encounters an
+    /// error, an exception is thrown.  This makes unit testing somewhat easier.
+    ///
+    /// \param probabilities Vector of probabilities.
+    bool areProbabilitiesValid(const std::vector<double>& probabilities) const
+    {
+        typedef std::vector<double>::const_iterator Iterator;
+        double sum = probabilities.empty() ? 1.0 : 0.0;
+        for(Iterator it = probabilities.begin(); it != probabilities.end(); ++it){
+            //The probability must be in [0, 1.0]
+            if(*it < 0.0 || *it > 1.0) {
+                isc_throw(InvalidProbValue,
+                          "probability must be in the range 0..1");
+            }
+
+            sum += *it;
+        }
+
+        double epsilon = 0.0001;
+        // The sum must be equal to 1
+       if (std::fabs(sum - 1.0) >= epsilon) {
+           isc_throw(SumNotOne, "Sum of probabilities is not equal to 1");
+       }
+
+       return true;
+    }
+
+    std::vector<double> cumulative_;    ///< Partial sum of the probabilities
+    boost::mt19937 rng_;                ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator 
+    boost::uniform_real<> dist_;        ///< Uniformly distributed real numbers
+
+    // Shortcut typedef
+    // This typedef is placed directly before its use, as the sunstudio
+    // compiler could not handle it being anywhere else (don't know why)
+    typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > UniformRealGenerator;
+    UniformRealGenerator uniform_real_gen_;     ///< Uniformly distributed random real numbers generator
+
+    size_t min_;                                ///< The minimum integer that will be generated
+};
+
+}   // namespace random
+}   // namespace util
+}   // namespace isc
+
+#endif//__NSAS_RANDOM_NUMBER_GENERATOR_H

+ 135 - 0
src/lib/util/strutil.cc

@@ -0,0 +1,135 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <numeric>
+
+#include <string.h>
+#include <util/strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace strutil {
+
+// Normalize slashes
+
+void
+normalizeSlash(std::string& name) {
+    if (!name.empty()) {
+        size_t pos = 0;
+        while ((pos = name.find('\\', pos)) != std::string::npos) {
+            name[pos] = '/';
+        }
+    }
+}
+
+// Trim String
+
+string
+trim(const string& instring) {
+    static const char* blanks = " \t\n";
+
+    string retstring = "";
+    if (!instring.empty()) {
+
+        // Search for first non-blank character in the string
+        size_t first = instring.find_first_not_of(blanks);
+        if (first != string::npos) {
+
+            // String not all blanks, so look for last character
+            size_t last = instring.find_last_not_of(blanks);
+
+            // Extract the trimmed substring
+            retstring = instring.substr(first, (last - first + 1));
+        }
+    }
+
+    return (retstring);
+}
+
+// Tokenise string.  As noted in the header, this is locally written to avoid
+// another dependency on a Boost library.
+
+vector<string>
+tokens(const std::string& text, const std::string& delim) {
+    vector<string> result;
+
+    // Search for the first non-delimiter character
+    size_t start = text.find_first_not_of(delim);
+    while (start != string::npos) {
+
+        // Non-delimiter found, look for next delimiter
+        size_t end = text.find_first_of(delim, start);
+        if (end != string::npos) {
+
+            // Delimiter found, so extract string & search for start of next
+            // non-delimiter segment.
+            result.push_back(text.substr(start, (end - start)));
+            start = text.find_first_not_of(delim, end);
+
+        } else {
+
+            // End of string found, extract rest of string and flag to exit
+            result.push_back(text.substr(start));
+            start = string::npos;
+        }
+    }
+
+    return (result);
+}
+
+// Local function to pass to accumulate() for summing up string lengths.
+
+namespace {
+
+size_t
+lengthSum(string::size_type curlen, const string& cur_string) {
+    return (curlen + cur_string.size());
+}
+
+}
+
+// Provide printf-style formatting.
+
+std::string
+format(const std::string& format, const std::vector<std::string>& args) {
+
+    static const string flag = "%s";
+
+    // Initialize return string.  To speed things up, we'll reserve an
+    // appropriate amount of space - current string size, plus length of all
+    // the argument strings, less two characters for each argument (the %s in
+    // the format string is being replaced).
+    string result;
+    size_t length = accumulate(args.begin(), args.end(), format.size(),
+        lengthSum) - (args.size() * flag.size());
+    result.reserve(length);
+
+    // Iterate through replacing all tokens
+    result = format;
+    size_t tokenpos = 0;    // Position of last token replaced
+    int i = 0;              // Index into argument array
+
+    while ((i < args.size()) && (tokenpos != string::npos)) {
+        tokenpos = result.find(flag, tokenpos);
+        if (tokenpos != string::npos) {
+            result.replace(tokenpos, flag.size(), args[i++]);
+        }
+    }
+
+    return (result);
+}
+
+} // namespace strutil
+} // namespace isc

+ 145 - 0
src/lib/util/strutil.h

@@ -0,0 +1,145 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __STRUTIL_H
+#define __STRUTIL_H
+
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace strutil {
+
+/// \brief A Set of C++ Utilities for Manipulating Strings
+
+/// \brief Normalize Backslash
+///
+/// Only relevant to Windows, this replaces all "\" in a string with "/" and
+/// returns the result.  On other systems it is a no-op.  Note that Windows does
+/// recognise file names with the "\" replaced by "/" (at least in system calls,
+/// if not the command line).
+///
+/// \param name Name to be substituted
+void normalizeSlash(std::string& name);
+
+
+/// \brief Trim Leading and Trailing Spaces
+///
+/// Returns a copy of the input string but with any leading or trailing spaces
+/// or tabs removed.
+///
+/// \param instring Input string to modify
+///
+/// \return String with leading and trailing spaces removed
+std::string trim(const std::string& instring);
+
+
+/// \brief Split String into Tokens
+///
+/// Splits a string into tokens (the tokens being delimited by one or more of
+/// the delimiter characters) and returns the tokens in a vector array. Note
+/// that adjacent delimiters are considered to be a single delimiter.
+///
+/// Special cases are:
+/// -# The empty string is considered to be zero tokens.
+/// -# A string comprising nothing but delimiters is considered to be zero
+///    tokens.
+///
+/// The reasoning behind this is that the string can be thought of as having
+/// invisible leading and trailing delimiter characters.  Therefore both cases
+/// reduce to a set of contiguous delimiters, which are considered a single
+/// delimiter (so getting rid of the string).
+///
+/// We could use Boost for this, but this (simple) function eliminates one
+/// dependency in the code.
+///
+/// \param text String to be split.  Passed by value as the internal copy is
+/// altered during the processing.
+/// \param delim Delimiter characters
+///
+/// \return Vector of tokens.
+std::vector<std::string> tokens(const std::string& text,
+        const std::string& delim = std::string(" \t\n"));
+
+
+/// \brief Uppercase Character
+///
+/// Used in uppercase() to pass as an argument to std::transform().  The
+/// function std::toupper() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because dereferencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be upper-cased.
+///
+/// \return Uppercase version of the argument
+inline char toUpper(char chr) {
+    return (static_cast<char>(std::toupper(static_cast<int>(chr))));
+}
+
+
+/// \brief Uppercase String
+///
+/// A convenience function to uppercase a string.
+///
+/// \param text String to be upper-cased.
+inline void uppercase(std::string& text) {
+    std::transform(text.begin(), text.end(), text.begin(),
+        isc::strutil::toUpper);
+}
+
+/// \brief Lowercase Character
+///
+/// Used in lowercase() to pass as an argument to std::transform().  The
+/// function std::tolower() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because dereferencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be lower-cased.
+///
+/// \return Lowercase version of the argument
+inline char toLower(char chr) {
+    return (static_cast<char>(std::tolower(static_cast<int>(chr))));
+}
+
+/// \brief Lowercase String
+///
+/// A convenience function to lowercase a string
+///
+/// \param text String to be lower-cased.
+inline void lowercase(std::string& text) {
+    std::transform(text.begin(), text.end(), text.begin(),
+        isc::strutil::toLower);
+}
+
+
+/// \brief Apply Formatting
+///
+/// Given a printf-style format string containing only "%s" place holders
+/// (others are ignored) and a vector of strings, this produces a single string
+/// with the placeholders replaced.
+///
+/// \param format Format string
+/// \param args Vector of argument strings
+///
+/// \return Resultant string
+std::string format(const std::string& format,
+    const std::vector<std::string>& args);
+
+
+} // namespace strutil
+} // namespace isc
+
+#endif // __STRUTIL_H

+ 41 - 0
src/lib/util/tests/Makefile.am

@@ -0,0 +1,41 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  =
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += base32hex_unittest.cc
+run_unittests_SOURCES += base64_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
+run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += buffer_unittest.cc
+run_unittests_SOURCES += time_utilities_unittest.cc
+run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += io_utilities_unittest.cc
+run_unittests_SOURCES += qid_gen_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/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)

+ 159 - 0
src/lib/util/tests/base32hex_unittest.cc

@@ -0,0 +1,159 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <cctype>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/base32hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::compat;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base32HexTest : public ::testing::Test {
+protected:
+    Base32HexTest() : encoding_chars("0123456789ABCDEFGHIJKLMNOPQRSTUV=") {
+        // test vectors from RFC4648
+        test_sequence.push_back(StringPair("", ""));
+        test_sequence.push_back(StringPair("f", "CO======"));
+        test_sequence.push_back(StringPair("fo", "CPNG===="));
+        test_sequence.push_back(StringPair("foo", "CPNMU==="));
+        test_sequence.push_back(StringPair("foob", "CPNMUOG="));
+        test_sequence.push_back(StringPair("fooba", "CPNMUOJ1"));
+        test_sequence.push_back(StringPair("foobar", "CPNMUOJ1E8======"));
+
+        // the same data, encoded using lower case chars (testable only
+        // for the decode side)
+        test_sequence_lower.push_back(StringPair("f", "co======"));
+        test_sequence_lower.push_back(StringPair("fo", "cpng===="));
+        test_sequence_lower.push_back(StringPair("foo", "cpnmu==="));
+        test_sequence_lower.push_back(StringPair("foob", "cpnmuog="));
+        test_sequence_lower.push_back(StringPair("fooba", "cpnmuoj1"));
+        test_sequence_lower.push_back(StringPair("foobar",
+                                                 "cpnmuoj1e8======"));
+    }
+    vector<StringPair> test_sequence;
+    vector<StringPair> test_sequence_lower;
+    vector<uint8_t> decoded_data;
+    const string encoding_chars;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+            const string& expected)
+{
+    decodeBase32Hex(input_string, output);
+    EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+}
+
+TEST_F(Base32HexTest, decode) {
+    for (vector<StringPair>::const_iterator it = test_sequence.begin();
+         it != test_sequence.end();
+         ++it) {
+        decodeCheck((*it).second, decoded_data, (*it).first);
+    }
+
+    // whitespace should be allowed
+    decodeCheck("CP NM\tUOG=", decoded_data, "foob");
+    decodeCheck("CPNMU===\n", decoded_data, "foo");
+
+    // invalid number of padding characters
+    EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase32Hex("CO0=====", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase32Hex("CO=======", decoded_data), // too many ='s
+                 BadValue);
+
+    // intermediate padding isn't allowed
+    EXPECT_THROW(decodeBase32Hex("CPNMUOG=CPNMUOG=", decoded_data), BadValue);
+
+    // Non canonical form isn't allowed.
+    // P => 25(11001), so the padding byte would be 01000000
+    EXPECT_THROW(decodeBase32Hex("0P======", decoded_data), BadValue);
+}
+
+TEST_F(Base32HexTest, decodeLower) {
+    for (vector<StringPair>::const_iterator it = test_sequence_lower.begin();
+         it != test_sequence_lower.end();
+         ++it) {
+        decodeCheck((*it).second, decoded_data, (*it).first);
+    }
+}
+
+TEST_F(Base32HexTest, encode) {
+    for (vector<StringPair>::const_iterator it = test_sequence.begin();
+         it != test_sequence.end();
+         ++it) {
+        decoded_data.assign((*it).first.begin(), (*it).first.end());
+        EXPECT_EQ((*it).second, encodeBase32Hex(decoded_data));
+    }
+}
+
+// For Base32Hex we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(Base32HexTest, decodeMap) {
+    string input(8, '0');       // input placeholder
+
+    // We're going to populate an input string with only the last character
+    // not equal to the zero character ('0') for each valid base32hex encoding
+    // character.  Decoding that input should result in a data stream with
+    // the last byte equal to the numeric value represented by the that
+    // character.  For example, we'll generate and confirm the following:
+    // "00000000" => should be 0 (as a 40bit integer)
+    // "00000001" => should be 1 (as a 40bit integer)
+    // ...
+    // "0000000V" => should be 31 (as a 40bit integer)
+    // We also check the use of an invalid character for the last character
+    // surely fails. '=' should be accepted as a valid padding in this
+    // context; space characters shouldn't be allowed in this context.
+
+    for (int i = 0; i < 256; ++i) {
+        input[7] = i;
+
+        const char ch = toupper(i);
+        const size_t pos = encoding_chars.find(ch);
+        if (pos == string::npos) {
+            EXPECT_THROW(decodeBase32Hex(input, decoded_data), BadValue);
+        } else {
+            decodeBase32Hex(input, decoded_data);
+            if (ch == '=') {
+                EXPECT_EQ(4, decoded_data.size());
+            } else {
+                EXPECT_EQ(5, decoded_data.size());
+                EXPECT_EQ(pos, decoded_data[4]);
+            }
+        }
+    }
+}
+
+TEST_F(Base32HexTest, encodeMap) {
+    for (int i = 0; i < 32; ++i) {
+        decoded_data.assign(4, 0);
+        decoded_data.push_back(i);
+        EXPECT_EQ(encoding_chars[i], encodeBase32Hex(decoded_data)[7]);
+    }
+}
+
+}

+ 93 - 0
src/lib/util/tests/base64_unittest.cc

@@ -0,0 +1,93 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/base64.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::compat;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base64Test : public ::testing::Test {
+protected:
+    Base64Test()
+    {
+        // test vectors from RFC4648
+        test_sequence.push_back(StringPair("", ""));
+        test_sequence.push_back(StringPair("f", "Zg=="));
+        test_sequence.push_back(StringPair("fo", "Zm8="));
+        test_sequence.push_back(StringPair("foo", "Zm9v"));
+        test_sequence.push_back(StringPair("foob", "Zm9vYg=="));
+        test_sequence.push_back(StringPair("fooba", "Zm9vYmE="));
+        test_sequence.push_back(StringPair("foobar", "Zm9vYmFy"));
+    }
+    vector<StringPair> test_sequence;
+    vector<uint8_t> decoded_data;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+            const string& expected)
+{
+    decodeBase64(input_string, output);
+    EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+}
+
+TEST_F(Base64Test, decode) {
+    for (vector<StringPair>::const_iterator it = test_sequence.begin();
+         it != test_sequence.end();
+         ++it) {
+        decodeCheck((*it).second, decoded_data, (*it).first);
+    }
+
+    // whitespace should be allowed
+    decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
+    decodeCheck("Zm9vYg==", decoded_data, "foob");
+    decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
+
+    // only up to 2 padding characters are allowed
+    EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
+    EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadValue);
+
+    // intermediate padding isn't allowed
+    EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadValue);
+
+    // Non canonical form isn't allowed.
+    // Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
+    // byte would be 0100 0000.
+    EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadValue);
+    // Same for the 1st padding byte.  This would make it 01100000.
+    EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadValue);
+}
+
+TEST_F(Base64Test, encode) {
+    for (vector<StringPair>::const_iterator it = test_sequence.begin();
+         it != test_sequence.end();
+         ++it) {
+        decoded_data.assign((*it).first.begin(), (*it).first.end());
+        EXPECT_EQ((*it).second, encodeBase64(decoded_data));
+    }
+}
+}

+ 193 - 0
src/lib/util/tests/buffer_unittest.cc

@@ -0,0 +1,193 @@
+// 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.
+
+#include <exceptions/exceptions.h>
+
+#include <util/io/buffer.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+
+namespace {
+
+using isc::util::io::InputBuffer;
+using isc::util::io::OutputBuffer;
+
+class BufferTest : public ::testing::Test {
+protected:
+    BufferTest() : ibuffer(testdata, sizeof(testdata)), obuffer(0),
+                   expected_size(0)
+    {
+        data16 = (2 << 8) | 3;
+        data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
+        memset(vdata, 0, sizeof(testdata));
+    }
+
+    InputBuffer ibuffer;
+    OutputBuffer obuffer;
+    static const uint8_t testdata[5];
+    uint8_t vdata[sizeof(testdata)];
+    size_t expected_size;
+    uint16_t data16;
+    uint32_t data32;
+};
+
+const uint8_t BufferTest::testdata[5] = {1, 2, 3, 4, 5};
+
+TEST_F(BufferTest, inputBufferRead) {
+    EXPECT_EQ(5, ibuffer.getLength());
+    EXPECT_EQ(1, ibuffer.readUint8());
+    EXPECT_EQ(1, ibuffer.getPosition());
+    data16 = ibuffer.readUint16();
+    EXPECT_EQ((2 << 8) | 3, data16);
+    EXPECT_EQ(3, ibuffer.getPosition());
+    ibuffer.setPosition(1);
+    EXPECT_EQ(1, ibuffer.getPosition());
+    data32 = ibuffer.readUint32();
+    EXPECT_EQ((2 << 24) | (3 << 16) | (4 << 8) | 5, data32);
+    ibuffer.setPosition(0);
+    memset(vdata, 0, sizeof(vdata));
+    ibuffer.readData(vdata, sizeof(vdata));
+    EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata)));
+}
+
+TEST_F(BufferTest, inputBufferException) {
+    EXPECT_THROW(ibuffer.setPosition(6), isc::util::io::InvalidBufferPosition);
+
+    ibuffer.setPosition(sizeof(testdata));
+    EXPECT_THROW(ibuffer.readUint8(), isc::util::io::InvalidBufferPosition);
+
+    ibuffer.setPosition(sizeof(testdata) - 1);
+    EXPECT_THROW(ibuffer.readUint16(), isc::util::io::InvalidBufferPosition);
+
+    ibuffer.setPosition(sizeof(testdata) - 3);
+    EXPECT_THROW(ibuffer.readUint32(), isc::util::io::InvalidBufferPosition);
+
+    ibuffer.setPosition(sizeof(testdata) - 4);
+    EXPECT_THROW(ibuffer.readData(vdata, sizeof(vdata)),
+                 isc::util::io::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferExtend) {
+    EXPECT_EQ(0, obuffer.getCapacity());
+    EXPECT_EQ(0, obuffer.getLength());
+    obuffer.writeUint8(10);
+    EXPECT_LT(0, obuffer.getCapacity());
+    EXPECT_EQ(1, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferWrite) {
+    const uint8_t* cp;
+
+    obuffer.writeUint8(1);
+    expected_size += sizeof(uint8_t);
+    EXPECT_EQ(expected_size, obuffer.getLength());
+    cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(1, *cp);
+
+    obuffer.writeUint16(data16);
+    expected_size += sizeof(data16);
+    cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(expected_size, obuffer.getLength());
+    EXPECT_EQ(2, *(cp + 1));
+    EXPECT_EQ(3, *(cp + 2));
+
+    obuffer.writeUint32(data32);
+    expected_size += sizeof(data32);
+    cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(expected_size, obuffer.getLength());
+    EXPECT_EQ(4, *(cp + 3));
+    EXPECT_EQ(5, *(cp + 4));
+    EXPECT_EQ(6, *(cp + 5));
+    EXPECT_EQ(7, *(cp + 6));
+
+    obuffer.writeData(testdata, sizeof(testdata));
+    expected_size += sizeof(testdata);
+    EXPECT_EQ(expected_size, obuffer.getLength());
+    cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(0, memcmp(cp + 7, testdata, sizeof(testdata)));
+}
+
+TEST_F(BufferTest, outputBufferWriteat) {
+    obuffer.writeUint32(data32);
+    expected_size += sizeof(data32);
+
+    // overwrite 2nd byte
+    obuffer.writeUint8At(4, 1);
+    EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+    const uint8_t* cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(4, *(cp + 1));
+
+    // overwrite 2nd and 3rd bytes
+    obuffer.writeUint16At(data16, 1);
+    EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+    cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(2, *(cp + 1));
+    EXPECT_EQ(3, *(cp + 2));
+
+    // overwrite 3rd and 4th bytes
+    obuffer.writeUint16At(data16, 2);
+    EXPECT_EQ(expected_size, obuffer.getLength());
+    cp = static_cast<const uint8_t*>(obuffer.getData());
+    EXPECT_EQ(2, *(cp + 2));
+    EXPECT_EQ(3, *(cp + 3));
+
+    EXPECT_THROW(obuffer.writeUint8At(data16, 5),
+                 isc::util::io::InvalidBufferPosition);
+    EXPECT_THROW(obuffer.writeUint8At(data16, 4),
+                 isc::util::io::InvalidBufferPosition);
+    EXPECT_THROW(obuffer.writeUint16At(data16, 3),
+                 isc::util::io::InvalidBufferPosition);
+    EXPECT_THROW(obuffer.writeUint16At(data16, 4),
+                 isc::util::io::InvalidBufferPosition);
+    EXPECT_THROW(obuffer.writeUint16At(data16, 5),
+                 isc::util::io::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferSkip) {
+    obuffer.skip(4);
+    EXPECT_EQ(4, obuffer.getLength());
+
+    obuffer.skip(2);
+    EXPECT_EQ(6, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferTrim) {
+    obuffer.writeData(testdata, sizeof(testdata));
+    EXPECT_EQ(5, obuffer.getLength());
+
+    obuffer.trim(1);
+    EXPECT_EQ(4, obuffer.getLength());
+
+    obuffer.trim(2);
+    EXPECT_EQ(2, obuffer.getLength());
+
+    EXPECT_THROW(obuffer.trim(3), OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferReadat) {
+    obuffer.writeData(testdata, sizeof(testdata));
+    for (int i = 0; i < sizeof(testdata); i ++) {
+        EXPECT_EQ(testdata[i], obuffer[i]);
+    }
+    EXPECT_THROW(obuffer[sizeof(testdata)], isc::util::io::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferClear) {
+    obuffer.writeData(testdata, sizeof(testdata));
+    obuffer.clear();
+    EXPECT_EQ(0, obuffer.getLength());
+}
+}

+ 179 - 0
src/lib/util/tests/filename_unittest.cc

@@ -0,0 +1,179 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <util/filename.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+class FilenameTest : public ::testing::Test {
+protected:
+    FilenameTest()
+    {
+    }
+};
+
+
+// Check that the name can be changed
+
+TEST_F(FilenameTest, SetName) {
+    Filename fname("/a/b/c.d");
+    EXPECT_EQ("/a/b/c.d", fname.fullName());
+
+    fname.setName("test.txt");
+    EXPECT_EQ("test.txt", fname.fullName());
+}
+
+
+// Check that the components are split correctly.  This is a check of the
+// private member split() method.
+
+TEST_F(FilenameTest, Components) {
+
+    // Complete name
+    Filename fname("/alpha/beta/gamma.delta");
+    EXPECT_EQ("/alpha/beta/", fname.directory());
+    EXPECT_EQ("gamma", fname.name());
+    EXPECT_EQ(".delta", fname.extension());
+
+    // Directory only
+    fname.setName("/gamma/delta/");
+    EXPECT_EQ("/gamma/delta/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Filename only
+    fname.setName("epsilon");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("epsilon", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Extension only
+    fname.setName(".zeta");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".zeta", fname.extension());
+
+    // Missing directory
+    fname.setName("eta.theta");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("eta", fname.name());
+    EXPECT_EQ(".theta", fname.extension());
+
+    // Missing filename
+    fname.setName("/iota/.kappa");
+    EXPECT_EQ("/iota/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".kappa", fname.extension());
+
+    // Missing extension
+    fname.setName("lambda/mu/nu");
+    EXPECT_EQ("lambda/mu/", fname.directory());
+    EXPECT_EQ("nu", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Check that the decomposition can occur in the presence of leading and
+    // trailing spaces
+    fname.setName("  lambda/mu/nu\t  ");
+    EXPECT_EQ("lambda/mu/", fname.directory());
+    EXPECT_EQ("nu", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Empty string
+    fname.setName("");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // ... and just spaces
+    fname.setName("  ");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    // Check corner cases - where separators are present, but strings are
+    // absent.
+    fname.setName("/");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ("", fname.extension());
+
+    fname.setName(".");
+    EXPECT_EQ("", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".", fname.extension());
+
+    fname.setName("/.");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ("", fname.name());
+    EXPECT_EQ(".", fname.extension());
+
+    // Note that the space is a valid filename here; only leading and trailing
+    // spaces should be trimmed.
+    fname.setName("/ .");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ(" ", fname.name());
+    EXPECT_EQ(".", fname.extension());
+
+    fname.setName(" / . ");
+    EXPECT_EQ("/", fname.directory());
+    EXPECT_EQ(" ", fname.name());
+    EXPECT_EQ(".", fname.extension());
+}
+
+// Check that the expansion with a default works.
+
+TEST_F(FilenameTest, ExpandWithDefault) {
+    Filename fname("a.b");
+
+    // These tests also check that the trimming of the default component is
+    // done properly.
+    EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/  "));
+    EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
+    EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
+
+    fname.setName("/a/b/c");
+    EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
+    EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
+    EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
+    EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
+
+    fname.setName(".h");
+    EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
+}
+
+// Check that we can use this as a default in expanding a filename
+
+TEST_F(FilenameTest, UseAsDefault) {
+
+    Filename fname("a.b");
+
+    // These tests also check that the trimming of the default component is
+    // done properly.
+    EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/  "));
+    EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
+    EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
+
+    fname.setName("/a/b/c");
+    EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
+    EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
+    EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
+    EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
+    EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
+}

+ 120 - 0
src/lib/util/tests/hex_unittest.cc

@@ -0,0 +1,120 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <vector>
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::compat;
+
+namespace {
+const string hex_txt("DEADBEEFDECADE");
+const string hex_txt_space("DEAD BEEF DECADE");
+const string hex_txt_lower("deadbeefdecade");
+
+class HexTest : public ::testing::Test {
+protected:
+    HexTest() : encoding_chars("0123456789ABCDEF") {}
+    vector<uint8_t> decoded_data;
+    const string encoding_chars;
+};
+
+TEST_F(HexTest, encodeHex) {
+    std::vector<uint8_t> data;
+
+    data.push_back(0xde);
+    data.push_back(0xad);
+    data.push_back(0xbe);
+    data.push_back(0xef);
+    data.push_back(0xde);
+    data.push_back(0xca);
+    data.push_back(0xde);
+    EXPECT_EQ(hex_txt, encodeHex(data));
+}
+
+void
+compareData(const std::vector<uint8_t>& data) {
+    EXPECT_EQ(0xde, data[0]);
+    EXPECT_EQ(0xad, data[1]);
+    EXPECT_EQ(0xbe, data[2]);
+    EXPECT_EQ(0xef, data[3]);
+    EXPECT_EQ(0xde, data[4]);
+    EXPECT_EQ(0xca, data[5]);
+    EXPECT_EQ(0xde, data[6]);
+}
+
+TEST_F(HexTest, decodeHex) {
+    std::vector<uint8_t> result;
+
+    decodeHex(hex_txt, result);
+    compareData(result);
+
+    // lower case hex digits should be accepted
+    result.clear();
+    decodeHex(hex_txt_lower, result);
+    compareData(result);
+
+    // white space should be ignored
+    result.clear();
+    decodeHex(hex_txt_space, result);
+    compareData(result);
+
+    // Bogus input: should fail
+    result.clear();
+    EXPECT_THROW(decodeHex("1x", result), BadValue);
+
+    // Bogus input: encoded string must have an even number of characters.
+    result.clear();
+    EXPECT_THROW(decodeHex("dea", result), BadValue);
+}
+
+// For Hex encode/decode we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(HexTest, decodeMap) {
+    string input("00");       // input placeholder
+
+    // See Base32HexTest.decodeMap for details of the following tests.
+    for (int i = 0; i < 256; ++i) {
+        input[1] = i;
+
+        const char ch = toupper(i);
+        const size_t pos = encoding_chars.find(ch);
+        if (pos == string::npos) {
+            EXPECT_THROW(decodeHex(input, decoded_data), BadValue);
+        } else {
+            decodeHex(input, decoded_data);
+            EXPECT_EQ(1, decoded_data.size());
+            EXPECT_EQ(pos, decoded_data[0]);
+        }
+    }
+}
+
+TEST_F(HexTest, encodeMap) {
+    for (int i = 0; i < 16; ++i) {
+        decoded_data.clear();
+        decoded_data.push_back(i);
+        EXPECT_EQ(encoding_chars[i], encodeHex(decoded_data)[1]);
+    }
+}
+
+}

+ 73 - 0
src/lib/util/tests/io_utilities_unittest.cc

@@ -0,0 +1,73 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// \brief Test of asiolink utilties
+///
+/// Tests the fuctionality of the asiolink utilities code by comparing them
+/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
+
+#include <cstddef>
+
+#include <gtest/gtest.h>
+
+#include <util/io/buffer.h>
+#include <util/io/io_utilities.h>
+
+using namespace isc::util::io;
+
+TEST(asioutil, readUint16) {
+
+    // Reference buffer
+    uint8_t data[2];
+    InputBuffer buffer(data, sizeof(data));
+
+    // Avoid possible compiler warnings by only setting uint8_t variables to
+    // uint8_t values.
+    uint8_t i8 = 0;
+    uint8_t j8 = 0;
+    for (int i = 0; i < (2 << 8); ++i, ++i8) {
+        for (int j = 0; j < (2 << 8); ++j, ++j8) {
+            data[0] = i8;
+            data[1] = j8;
+            buffer.setPosition(0);
+            EXPECT_EQ(buffer.readUint16(), readUint16(data));
+        }
+    }
+}
+
+
+TEST(asioutil, writeUint16) {
+
+    // Reference buffer
+    OutputBuffer buffer(2);
+    uint8_t test[2];
+
+    // Avoid possible compiler warnings by only setting uint16_t variables to
+    // uint16_t values.
+    uint16_t i16 = 0;
+    for (uint32_t i = 0; i < (2 << 16); ++i, ++i16) {
+
+        // Write the reference data
+        buffer.clear();
+        buffer.writeUint16(i16);
+
+        // ... and the test data
+        writeUint16(i16, test);
+
+        // ... and compare
+        const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
+        EXPECT_EQ(ref[0], test[0]);
+        EXPECT_EQ(ref[1], test[1]);
+    }
+}

+ 427 - 0
src/lib/util/tests/lru_list_unittest.cc

@@ -0,0 +1,427 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <util/lru_list.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+
+/// \brief Invalid Iterator
+///
+/// Thrown if an attempt was made to access the iterator - the pointer into
+/// the LRU list where this element is located - when it is marked as invalid.
+class InvalidLruIterator : public isc::Exception {
+public:
+    InvalidLruIterator(const char* file, size_t line, const char* what) :
+        Exception(file, line, what)
+    {}
+};
+
+template <typename T>
+class TestEntryT : public boost::enable_shared_from_this <T> {
+public:
+
+    /// \brief Constructor
+    ///
+    /// \param name Name that will be used for the object.  This will form
+    /// part of the key.
+    /// \param class_code Class associated with the object.
+    TestEntryT() : valid_(false)
+    {}
+
+    /// \brief Virtual Destructor
+    virtual ~TestEntryT()
+        {}
+
+    /// \brief Sets the iterator of the object
+    ///
+    /// Sets the iterator of an object and, as a side effect, marks it as valid.
+    ///
+    /// \param iterator Iterator of this element in the list
+    virtual void setLruIterator(typename LruList<T>::iterator iterator) {
+        iterator_ = iterator;
+        valid_ = true;
+    }
+
+    /// \brief Return Iterator
+    ///
+    /// \return iterator Iterator of this element in the list.
+    ///
+    /// \exception InvalidLruIterator Thrown if the iterator is not valid.
+    virtual typename LruList<T>::iterator getLruIterator() const {
+        /*if (! valid_) {
+            isc_throw(InvalidLruIterator,
+                "pointer of element into LRU list was not valid");
+        }*/
+        return iterator_;
+    }
+
+    /// \brief Iterator Valid
+    ///
+    /// \return true if the stored iterator is valid.
+    virtual bool iteratorValid() const {
+        return valid_;
+    }
+
+    /// \brief Invalidate Iterator
+    ///
+    /// Marks the iterator as invalid; it can oly be set valid again by a call
+    /// to setLruIterator.
+    virtual void invalidateIterator() {
+        valid_ = false;
+    }
+
+private:
+    typename LruList<T>::iterator  iterator_;  ///< Handle into the LRU List
+    bool                           valid_;     ///< true if handle is valid
+};
+
+class TestEntry : public TestEntryT<TestEntry> {
+public:
+    TestEntry(std::string name, const int & code) :
+        name_(name), code_(code)
+    {}
+
+    /// \brief Get the Name
+    ///
+    /// \return Name given to this object
+    virtual std::string getName() const {
+        return name_;
+    }
+
+    /// \brief Set the Name
+    ///
+    /// \param name New name of the object
+    virtual void setName(const std::string& name) {
+        name_ = name;
+    }
+
+    /// \brief Get the Class
+    ///
+    /// \return Class code assigned to this object
+    virtual const int& getCode() const {
+        return code_;
+    }
+
+    /// \brief Set the Class
+    ///
+    /// \param code New code of the object
+    virtual void setCode(const int& code) {
+        code_ = code;
+    }
+
+private:
+    std::string name_;          ///< Name of the object
+    int code_;    ///< Class of the object
+
+};
+
+/// \brief Dropped Functor Class
+///
+/// Functor object is called when an object is dropped from the LRU list.
+/// To prove that it has run, this function does nothing more than set the
+/// MS bit on the 16-bit code value.
+class Dropped : public LruList<TestEntry>::Dropped {
+public:
+    virtual void operator()(TestEntry* entry) const {
+        entry->setCode(entry->getCode() | 0x8000);
+    }
+};
+
+
+/// \brief Text Fixture Class
+class LruListTest : public ::testing::Test {
+protected:
+    LruListTest() :
+        entry1_(new TestEntry("alpha", 1)),
+        entry2_(new TestEntry("beta", 3)),
+        entry3_(new TestEntry("gamma", 4)),
+        entry4_(new TestEntry("delta", 1)),
+        entry5_(new TestEntry("epsilon", 4)),
+        entry6_(new TestEntry("zeta", 3)),
+        entry7_(new TestEntry("eta", 1))
+    {}
+
+    virtual ~LruListTest() 
+    {}
+
+    boost::shared_ptr<TestEntry>    entry1_;
+    boost::shared_ptr<TestEntry>    entry2_;
+    boost::shared_ptr<TestEntry>    entry3_;
+    boost::shared_ptr<TestEntry>    entry4_;
+    boost::shared_ptr<TestEntry>    entry5_;
+    boost::shared_ptr<TestEntry>    entry6_;
+    boost::shared_ptr<TestEntry>    entry7_;
+};
+
+
+// Test of the constructor
+TEST_F(LruListTest, Constructor) {
+    LruList<TestEntry>  lru(100);
+    EXPECT_EQ(100, lru.getMaxSize());
+    EXPECT_EQ(0, lru.size());
+}
+
+// Test of Get/Set the maximum number of entrys
+TEST_F(LruListTest, GetSet) {
+    LruList<TestEntry>  lru(100);
+    EXPECT_EQ(100, lru.getMaxSize());
+    lru.setMaxSize(42);
+    EXPECT_EQ(42, lru.getMaxSize());
+}
+
+// Test that adding an entry really does add an entry
+TEST_F(LruListTest, Add) {
+    LruList<TestEntry>  lru(100);
+    EXPECT_EQ(0, lru.size());
+
+    lru.add(entry1_);
+    EXPECT_EQ(1, lru.size());
+
+    lru.add(entry2_);
+    EXPECT_EQ(2, lru.size());
+}
+
+// Test that removing an entry really does remove it.
+TEST_F(LruListTest, Remove) {
+    LruList<TestEntry>  lru(100);
+    EXPECT_EQ(0, lru.size());
+
+    EXPECT_FALSE(entry1_->iteratorValid());
+    lru.add(entry1_);
+    EXPECT_TRUE(entry1_->iteratorValid());
+    EXPECT_EQ(1, lru.size());
+
+    EXPECT_FALSE(entry2_->iteratorValid());
+    lru.add(entry2_);
+    EXPECT_TRUE(entry2_->iteratorValid());
+    EXPECT_EQ(2, lru.size());
+
+    lru.remove(entry1_);
+    EXPECT_FALSE(entry1_->iteratorValid());
+    EXPECT_EQ(1, lru.size());
+}
+
+// Check that adding a new entry to a limited size list does delete the
+// oldest entry from the list.
+TEST_F(LruListTest, SizeLimit) {
+    LruList<TestEntry>  lru(3);
+    EXPECT_EQ(0, lru.size());
+
+    // Add first entry and check that the shared pointer's reference count
+    // has increased.  There will be two references: one from the "entry1_"
+    // member in the test fixture class, and one from the list.
+    EXPECT_EQ(1, entry1_.use_count());
+    lru.add(entry1_);
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(1, lru.size());
+
+    // Same for entry 2.
+    EXPECT_EQ(1, entry2_.use_count());
+    lru.add(entry2_);
+    EXPECT_EQ(2, entry2_.use_count());
+    EXPECT_EQ(2, lru.size());
+
+    // Same for entry 3.
+    EXPECT_EQ(1, entry3_.use_count());
+    lru.add(entry3_);
+    EXPECT_EQ(2, entry3_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // Adding entry 4 should remove entry 1 from the list.  This will
+    // delete the list's shared pointer to the entry and will therefore
+    // drop the reference count back to one (from the "entry1_" member in
+    // the text fixture class).
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(1, entry4_.use_count());
+    lru.add(entry4_);
+    EXPECT_EQ(1, entry1_.use_count());
+    EXPECT_EQ(2, entry4_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // Adding entry 5 should remove entry 2 from the list.
+    EXPECT_EQ(2, entry2_.use_count());
+    EXPECT_EQ(1, entry5_.use_count());
+    lru.add(entry5_);
+    EXPECT_EQ(1, entry2_.use_count());
+    EXPECT_EQ(2, entry5_.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<TestEntry>  lru(3);
+    EXPECT_EQ(0, lru.size());
+    lru.add(entry1_);
+    lru.add(entry2_);
+    lru.add(entry3_);
+
+    // Check the reference counts of the entrys and the list size
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(2, entry2_.use_count());
+    EXPECT_EQ(2, entry3_.use_count());
+    EXPECT_EQ(1, entry4_.use_count());
+    EXPECT_EQ(1, entry5_.use_count());
+    EXPECT_EQ(1, entry6_.use_count());
+    EXPECT_EQ(1, entry7_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // "Touch" the first entry
+    lru.touch(entry1_);
+
+    // Adding two more entries should not remove the touched entry.
+    lru.add(entry4_);
+    lru.add(entry5_);
+
+    // Check the status of the entrys and the list.
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(1, entry2_.use_count());
+    EXPECT_EQ(1, entry3_.use_count());
+    EXPECT_EQ(2, entry4_.use_count());
+    EXPECT_EQ(2, entry5_.use_count());
+    EXPECT_EQ(1, entry6_.use_count());
+    EXPECT_EQ(1, entry7_.use_count());
+    EXPECT_EQ(3, lru.size());
+
+    // Now touch the entry agin to move it to the back of the list.
+    // This checks that the iterator stored in the entry as a result of the
+    // last touch operation is valid.
+    lru.touch(entry1_);
+
+    // Check this by adding two more entrys and checking reference counts
+    // to see what is stored.
+    lru.add(entry6_);
+    lru.add(entry7_);
+
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(1, entry2_.use_count());
+    EXPECT_EQ(1, entry3_.use_count());
+    EXPECT_EQ(1, entry4_.use_count());
+    EXPECT_EQ(1, entry5_.use_count());
+    EXPECT_EQ(2, entry6_.use_count());
+    EXPECT_EQ(2, entry7_.use_count());
+    EXPECT_EQ(3, lru.size());
+}
+
+// Dropped functor tests: tests that the function object is called when an
+// object expires from the list.
+TEST_F(LruListTest, Dropped) {
+
+    // Create an object with an expiration handler.
+    LruList<TestEntry> lru(3, new Dropped());
+
+    // Fill the list
+    lru.add(entry1_);
+    lru.add(entry2_);
+    lru.add(entry3_);
+
+    EXPECT_EQ(1, entry1_->getCode());
+    EXPECT_EQ(3, entry2_->getCode());
+
+    // Add another entry and check that the handler runs.
+    EXPECT_EQ(0, (entry1_->getCode() & 0x8000));
+    lru.add(entry4_);
+    EXPECT_NE(0, (entry1_->getCode() & 0x8000));
+
+    EXPECT_EQ(0, (entry2_->getCode() & 0x8000));
+    lru.add(entry5_);
+    EXPECT_NE(0, (entry2_->getCode() & 0x8000));
+
+    // Delete an entry and check that the handler does not run. 
+    EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+    lru.remove(entry3_);
+    EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+}
+
+// Clear functor tests: tests whether all the elements in
+// the list are dropped properly and the size of list is
+// set to 0.
+TEST_F(LruListTest, Clear) {
+    // Create an object with an expiration handler.
+    LruList<TestEntry> lru(3, new Dropped());
+
+    // Fill the list
+    lru.add(entry1_);
+    lru.add(entry2_);
+    lru.add(entry3_);
+
+    EXPECT_EQ(1, entry1_->getCode());
+    EXPECT_EQ(3, entry2_->getCode());
+    EXPECT_EQ(4, entry3_->getCode());
+
+    EXPECT_EQ(0, (entry1_->getCode() & 0x8000));
+    EXPECT_EQ(0, (entry2_->getCode() & 0x8000));
+    EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+
+    // Clear the lru list, and check the drop handler run
+    lru.clear();
+    EXPECT_NE(0, (entry1_->getCode() & 0x8000));
+    EXPECT_NE(0, (entry2_->getCode() & 0x8000));
+    EXPECT_NE(0, (entry3_->getCode() & 0x8000));
+ 
+    EXPECT_EQ(0, lru.size());
+}
+
+// Miscellaneous tests - pathological conditions
+TEST_F(LruListTest, Miscellaneous) {
+
+    // Zero size list should not allow entrys to be added
+    LruList<TestEntry> lru_1(0);
+    lru_1.add(entry1_);
+    EXPECT_EQ(0, lru_1.size());
+    EXPECT_EQ(1, entry1_.use_count());
+
+    // Removing an uninserted entry should not affect the list.
+    LruList<TestEntry> lru_2(100);
+    lru_2.add(entry1_);
+    lru_2.add(entry2_);
+    lru_2.add(entry3_);
+    EXPECT_EQ(3, lru_2.size());
+
+    lru_2.remove(entry4_);
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(2, entry2_.use_count());
+    EXPECT_EQ(2, entry3_.use_count());
+    EXPECT_EQ(1, entry4_.use_count());
+    EXPECT_EQ(1, entry5_.use_count());
+    EXPECT_EQ(3, lru_2.size());
+
+    // Touching an uninserted entry should not affect the list.
+    lru_2.touch(entry5_);
+    EXPECT_EQ(2, entry1_.use_count());
+    EXPECT_EQ(2, entry2_.use_count());
+    EXPECT_EQ(2, entry3_.use_count());
+    EXPECT_EQ(1, entry4_.use_count());
+    EXPECT_EQ(1, entry5_.use_count());
+    EXPECT_EQ(3, lru_2.size());
+}
+
+}   // namespace nsas
+}   // namespace isc

+ 60 - 0
src/lib/util/tests/qid_gen_unittest.cc

@@ -0,0 +1,60 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+/// \brief Test of QidGenerator
+///
+
+#include <gtest/gtest.h>
+
+#include <util/random/qid_gen.h>
+
+using namespace isc::util::random;
+
+// Tests the operation of the Qid generator
+
+// Check that getInstance returns a singleton
+TEST(QidGenerator, singleton) {
+    QidGenerator& g1 = QidGenerator::getInstance();
+    QidGenerator& g2 = QidGenerator::getInstance();
+
+    EXPECT_TRUE(&g1 == &g2);
+}
+
+TEST(QidGenerator, generate) {
+    // We'll assume that boost's generator is 'good enough', and won't
+    // do full statistical checking here. Let's just call it the xkcd
+    // test (http://xkcd.com/221/), and check if three consecutive
+    // generates are not all the same.
+    uint16_t one, two, three;
+    QidGenerator& gen = QidGenerator::getInstance();
+    one = gen.generateQid();
+    two = gen.generateQid();
+    three = gen.generateQid();
+    ASSERT_FALSE((one == two) && (one == three));
+}

+ 311 - 0
src/lib/util/tests/random_number_generator_unittest.cc

@@ -0,0 +1,311 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <gtest/gtest.h>
+#include <boost/shared_ptr.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <vector>
+
+#include <util/random/random_number_generator.h>
+
+namespace isc {
+namespace util {
+namespace random {
+
+using namespace std;
+
+/// \brief Test Fixture Class for uniform random number generator
+///
+/// The hard part for this test is how to test that the number is random?
+/// and how to test that the number is uniformly distributed? 
+/// Or maybe we can trust the boost implementation
+class UniformRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+    UniformRandomIntegerGeneratorTest():
+        gen_(min_, max_)
+    {
+    }
+    virtual ~UniformRandomIntegerGeneratorTest(){}
+
+    int gen() { return gen_(); }
+    int max() const { return max_; }
+    int min() const { return min_; }
+
+private:
+    UniformRandomIntegerGenerator gen_;
+
+    const static int min_ = 1;
+    const static int max_ = 10;
+};
+
+// Some validation tests will incur performance penalty, so the tests are
+// made only in "debug" version with assert(). But if NDEBUG is defined
+// the tests will be failed since assert() is non-op in non-debug version.
+// The "#ifndef NDEBUG" is added to make the tests be performed only in
+// non-debug environment.
+// Note: the death test is not supported by all platforms.  We need to
+// compile tests using it selectively.
+#if !defined(NDEBUG)
+// Test of the constructor
+TEST_F(UniformRandomIntegerGeneratorTest, Constructor) {
+    // The range must be min<=max
+    ASSERT_THROW(UniformRandomIntegerGenerator(3, 2), InvalidLimits);
+}
+#endif
+
+// Test of the generated integers are in the range [min, max]
+TEST_F(UniformRandomIntegerGeneratorTest, IntegerRange) {
+    vector<int> numbers;
+
+    // Generate a lot of random integers
+    for (int i = 0; i < max()*10; ++i) {
+        numbers.push_back(gen());
+    }
+
+    // Remove the duplicated values
+    sort(numbers.begin(), numbers.end());
+    vector<int>::iterator it = unique(numbers.begin(), numbers.end());
+
+    // make sure the numbers are in range [min, max]
+    ASSERT_EQ(it - numbers.begin(), max() - min() + 1); 
+}
+
+/// \brief Test Fixture Class for weighted random number generator
+class WeightedRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+    WeightedRandomIntegerGeneratorTest()
+    { }
+
+    virtual ~WeightedRandomIntegerGeneratorTest()
+    { }
+};
+
+// Test of the weighted random number generator constructor
+TEST_F(WeightedRandomIntegerGeneratorTest, Constructor) {
+    vector<double> probabilities;
+
+    // If no probabilities is provided, the smallest integer will always be generated
+    WeightedRandomIntegerGenerator gen(probabilities, 123);
+    for (int i = 0; i < 100; ++i) {
+        ASSERT_EQ(gen(), 123);
+    }
+
+/// Some validation tests will incur performance penalty, so the tests are
+/// made only in "debug" version with assert(). But if NDEBUG is defined
+/// the tests will be failed since assert() is non-op in non-debug version.
+/// The "#ifndef NDEBUG" is added to make the tests be performed only in
+/// non-debug environment.
+#if !defined(NDEBUG)
+    //The probability must be >= 0
+    probabilities.push_back(-0.1);
+    probabilities.push_back(1.1);
+    ASSERT_THROW(WeightedRandomIntegerGenerator gen2(probabilities),
+                 InvalidProbValue);
+
+    //The probability must be <= 1.0
+    probabilities.clear();
+    probabilities.push_back(0.1);
+    probabilities.push_back(1.1);
+    ASSERT_THROW(WeightedRandomIntegerGenerator gen3(probabilities),
+                 InvalidProbValue);
+
+    //The sum must be equal to 1.0
+    probabilities.clear();
+    probabilities.push_back(0.2);
+    probabilities.push_back(0.9);
+    ASSERT_THROW(WeightedRandomIntegerGenerator gen4(probabilities), SumNotOne);
+
+    //The sum must be equal to 1.0
+    probabilities.clear();
+    probabilities.push_back(0.3);
+    probabilities.push_back(0.2);
+    probabilities.push_back(0.1);
+    ASSERT_THROW(WeightedRandomIntegerGenerator gen5(probabilities), SumNotOne);
+#endif
+}
+
+// Test the randomization of the generator
+TEST_F(WeightedRandomIntegerGeneratorTest, WeightedRandomization) {
+    const int repeats = 100000;
+    // We repeat the simulation for N=repeats times
+    // for each probability p, its average is mu = N*p
+    // variance sigma^2 = N * p * (1-p)
+    // sigma = sqrt(N*2/9)
+    // we should make sure that mu - 4sigma < count < mu + 4sigma
+    // which means for 99.99366% of the time this should be true
+    {
+        double p = 0.5;
+        vector<double> probabilities;
+        probabilities.push_back(p);
+        probabilities.push_back(p);
+
+        // Uniformly generated integers
+        WeightedRandomIntegerGenerator gen(probabilities);
+        int c1 = 0;
+        int c2 = 0;
+        for (int i = 0; i < repeats; ++i){
+            int n = gen();
+            if (n == 0) {
+                ++c1;
+            } else if (n == 1) {
+                ++c2;
+            }
+        }
+        double mu = repeats * p;
+        double sigma = sqrt(repeats * p * (1 - p));
+        ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
+        ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
+    }
+
+    {
+        vector<double> probabilities;
+        int c1 = 0;
+        int c2 = 0;
+        double p1 = 0.2;
+        double p2 = 0.8;
+        probabilities.push_back(p1);
+        probabilities.push_back(p2);
+        WeightedRandomIntegerGenerator gen(probabilities);
+        for (int i = 0; i < repeats; ++i) {
+            int n = gen();
+            if (n == 0) {
+                ++c1;
+            } else if (n == 1) {
+                ++c2;
+            }
+        }
+        double mu1 = repeats * p1;
+        double mu2 = repeats * p2;
+        double sigma1 = sqrt(repeats * p1 * (1 - p1));
+        double sigma2 = sqrt(repeats * p2 * (1 - p2));
+        ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+        ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+    }
+
+    {
+        vector<double> probabilities;
+        int c1 = 0;
+        int c2 = 0;
+        double p1 = 0.8;
+        double p2 = 0.2;
+        probabilities.push_back(p1);
+        probabilities.push_back(p2);
+        WeightedRandomIntegerGenerator gen(probabilities);
+        for (int i = 0; i < repeats; ++i) {
+            int n = gen();
+            if (n == 0) {
+                ++c1;
+            } else if (n == 1) {
+                ++c2;
+            }
+        }
+        double mu1 = repeats * p1;
+        double mu2 = repeats * p2;
+        double sigma1 = sqrt(repeats * p1 * (1 - p1));
+        double sigma2 = sqrt(repeats * p2 * (1 - p2));
+        ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+        ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+    }
+
+    {
+        vector<double> probabilities;
+        int c1 = 0;
+        int c2 = 0;
+        int c3 = 0;
+        double p1 = 0.5;
+        double p2 = 0.25;
+        double p3 = 0.25;
+        probabilities.push_back(p1);
+        probabilities.push_back(p2);
+        probabilities.push_back(p3);
+        WeightedRandomIntegerGenerator gen(probabilities);
+        for (int i = 0; i < repeats; ++i){
+            int n = gen();
+            if (n == 0) {
+                ++c1;
+            } else if (n == 1) {
+                ++c2;
+            } else if (n == 2) {
+                ++c3;
+            }
+        }
+        double mu1 = repeats * p1;
+        double mu2 = repeats * p2;
+        double mu3 = repeats * p3;
+        double sigma1 = sqrt(repeats * p1 * (1 - p1));
+        double sigma2 = sqrt(repeats * p2 * (1 - p2));
+        double sigma3 = sqrt(repeats * p3 * (1 - p3));
+        ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+        ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+        ASSERT_TRUE(fabs(c3 - mu3) < 4*sigma3);
+    }
+}
+
+// Test the reset function of generator
+TEST_F(WeightedRandomIntegerGeneratorTest, ResetProbabilities) {
+    const int repeats = 100000;
+    vector<double> probabilities;
+    int c1 = 0;
+    int c2 = 0;
+    double p1 = 0.8;
+    double p2 = 0.2;
+    probabilities.push_back(p1);
+    probabilities.push_back(p2);
+    WeightedRandomIntegerGenerator gen(probabilities);
+    for (int i = 0; i < repeats; ++i) {
+        int n = gen();
+        if (n == 0) {
+            ++c1;
+        } else if (n == 1) {
+            ++c2;
+        }
+    }
+    double mu1 = repeats * p1;
+    double mu2 = repeats * p2;
+    double sigma1 = sqrt(repeats * p1 * (1 - p1));
+    double sigma2 = sqrt(repeats * p2 * (1 - p2));
+    ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+    ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+
+    // Reset the probabilities
+    probabilities.clear();
+    c1 = c2 = 0;
+    p1 = 0.2;
+    p2 = 0.8;
+    probabilities.push_back(p1);
+    probabilities.push_back(p2);
+    gen.reset(probabilities);
+    for (int i = 0; i < repeats; ++i) {
+        int n = gen();
+        if (n == 0) {
+            ++c1;
+        } else if (n == 1) {
+            ++c2;
+        }
+    }
+    mu1 = repeats * p1;
+    mu2 = repeats * p2;
+    sigma1 = sqrt(repeats * p1 * (1 - p1));
+    sigma2 = sqrt(repeats * p2 * (1 - p2));
+    ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+    ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+}
+
+} // namespace random
+} // namespace util
+} // namespace isc

+ 21 - 0
src/lib/util/tests/run_unittests.cc

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

+ 107 - 0
src/lib/util/tests/sha1_unittest.cc

@@ -0,0 +1,107 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <string>
+
+#include <util/compat/sha1.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+
+namespace {
+class Sha1Test : public ::testing::Test {
+protected:
+    Sha1Test() {}
+};
+
+// Tests copied from RFC 3174
+TEST_F(Sha1Test, Test1) {
+    SHA1Context sha;
+    uint8_t digest[SHA1_HASHSIZE];
+    uint8_t expected[SHA1_HASHSIZE] = {
+        0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
+        0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
+    };
+
+    EXPECT_EQ(0, SHA1Reset(&sha));
+    EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "abc", 3));
+    EXPECT_EQ(0, SHA1Result(&sha, digest));
+    for (int i = 0; i < SHA1_HASHSIZE; i++) {
+        EXPECT_EQ(digest[i], expected[i]);
+    }
+}
+
+TEST_F(Sha1Test, Test2) {
+    SHA1Context sha;
+    string test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+    uint8_t digest[SHA1_HASHSIZE];
+    uint8_t expected[SHA1_HASHSIZE] = {
+        0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
+        0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1
+    };
+
+    EXPECT_EQ(0, SHA1Reset(&sha));
+    EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
+                           test.length()));
+    EXPECT_EQ(0, SHA1Result(&sha, digest));
+    for (int i = 0; i < SHA1_HASHSIZE; i++) {
+        EXPECT_EQ(digest[i], expected[i]);
+    }
+}
+
+TEST_F(Sha1Test, Test3) {
+    SHA1Context sha;
+    uint8_t digest[SHA1_HASHSIZE];
+    uint8_t expected[SHA1_HASHSIZE] = {
+        0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
+        0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f
+    };
+
+    EXPECT_EQ(0, SHA1Reset(&sha));
+    for (int i = 0; i < 1000000; i++) {
+        EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "a", 1));
+    }
+    EXPECT_EQ(0, SHA1Result(&sha, digest));
+    for (int i = 0; i < SHA1_HASHSIZE; i++) {
+        EXPECT_EQ(digest[i], expected[i]);
+    }
+}
+
+TEST_F(Sha1Test, Test4) {
+    SHA1Context sha;
+    string test("01234567012345670123456701234567"
+                "01234567012345670123456701234567");
+    uint8_t digest[SHA1_HASHSIZE];
+    uint8_t expected[SHA1_HASHSIZE] = {
+        0xde, 0xa3, 0x56, 0xa2, 0xcd, 0xdd, 0x90, 0xc7, 0xa7, 0xec,
+        0xed, 0xc5, 0xeb, 0xb5, 0x63, 0x93, 0x4f, 0x46, 0x04, 0x52
+    };
+
+    EXPECT_EQ(0, SHA1Reset(&sha));
+    for (int i = 0; i < 10; i++) {
+        EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
+                               test.length()));
+    }
+    EXPECT_EQ(0, SHA1Result(&sha, digest));
+    for (int i = 0; i < SHA1_HASHSIZE; i++) {
+        EXPECT_EQ(digest[i], expected[i]);
+    }
+}
+}
+

+ 214 - 0
src/lib/util/tests/strutil_unittest.cc

@@ -0,0 +1,214 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <util/strutil.h>
+
+using namespace isc;
+using namespace std;
+
+class StringUtilTest : public ::testing::Test {
+protected:
+    StringUtilTest()
+    {
+    }
+};
+
+
+// Check for slash replacement
+
+TEST_F(StringUtilTest, Slash) {
+
+    string instring = "";
+    isc::strutil::normalizeSlash(instring);
+    EXPECT_EQ("", instring);
+
+    instring = "C:\\A\\B\\C.D";
+    isc::strutil::normalizeSlash(instring);
+    EXPECT_EQ("C:/A/B/C.D", instring);
+
+    instring = "// \\ //";
+    isc::strutil::normalizeSlash(instring);
+    EXPECT_EQ("// / //", instring);
+}
+
+// Check that leading and trailing space trimming works
+
+TEST_F(StringUtilTest, Trim) {
+
+    // Empty and full string.
+    EXPECT_EQ("", isc::strutil::trim(""));
+    EXPECT_EQ("abcxyz", isc::strutil::trim("abcxyz"));
+
+    // Trim right-most blanks
+    EXPECT_EQ("ABC", isc::strutil::trim("ABC   "));
+    EXPECT_EQ("ABC", isc::strutil::trim("ABC\t\t  \n\t"));
+
+    // Left-most blank trimming
+    EXPECT_EQ("XYZ", isc::strutil::trim("  XYZ"));
+    EXPECT_EQ("XYZ", isc::strutil::trim("\t\t  \tXYZ"));
+
+    // Right and left, with embedded spaces
+    EXPECT_EQ("MN \t OP", isc::strutil::trim("\t\tMN \t OP \t"));
+}
+
+// Check tokenization.  Note that ASSERT_EQ is used to check the size of the
+// returned vector; if not as expected, the following references may be invalid
+// so should not be used.
+
+TEST_F(StringUtilTest, Tokens) {
+    vector<string>  result;
+
+    // Default delimiters
+
+    // Degenerate cases
+    result = isc::strutil::tokens("");          // Empty string
+    EXPECT_EQ(0, result.size());
+
+    result = isc::strutil::tokens(" \n ");      // String is all delimiters
+    EXPECT_EQ(0, result.size());
+
+    result = isc::strutil::tokens("abc");       // String has no delimiters
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("abc"), result[0]);
+
+    // String containing leading and/or trailing delimiters, no embedded ones.
+    result = isc::strutil::tokens("\txyz");     // One leading delimiter
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("\t \nxyz");  // Multiple leading delimiters
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("xyz\n");     // One trailing delimiter
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("xyz  \t");   // Multiple trailing
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    result = isc::strutil::tokens("\t xyz \n"); // Leading and trailing
+    ASSERT_EQ(1, result.size());
+    EXPECT_EQ(string("xyz"), result[0]);
+
+    // Embedded delimiters
+    result = isc::strutil::tokens("abc\ndef");  // 2 tokens, one separator
+    ASSERT_EQ(2, result.size());
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+
+    result = isc::strutil::tokens("abc\t\t\ndef");  // 2 tokens, 3 separators
+    ASSERT_EQ(2, result.size());
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+
+    result = isc::strutil::tokens("abc\n  \tdef\t\tghi");
+    ASSERT_EQ(3, result.size());                // Multiple tokens, many delims
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+    EXPECT_EQ(string("ghi"), result[2]);
+
+    // Embedded and non-embedded delimiters
+
+    result = isc::strutil::tokens("\t\t  \nabc\n  \tdef\t\tghi   \n\n");
+    ASSERT_EQ(3, result.size());                // Multiple tokens, many delims
+    EXPECT_EQ(string("abc"), result[0]);
+    EXPECT_EQ(string("def"), result[1]);
+    EXPECT_EQ(string("ghi"), result[2]);
+
+    // Non-default delimiter
+    result = isc::strutil::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
+    ASSERT_EQ(6, result.size());
+    EXPECT_EQ(string("alpha"), result[0]);
+    EXPECT_EQ(string("beta"), result[1]);
+    EXPECT_EQ(string(" "), result[2]);
+    EXPECT_EQ(string("gamma"), result[3]);
+    EXPECT_EQ(string("delta"), result[4]);
+    EXPECT_EQ(string("epsilon"), result[5]);
+
+    // Non-default delimiters (plural)
+    result = isc::strutil::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
+        "*+-");
+    ASSERT_EQ(6, result.size());
+    EXPECT_EQ(string("alpha"), result[0]);
+    EXPECT_EQ(string("beta"), result[1]);
+    EXPECT_EQ(string(" "), result[2]);
+    EXPECT_EQ(string("gamma"), result[3]);
+    EXPECT_EQ(string("delta"), result[4]);
+    EXPECT_EQ(string("epsilon"), result[5]);
+}
+
+// Changing case
+
+TEST_F(StringUtilTest, ChangeCase) {
+    string mixed("abcDEFghiJKLmno123[]{=+--+]}");
+    string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
+    string lower("abcdefghijklmno123[]{=+--+]}");
+
+    string test = mixed;
+    isc::strutil::lowercase(test);
+    EXPECT_EQ(lower, test);
+
+    test = mixed;
+    isc::strutil::uppercase(test);
+    EXPECT_EQ(upper, test);
+}
+
+// Formatting
+
+TEST_F(StringUtilTest, Formatting) {
+
+    vector<string> args;
+    args.push_back("arg1");
+    args.push_back("arg2");
+    args.push_back("arg3");
+
+    string format1 = "This is a string with no tokens";
+    EXPECT_EQ(format1, isc::strutil::format(format1, args));
+
+    string format2 = "";    // Empty string
+    EXPECT_EQ(format2, isc::strutil::format(format2, args));
+
+    string format3 = "   ";    // Empty string
+    EXPECT_EQ(format3, isc::strutil::format(format3, args));
+
+    string format4 = "String with %d non-string tokens %lf";
+    EXPECT_EQ(format4, isc::strutil::format(format4, args));
+
+    string format5 = "String with %s correct %s number of tokens %s";
+    string result5 = "String with arg1 correct arg2 number of tokens arg3";
+    EXPECT_EQ(result5, isc::strutil::format(format5, args));
+
+    string format6 = "String with %s too %s few tokens";
+    string result6 = "String with arg1 too arg2 few tokens";
+    EXPECT_EQ(result6, isc::strutil::format(format6, args));
+
+    string format7 = "String with %s too %s many %s tokens %s !";
+    string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
+    EXPECT_EQ(result7, isc::strutil::format(format7, args));
+
+    string format8 = "String with embedded%s%s%stokens";
+    string result8 = "String with embeddedarg1arg2arg3tokens";
+    EXPECT_EQ(result8, isc::strutil::format(format8, args));
+
+    // Handle an empty vector
+    args.clear();
+    string format9 = "%s %s";
+    EXPECT_EQ(format9, isc::strutil::format(format9, args));
+}

+ 163 - 0
src/lib/util/tests/time_utilities_unittest.cc

@@ -0,0 +1,163 @@
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <time.h>
+
+#include <util/compat/time_utilities.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::util::compat;
+
+// See time_utilities.cc
+namespace isc {
+namespace util {
+namespace compat {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+}
+
+namespace {
+
+class DNSSECTimeTest : public ::testing::Test {
+protected:
+    ~DNSSECTimeTest() {
+        detail::gettimeFunction = NULL;
+    }
+};
+
+TEST_F(DNSSECTimeTest, fromText) {
+    // In most cases (in practice) the 32-bit and 64-bit versions should
+    // behave identically, so we'll mainly test the 32-bit version, which
+    // will be more commonly used in actual code (because many of the wire
+    // format time field are 32-bit).  The subtle cases where these two
+    // return different values will be tested at the end of this test case.
+
+    // These are bogus and should be rejected
+    EXPECT_THROW(timeFromText32("2011 101120000"), InvalidTime);
+    EXPECT_THROW(timeFromText32("201101011200-0"), InvalidTime);
+
+    // Short length (or "decimal integer" version of representation;
+    // it's valid per RFC4034, but is not supported in this implementation)
+    EXPECT_THROW(timeFromText32("20100223"), InvalidTime);
+
+    // Leap year checks
+    EXPECT_THROW(timeFromText32("20110229120000"), InvalidTime);
+    EXPECT_THROW(timeFromText32("21000229120000"), InvalidTime);
+    EXPECT_NO_THROW(timeFromText32("20000229120000"));
+    EXPECT_NO_THROW(timeFromText32("20120229120000"));
+
+    // unusual case: this implementation allows SS=60 for "leap seconds"
+    EXPECT_NO_THROW(timeFromText32("20110101120060"));
+
+    // Out of range parameters
+    EXPECT_THROW(timeFromText32("19100223214617"), InvalidTime); // YY<1970
+    EXPECT_THROW(timeFromText32("20110001120000"), InvalidTime); // MM=00
+    EXPECT_THROW(timeFromText32("20111301120000"), InvalidTime); // MM=13
+    EXPECT_THROW(timeFromText32("20110100120000"), InvalidTime); // DD=00
+    EXPECT_THROW(timeFromText32("20110132120000"), InvalidTime); // DD=32
+    EXPECT_THROW(timeFromText32("20110431120000"), InvalidTime); // 'Apr31'
+    EXPECT_THROW(timeFromText32("20110101250000"), InvalidTime); // HH=25
+    EXPECT_THROW(timeFromText32("20110101126000"), InvalidTime); // mm=60
+    EXPECT_THROW(timeFromText32("20110101120061"), InvalidTime); // SS=61
+
+    // Feb 7, 06:28:15 UTC 2106 is the possible maximum time that can be
+    // represented as an unsigned 32bit integer without overflow.
+    EXPECT_EQ(4294967295LU, timeFromText32("21060207062815"));
+
+    // After that, timeFromText32() should start returning the second count
+    // modulo 2^32.
+    EXPECT_EQ(0, timeFromText32("21060207062816"));
+    EXPECT_EQ(10, timeFromText32("21060207062826"));
+
+    // On the other hand, the 64-bit version should return monotonically
+    // increasing counters.
+    EXPECT_EQ(4294967296LL, timeFromText64("21060207062816"));
+    EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
+}
+
+// This helper templated function tells timeToText32 a faked current time.
+// The template parameter is that faked time in the form of int64_t seconds
+// since epoch.
+template <int64_t NOW>
+int64_t
+testGetTime() {
+    return (NOW);
+}
+
+// Seconds since epoch for the year 10K eve.  Commonly used in some tests
+// below.
+const uint64_t YEAR10K_EVE = 253402300799LL;
+
+TEST_F(DNSSECTimeTest, toText) {
+    // Check a basic case with the default (normal) gettimeFunction
+    // based on the "real current time".
+    // Note: this will fail after year 2078, but at that point we won't use
+    // this program anyway:-)
+    EXPECT_EQ("20100311233000", timeToText32(1268350200));
+
+    // Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
+    // in the range of the first half of uint32 since epoch).
+    detail::gettimeFunction = testGetTime<1329555854LL>;
+
+    // Test the "year 2038" problem.
+    // Check the result of toText() for "INT_MIN" in int32_t.  It's in the
+    // 68-year range from the faked current time, so the result should be
+    // in year 2038, instead of 1901.
+    EXPECT_EQ("20380119031408", timeToText64(0x80000000L));
+    EXPECT_EQ("20380119031408", timeToText32(0x80000000L));
+
+    // A controversial case: what should we do with "-1"?  It's out of range
+    // in future, but according to RFC time before epoch doesn't seem to be
+    // considered "in-range" either.  Our toText() implementation handles
+    // this range as a special case and always treats them as future time
+    // until year 2038.  This won't be a real issue in practice, though,
+    // since such too large values won't be used in actual deployment by then.
+    EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+    // After the singular point of year 2038, the first half of uint32 can
+    // point to a future time.
+    // Set the current time to: Apr 1 00:00:00 UTC 2038:
+    detail::gettimeFunction = testGetTime<2153692800LL>;
+    // then time "10" is Feb 7 06:28:26 UTC 2106
+    EXPECT_EQ("21060207062826", timeToText32(10));
+    // in 64-bit, it's 2^32 + 10
+    EXPECT_EQ("21060207062826", timeToText64(0x10000000aLL));
+
+    // After year 2106, the upper half of uint32 can point to past time
+    // (as it should).
+    detail::gettimeFunction = testGetTime<0x10000000aLL>;
+    EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+    // Try very large time value.  Actually it's the possible farthest time
+    // that can be represented in the form of YYYYMMDDHHmmSS.
+    EXPECT_EQ("99991231235959", timeToText64(YEAR10K_EVE));
+    detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+    EXPECT_EQ("99991231235959", timeToText32(4294197631LU));
+}
+
+TEST_F(DNSSECTimeTest, overflow) {
+    // Jan 1, Year 10,000.
+    EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
+    detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+    EXPECT_THROW(timeToText32(4294197632LU), InvalidTime);
+}
+
+}