Parcourir la source

[master] Merge branch 'trac2097'

JINMEI Tatuya il y a 12 ans
Parent
commit
732703856c

+ 1 - 0
src/lib/datasrc/memory/Makefile.am

@@ -11,6 +11,7 @@ CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
 libdatasrc_memory_la_SOURCES = \
+	rdataset.h rdataset.cc \
 	rdata_serialization.h rdata_serialization.cc \
 	domaintree.h
 libdatasrc_memory_la_SOURCES += zone_data.h

+ 1 - 1
src/lib/datasrc/memory/domaintree.h

@@ -926,7 +926,7 @@ private:
     /// \brief The destructor.
     ///
     /// An object of this class is always expected to be destroyed explicitly
-    /// by \c destroy(), so the constructor is hidden as private.
+    /// by \c destroy(), so the destructor is hidden as private.
     ///
     /// \note DomainTree is not intended to be inherited so the destructor
     /// is not virtual

+ 2 - 6
src/lib/datasrc/memory/rdata_serialization.cc

@@ -547,20 +547,16 @@ RdataReader::next() {
     return (nextInternal(name_action_, data_action_));
 }
 
-namespace {
-
 void
-emptyNameAction(const LabelSequence&, unsigned) {
+RdataReader::emptyNameAction(const LabelSequence&, RdataNameAttributes) {
     // Do nothing here.
 }
 
 void
-emptyDataAction(const void*, size_t) {
+RdataReader::emptyDataAction(const void*, size_t) {
     // Do nothing here.
 }
 
-}
-
 RdataReader::Boundary
 RdataReader::nextSig() {
     if (sig_pos_ < sig_count_) {

+ 16 - 2
src/lib/datasrc/memory/rdata_serialization.h

@@ -97,8 +97,7 @@ namespace memory {
 /// \brief General error in RDATA encoding.
 ///
 /// This is thrown when \c RdataEncoder encounters a rare, unsupported
-/// situation. a method is called for a name or RRset which
-/// is not in or below the zone.
+/// situation.
 class RdataEncodingError : public Exception {
 public:
     RdataEncodingError(const char* file, size_t line, const char* what) :
@@ -359,6 +358,21 @@ public:
     /// \brief Function called on each data field in the data.
     typedef boost::function<void(const void*, size_t)> DataAction;
 
+    /// \brief An NameAction that does intentionally nothing.
+    ///
+    /// This static method can be used as the name action parameter to
+    /// construct \c RdataReader when the caller does not have to anything
+    /// for name fields.
+    static void emptyNameAction(const dns::LabelSequence&,
+                                RdataNameAttributes);
+
+    /// \brief An DataAction that does intentionally nothing.
+    ///
+    /// This static method can be used as the data action parameter to
+    /// construct \c RdataReader when the caller does not have to anything
+    /// for opaque data fields.
+    static void emptyDataAction(const void*, size_t);
+
     /// \brief Constructor
     ///
     /// This constructs the reader on top of some serialized data.

+ 178 - 0
src/lib/datasrc/memory/rdataset.cc

@@ -0,0 +1,178 @@
+// Copyright (C) 2012  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 <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+
+#include "rdataset.h"
+#include "rdata_serialization.h"
+
+#include <boost/static_assert.hpp>
+
+#include <stdint.h>
+#include <cstring>
+#include <typeinfo>             // for bad_cast
+#include <new>                  // for the placement new
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+namespace {
+RRType
+getCoveredType(const Rdata& rdata) {
+    try {
+        const generic::RRSIG& rrsig_rdata =
+            dynamic_cast<const generic::RRSIG&>(rdata);
+        return (rrsig_rdata.typeCovered());
+    } catch (const std::bad_cast&) {
+        isc_throw(BadValue, "Non RRSIG is given where it's expected");
+    }
+}
+}
+
+RdataSet*
+RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
+                 ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset)
+{
+    // Check basic validity
+    if (!rrset && !sig_rrset) {
+        isc_throw(BadValue, "Both RRset and RRSIG are NULL");
+    }
+    if (rrset && rrset->getRdataCount() == 0) {
+        isc_throw(BadValue, "Empty RRset");
+    }
+    if (sig_rrset && sig_rrset->getRdataCount() == 0) {
+        isc_throw(BadValue, "Empty SIG RRset");
+    }
+    if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
+        isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
+    }
+
+    // Check assumptions on the number of RDATAs
+    if (rrset && rrset->getRdataCount() > MAX_RDATA_COUNT) {
+        isc_throw(RdataSetError, "Too many RDATAs for RdataSet: "
+                  << rrset->getRdataCount() << ", must be <= "
+                  << MAX_RDATA_COUNT);
+    }
+    if (sig_rrset && sig_rrset->getRdataCount() > MAX_RRSIG_COUNT) {
+        isc_throw(RdataSetError, "Too many RRSIGs for RdataSet: "
+                  << sig_rrset->getRdataCount() << ", must be <= "
+                  << MAX_RRSIG_COUNT);
+    }
+
+    const RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
+    const RRType rrtype = rrset ? rrset->getType() :
+        getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
+    const RRTTL rrttl = rrset ? rrset->getTTL() : sig_rrset->getTTL();
+
+    encoder.start(rrclass, rrtype);
+    if (rrset) {
+        for (RdataIteratorPtr it = rrset->getRdataIterator();
+             !it->isLast();
+             it->next()) {
+            encoder.addRdata(it->getCurrent());
+        }
+    }
+    if (sig_rrset) {
+        for (RdataIteratorPtr it = sig_rrset->getRdataIterator();
+             !it->isLast();
+             it->next())
+        {
+            if (getCoveredType(it->getCurrent()) != rrtype) {
+                isc_throw(BadValue, "Type covered doesn't match");
+            }
+            encoder.addSIGRdata(it->getCurrent());
+        }
+    }
+
+    const size_t rrsig_count = sig_rrset ? sig_rrset->getRdataCount() : 0;
+    const size_t ext_rrsig_count_len =
+        rrsig_count >= MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    const size_t data_len = encoder.getStorageLength();
+    void* p = mem_sgmt.allocate(sizeof(RdataSet) + ext_rrsig_count_len +
+                                data_len);
+    RdataSet* rdataset = new(p) RdataSet(rrtype,
+                                         rrset ? rrset->getRdataCount() : 0,
+                                         rrsig_count, rrttl);
+    if (rrsig_count >= MANY_RRSIG_COUNT) {
+        *rdataset->getExtSIGCountBuf() = rrsig_count;
+    }
+    encoder.encode(rdataset->getDataBuf(), data_len);
+    return (rdataset);
+}
+
+void
+RdataSet::destroy(util::MemorySegment& mem_sgmt, RRClass rrclass,
+                  RdataSet* rdataset)
+{
+    const size_t data_len =
+        RdataReader(rrclass, rdataset->type,
+                    reinterpret_cast<const uint8_t*>(rdataset->getDataBuf()),
+                    rdataset->getRdataCount(), rdataset->getSigRdataCount(),
+                    &RdataReader::emptyNameAction,
+                    &RdataReader::emptyDataAction).getSize();
+    const size_t ext_rrsig_count_len =
+        rdataset->sig_rdata_count_ == MANY_RRSIG_COUNT ? sizeof(uint16_t) : 0;
+    rdataset->~RdataSet();
+    mem_sgmt.deallocate(rdataset,
+                        sizeof(RdataSet) + ext_rrsig_count_len + data_len);
+}
+
+namespace {
+// Convert the given RRTTL into the corresponding 32-bit unsigned integer,
+// in the network byte order.  We do not use htonl() to be as portable as
+// possible.
+uint32_t
+convertTTL(RRTTL ttl) {
+    const uint32_t ttl_val = ttl.getValue();
+    uint8_t buf[4];
+    buf[0] = (ttl_val & 0xff000000) >> 24;
+    buf[1] = (ttl_val & 0x00ff0000) >> 16;
+    buf[2] = (ttl_val & 0x0000ff00) >> 8;
+    buf[3] = (ttl_val & 0x000000ff);
+    uint32_t ret;
+    std::memcpy(&ret, buf, sizeof(ret));
+    return (ret);
+}
+}
+
+RdataSet::RdataSet(RRType type_param, size_t rdata_count,
+                   size_t sig_rdata_count, RRTTL ttl) :
+    type(type_param),
+    sig_rdata_count_(sig_rdata_count >= MANY_RRSIG_COUNT ?
+                     MANY_RRSIG_COUNT : sig_rdata_count),
+    rdata_count_(rdata_count), ttl_(convertTTL(ttl))
+{
+    // Make sure an RRType object is essentially a plain 16-bit value, so
+    // our assumption of the size of RdataSet holds.  If it's not the case
+    // we should use the bare value instead of the class object.
+    BOOST_STATIC_ASSERT(sizeof(type) == sizeof(uint16_t));
+
+    // Confirm we meet the alignment requirement for RdataEncoder
+    // ("this + 1" should be safely passed to the encoder).
+    BOOST_STATIC_ASSERT(sizeof(RdataSet) % sizeof(uint16_t) == 0);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // datasrc isc

+ 323 - 0
src/lib/datasrc/memory/rdataset.h

@@ -0,0 +1,323 @@
+// Copyright (C) 2012  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 DATASRC_MEMORY_RDATASET_H
+#define DATASRC_MEMORY_RDATASET_H 1
+
+#include <util/memory_segment.h>
+
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+class RdataEncoder;
+
+/// \brief General error on creating RdataSet.
+///
+/// This is thrown when creating \c RdataSet encounters a rare, unsupported
+/// situation.
+class RdataSetError : public Exception {
+public:
+    RdataSetError(const char* file, size_t line, const char* what) :
+        Exception(file, line, what) {}
+};
+
+/// \brief Memory-efficient representation of RRset data with RRSIGs.
+///
+/// This class provides memory-efficient and lightweight interface to various
+/// attributes of an RRset, which may or may not be signed with RRSIGs.
+///
+/// This class is primarily intended to be used in the in-memory data source
+/// implementation, and is not supposed to be used by general applications.
+/// The major design goals is to keep required memory footprint for the given
+/// amount of data as small as possible, while providing a reasonably
+/// efficient interface to examine the data, focusing on zone lookup and DNS
+/// message rendering.
+///
+/// It encodes a specific set of RRset and (when signed) its RRSIGs, excluding
+/// the owner name and the RR class.  The owner name is supposed to be
+/// maintained by the application outside this class (the intended place to
+/// store this information is a \c DomainTree node, although this
+/// implementation does not rely on that intent).  The RR class must be the
+/// same in a single zone, and it's simply a waste if we have it with each
+/// RRset.  The RR class information is therefore expected to be maintained
+/// outside this class.
+///
+/// This class imposes some limitations on the number of RDATAs of the RRset
+/// and RRSIG: a (non RRSIG) RRset containing more than 8191 (2^13 - 1)
+/// or an RRSIG containing more than 65535 (2^16 - 1) RDATAs cannot be
+/// maintained in this class.  The former restriction comes from the
+/// following observation: any RR in wire format in a DNS message must at
+/// least contain 10 bytes of data (for RR type, class, TTL and RDATA length),
+/// and since a valid DNS message must not be larger than 65535 bytes,
+/// no valid DNS response can contain more than 6554 RRs.  So, in practice,
+/// it should be reasonable even if we reject very large RRsets that would
+/// not fit in a DNS message.  For the same reason we restrict the number of
+/// RRSIGs, although due to internal implementation details the limitation
+/// is more relaxed for RRSIGs.
+///
+/// \note (This is pure implementation details) By limiting the number of
+/// RDATAs so it will fit in a 13-bit integer, we can use 3 more bits in a
+/// 2-byte integer for other purposes.  We use this additional field to
+/// represent the number of RRSIGs up to 6, while using the value of 7 to mean
+/// there are more than 6 RRSIGs.  In the vast majority of real world
+/// deployment, an RRset should normally have only a few RRSIGs, and 6 should
+/// normally be more than sufficient.  So we can cover most practical cases
+/// regarding the number of records with this 2-byte field.
+///
+/// A set of objects of this class (which would be \c RdataSets of various
+/// types of the same owner name) will often be maintained in a single linked
+/// list.  The class has a member variable to make the link.
+///
+/// This class is designed so an instance can be stored in a shared
+/// memory region.  So it only contains straightforward data (e.g., it
+/// doesn't hold a pointer to an object of some base class that
+/// contains virtual methods), and the pointer member (see the
+/// previous paragraph) is represented as an offset pointer.  For the
+/// same reason this class should never have virtual methods (and as a
+/// result, should never be inherited in practice).  When this class
+/// is extended these properties must be preserved.
+///
+/// The \c RdataSet class itself only contains a subset of attributes that
+/// it is conceptually expected to contain.  The rest of the attributes
+/// are encoded in a consecutive memory region immediately following the main
+/// \c RdataSet object.  The memory layout would be as follows:
+/// \verbatim
+/// RdataSet object
+/// (optional) uint16_t: number of RRSIGs, if it's larger than 6 (see above)
+/// encoded RDATA (generated by RdataEncoder) \endverbatim
+///
+/// This is shown here only for reference purposes.  The application must not
+/// assume any particular format of data in this region directly; it must
+/// get access to it via public interfaces provided in the main \c RdataSet
+/// class.
+class RdataSet : boost::noncopyable {
+public:
+    /// \brief Allocate and construct \c RdataSet
+    ///
+    /// This static method allocates memory for a new \c RdataSet
+    /// object for the set of an RRset and (if it's supposed to be signed)
+    /// RRSIG from the given memory segment, constructs the object, and
+    /// returns a pointer to it.
+    ///
+    /// Normally the (non RRSIG) RRset is given (\c rrset is not NULL) while
+    /// its RRSIG (\c sig_rrset) may or may not be provided.  But it's also
+    /// expected that in some rare (mostly broken) cases there can be an RRSIG
+    /// RR in a zone without having the covered RRset in the zone.  To handle
+    /// such cases, this class allows to only hold RRSIG, in which case
+    /// \c rrset can be NULL.  At least \c rrset or \c sig_rrset must be
+    /// non NULL, however.  Also, if non NULL, the RRset must not be empty,
+    /// that is, it must contain at least one RDATA.
+    ///
+    /// The RR type of \c rrset must not be RRSIG; the RR type of \c sig_rrset
+    /// must be RRSIG.
+    ///
+    /// When both \c rrset and \c sig_rrset are provided (both are non
+    /// NULL), the latter must validly cover the former: the RR class
+    /// must be identical; the type covered field of any RDATA of \c
+    /// sig_rrset must be identical to the RR type of \c rrset.  The owner
+    /// name of these RRsets must also be identical, but this implementation
+    /// doesn't require it because \c RdataSet itself does not rely on the
+    /// owner name, and it should be pretty likely that this condition is met
+    /// in the context where this class is used (and, name comparison is
+    /// relatively expensive, and if we end up comparing them twice the
+    /// overhead can be non negligible).
+    ///
+    /// If any of the above conditions isn't met, an isc::BadValue exception
+    /// will be thrown; basically, there should be a bug in the caller if this
+    /// happens.
+    ///
+    /// Due to implementation limitations, this class cannot contain more than
+    /// 8191 RDATAs for the non RRISG RRset; also, it cannot contain more than
+    /// 65535 RRSIGs.  If the given RRset(s) fail to meet this condition,
+    /// an \c RdataSetError exception will be thrown.
+    ///
+    /// \throw isc::BadValue Given RRset(s) are invalid (see the description)
+    /// \throw RdataSetError Number of RDATAs exceed the limits
+    /// \throw std::bad_alloc Memory allocation fails.
+    ///
+    /// \param mem_sgmt A \c MemorySegment from which memory for the new
+    /// \c RdataSet is allocated.
+    /// \param encoder The RDATA encoder to encode \c rrset and \c sig_rrset
+    /// with the \c RdataSet to be created.
+    /// \param rrset A (non RRSIG) RRset from which the \c RdataSet is to be
+    /// created.  Can be NULL if sig_rrset is not.
+    /// \param sig_rrset An RRSIG RRset from which the \c RdataSet is to be
+    /// created.  Can be NULL if rrset is not.
+    ///
+    /// \return A pointer to the created \c RdataSet.
+    static RdataSet* create(util::MemorySegment& mem_sgmt,
+                            RdataEncoder& encoder,
+                            dns::ConstRRsetPtr rrset,
+                            dns::ConstRRsetPtr sig_rrset);
+
+    /// \brief Destruct and deallocate \c RdataSet
+    ///
+    /// Note that this method needs to know the expected RR class of the
+    /// \c RdataSet.  This is because the destruction may depend on the
+    /// internal data encoding that only \c RdataEncoder and \c RdataReader
+    /// know, and they need to know the corresponding RR class and type to
+    /// identify the internal data representation.  Since \c RdataSet itself
+    /// does not hold the class information, the caller needs to provide it.
+    /// Obviously, this RR class must be identical to the RR class of \c rrset
+    /// (when given) or of \c sig_rrset (when \c rrset isn't given) at the
+    /// \c create() time.
+    ///
+    /// \throw none
+    ///
+    /// \param mem_sgmt The \c MemorySegment that allocated memory for
+    /// \c node.
+    /// \param rrclass The RR class of the \c RdataSet to be destroyed.
+    /// \param rdataset A non NULL pointer to a valid \c RdataSet object
+    /// that was originally created by the \c create() method (the behavior
+    /// is undefined if this condition isn't met).
+    static void destroy(util::MemorySegment& mem_sgmt, dns::RRClass rrclass,
+                        RdataSet* rdataset);
+
+    typedef boost::interprocess::offset_ptr<RdataSet> RdataSetPtr;
+    typedef boost::interprocess::offset_ptr<const RdataSet> ConstRdataSetPtr;
+
+    // Note: the size and order of the members are carefully chosen to
+    // maximize efficiency.  Don't change them unless there's strong reason
+    // for that and the consequences are considered.
+    // For convenience (and since this class is mostly intended to be an
+    // internal definition for the in-memory data source implementation),
+    // we allow the application to get access to some members directly.
+    // Some others require some conversion to use in a meaningful way,
+    // for which we force the application to use accessor methods in order
+    // to prevent misuse.
+
+    RdataSetPtr next; ///< Pointer to the next \c RdataSet (when linked)
+    const dns::RRType type;     ///< The RR type of the \c RdataSet
+
+private:
+    const uint16_t sig_rdata_count_ : 3; // # of RRSIGs, up to 6 (7 means many)
+    const uint16_t rdata_count_ : 13; // # of RDATAs, up to 8191
+    const uint32_t ttl_;       // TTL of the RdataSet, net byte order
+
+    // Max number of normal RDATAs that can be stored in \c RdataSet.
+    // It's 2^13 - 1 = 8191.
+    static const size_t MAX_RDATA_COUNT = (1 << 13) - 1;
+
+    // Max number of RRSIGs that can be stored in \c RdataSet.
+    // It's 2^16 - 1 = 65535.
+    static const size_t MAX_RRSIG_COUNT = (1 << 16) - 1;
+
+    // Indicate the \c RdataSet contains many RRSIGs that require an additional
+    // field for the real number of RRSIGs.  It's 2^3 - 1 = 7.
+    static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
+
+public:
+    /// \brief Return the number of RDATAs stored in the \c RdataSet.
+    size_t getRdataCount() const { return (rdata_count_); }
+
+    /// \brief Return the number of RRSIG RDATAs stored in the \c RdataSet.
+    size_t getSigRdataCount() const {
+        if (sig_rdata_count_ < MANY_RRSIG_COUNT) {
+            return (sig_rdata_count_);
+        } else {
+            return (*getExtSIGCountBuf());
+        }
+    }
+
+    /// \brief Return a pointer to the TTL data of the \c RdataSet.
+    ///
+    /// The returned pointer points to a memory region that is valid at least
+    /// for 32 bits, storing the TTL of the \c RdataSet in the network byte
+    /// order.  It returns opaque data to make it clear that unless the wire
+    /// format data is necessary (e.g., when rendering it in a DNS message),
+    /// it should be converted to, e.g., an \c RRTTL object explicitly.
+    ///
+    /// \throw none
+    const void* getTTLData() const { return (&ttl_); }
+
+    /// \brief Accessor to the memory region for encoded RDATAs.
+    ///
+    /// The only valid usage of the returned pointer is to pass it to
+    /// the constructor of \c RdataReader.
+    ///
+    /// \throw none
+    const void* getDataBuf() const {
+        return (getDataBuf<const void, const RdataSet>(this));
+    }
+
+private:
+    /// \brief Accessor to the memory region for encoded RDATAs, mutable
+    /// version.
+    ///
+    /// This version is only used within the class implementation, so it's
+    /// defined as private.
+    void* getDataBuf() {
+        return (getDataBuf<void, RdataSet>(this));
+    }
+
+    // Implementation of getDataBuf().  Templated to unify the mutable and
+    // immutable versions.
+    template <typename RetType, typename ThisType>
+    static RetType* getDataBuf(ThisType* rdataset) {
+        if (rdataset->sig_rdata_count_ < MANY_RRSIG_COUNT) {
+            return (rdataset + 1);
+        } else {
+            return (rdataset->getExtSIGCountBuf() + 1);
+        }
+    }
+
+    /// \brief Accessor to the memory region for the RRSIG count field for
+    /// a large number of RRSIGs.
+    ///
+    /// These are used only internally and defined as private.
+    const uint16_t* getExtSIGCountBuf() const {
+        return (reinterpret_cast<const uint16_t*>(this + 1));
+    }
+    uint16_t* getExtSIGCountBuf() {
+        return (reinterpret_cast<uint16_t*>(this + 1));
+    }
+
+    /// \brief The constructor.
+    ///
+    /// An object of this class is always expected to be created by the
+    /// allocator (\c create()), so the constructor is hidden as private.
+    ///
+    /// It never throws an exception.
+    RdataSet(dns::RRType type, size_t rdata_count, size_t sig_rdata_count,
+             dns::RRTTL ttl);
+
+    /// \brief The destructor.
+    ///
+    /// An object of this class is always expected to be destroyed explicitly
+    /// by \c destroy(), so the destructor is hidden as private.
+    ///
+    /// This currently does nothing, but is explicitly defined to clarify
+    /// it's intentionally defined as private.
+    ~RdataSet() {}
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_RDATASET_H
+
+// Local Variables:
+// mode: c++
+// End:

+ 3 - 0
src/lib/datasrc/memory/tests/Makefile.am

@@ -19,6 +19,7 @@ TESTS += run_unittests
 
 run_unittests_SOURCES = run_unittests.cc
 run_unittests_SOURCES += rdata_serialization_unittest.cc
+run_unittests_SOURCES += rdataset_unittest.cc
 run_unittests_SOURCES += domaintree_unittest.cc
 run_unittests_SOURCES += zone_table_unittest.cc
 
@@ -28,6 +29,8 @@ run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
 run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif

+ 279 - 0
src/lib/datasrc/memory/tests/rdataset_unittest.cc

@@ -0,0 +1,279 @@
+// Copyright (C) 2012  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/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using boost::lexical_cast;
+
+namespace {
+
+class RdataSetTest : public ::testing::Test {
+protected:
+    RdataSetTest() :
+        // 1076895760 = 0x40302010.  Use this so we fill in all 8-bit "field"
+        // of the 32-bit TTL
+        a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
+        rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
+                                 "A 5 2 3600 20120814220826 20120715220826 "
+                                 "1234 example.com. FAKE"))
+    {}
+    void TearDown() {
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    ConstRRsetPtr a_rrset_, rrsig_rrset_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+    RdataEncoder encoder_;
+};
+
+// Convert the given 32-bit integer (network byte order) to the corresponding
+// RRTTL object.
+RRTTL
+restoreTTL(const void* ttl_data) {
+    isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
+    return (RRTTL(b));
+}
+
+// A helper callback for checkRdataSet.  This confirms the given data
+// is the expected in::A RDATA (the value is taken from the RdataSetTest
+// constructor).
+void
+checkData(const void* data, size_t size) {
+    isc::util::InputBuffer b(data, size);
+    EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
+}
+
+// This is a set of checks for an RdataSet created with some simple
+// conditions.  with_rrset/with_rrsig is true iff the RdataSet is supposed to
+// contain normal/RRSIG RDATA.
+void
+checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
+    EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
+    EXPECT_EQ(RRType::A(), rdataset.type);
+    // See the RdataSetTest constructor for the magic number.
+    EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
+    EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
+    EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
+
+    // A simple test for the data content.  Details tests for the encoder/
+    // reader should be basically sufficient for various cases of the data,
+    // and the fact that this test doesn't detect memory leak should be
+    // reasonably sufficient that the implementation handles the data region
+    // correctly.  Here we check one simple case for a simple form of RDATA,
+    // mainly for checking the behavior of getDataBuf().
+    RdataReader reader(RRClass::IN(), RRType::A(),
+                       reinterpret_cast<const uint8_t*>(
+                           rdataset.getDataBuf()),
+                       rdataset.getRdataCount(), rdataset.getSigRdataCount(),
+                       &RdataReader::emptyNameAction, checkData);
+    reader.iterate();
+}
+
+TEST_F(RdataSetTest, create) {
+    // A simple case of creating an RdataSet.  Confirming the resulting
+    // fields have the expected values, and then destroying it (TearDown()
+    // would detect any memory leak)
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+    checkRdataSet(*rdataset, true, false);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRsetWithRdataCount(size_t rdata_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
+                             RRTTL(3600)));
+    for (size_t i = 0; i < rdata_count; ++i) {
+        rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
+                                           lexical_cast<std::string>(i)));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRs) {
+    // RRset with possible maximum number of RDATAs
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
+                                          getRRsetWithRdataCount(8191),
+                                          ConstRRsetPtr());
+    EXPECT_EQ(8191, rdataset->getRdataCount());
+    EXPECT_EQ(0, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding that will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(8192),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(65535),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIG) {
+    // Normal case.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Unusual case: TTL doesn't match.  This implementation accepts that,
+    // using the TTL of the covered RRset.
+    ConstRRsetPtr rrsig_badttl(textToRRset(
+                                   "www.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 "
+                                   "20120715220826 1234 example.com. FAKE"));
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRSIG RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRSIGWithRdataCount(size_t sig_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
+                             RRType::RRSIG(), RRTTL(3600)));
+    // We use a base wire-format image and tweak the original TTL field to
+    // generate unique RDATAs in the loop.  (Creating them from corresponding
+    // text is simpler, but doing so for a large number of RRSIGs is
+    // relatively heavy and could be too long for unittests).
+    ConstRdataPtr rrsig_base =
+        rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                           "A 5 2 3600 20120814220826 20120715220826 1234 "
+                           "example.com. FAKE");
+    isc::util::OutputBuffer ob(0);
+    rrsig_base->toWire(ob);
+    for (size_t i = 0; i < sig_count; ++i) {
+        ob.writeUint16At((i >> 16) & 0xffff, 4);
+        ob.writeUint16At(i & 0xffff, 6);
+        isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+        rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                                           ib, ib.getLength()));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRSIGs) {
+    // 7 has a special meaning in the implementation: if the number of the
+    // RRSIGs reaches this value, an extra 'sig count' field will be created.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          getRRSIGWithRdataCount(7));
+    EXPECT_EQ(7, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // 8 would cause overflow in the normal 3-bit field if there were no extra
+    // count field.
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(8));
+    EXPECT_EQ(8, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Up to 2^16-1 RRSIGs are allowed (although that would be useless
+    // in practice)
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(65535));
+    EXPECT_EQ(65535, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding this limit will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(65536)),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(70000)),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIGOnly) {
+    // A rare, but allowed, case: RdataSet without the main RRset but with
+    // RRSIG.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, false, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, badCeate) {
+    // Neither the RRset nor RRSIG RRset is given
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  ConstRRsetPtr()), isc::BadValue);
+
+    // Empty RRset (An RRset without RDATA)
+    ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::A(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
+                                  ConstRRsetPtr()), isc::BadValue);
+    ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::RRSIG(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  empty_rrsig), isc::BadValue);
+
+    // The RRset type and RRSIG's type covered don't match
+    ConstRRsetPtr bad_rrsig(textToRRset(
+                                "www.example.com. 1076895760 IN RRSIG "
+                                "NS 5 2 3600 20120814220826 20120715220826 "
+                                "1234 example.com. FAKE"));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
+                 isc::BadValue);
+
+    // Pass non RRSIG for the sig parameter
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
+                 isc::BadValue);
+
+    // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
+                                  rrsig_rrset_),
+                 isc::BadValue);
+
+    // RR class doesn't match between RRset and RRSIG
+    ConstRRsetPtr badclass_rrsig(textToRRset(
+                                     "www.example.com. 1076895760 CH RRSIG "
+                                     "A 5 2 3600 20120814220826 "
+                                     "20120715220826 1234 example.com. FAKE",
+                                     RRClass::CH()));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  badclass_rrsig),
+                 isc::BadValue);
+}
+}