BIND 10 master, updated. 732703856c60f3934403e666df86fb105b3f69ce [master] Merge branch 'trac2097'
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Aug 28 20:27:35 UTC 2012
The branch, master has been updated
via 732703856c60f3934403e666df86fb105b3f69ce (commit)
via aa708cee10b9ca277966df0d490606ae56a00fff (commit)
via 23492e5eb5cba0ad07253db37f69b3e3e32c4299 (commit)
via 9c03bc82344de6539ba48a197add60a0c9bdcff4 (commit)
via 4e3a85f9ba40aa0934e84eb54bf518733e56a504 (commit)
via a851fd51275ea398dd819fbaca703524f417c281 (commit)
via b59e0b908ecbf12daaee9b66d3cd77b7a8554b04 (commit)
via 7c0e1396fab1be6b6021d299c79c275335488944 (commit)
via f4833d5074447b1fc5aaca8d705fd36f7a49244e (commit)
via 2fd528da04c3a154ca0b787c7f1b537d4bd869db (commit)
via 0596cd9d23b29e2ba6096838044de6994d6b06ff (commit)
via 879a73596bebf9a46b6667a0cdb8c7493c50eebb (commit)
via e6152707a2bb03f93d4c72e4122eb0020b8949ae (commit)
via c097d3c770c379cd3bccf310a251dd517e040a43 (commit)
via 6ddaa4996de0032c41b2e32fbe351ccb3b0f20b6 (commit)
via ba9f6851070614e9b5e23f43267305a3fe455d02 (commit)
via e8849df60c9347f76b33903ca115a1f77f5e0282 (commit)
via a7df756bee32ebd4be5a010659b68c71df152af7 (commit)
via e3fa6f452eed5632904fc77e2e33da560e721c25 (commit)
via 6753f9e88f5515d8896ad1479a6eb3634b32882f (commit)
via c9b0c40018c2007309b68af9cc78b474a28258ef (commit)
via 0019f64f94e6c449b8d702f65b3fd0453aa56633 (commit)
from 6e500e577f4fd8d6be953c15561f20c5ef1a784e (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit 732703856c60f3934403e666df86fb105b3f69ce
Merge: 6e500e5 aa708ce
Author: JINMEI Tatuya <jinmei at isc.org>
Date: Tue Aug 28 13:20:31 2012 -0700
[master] Merge branch 'trac2097'
-----------------------------------------------------------------------
Summary of changes:
src/lib/datasrc/memory/Makefile.am | 1 +
src/lib/datasrc/memory/domaintree.h | 2 +-
src/lib/datasrc/memory/rdata_serialization.cc | 8 +-
src/lib/datasrc/memory/rdata_serialization.h | 18 +-
src/lib/datasrc/memory/rdataset.cc | 178 ++++++++++++
src/lib/datasrc/memory/rdataset.h | 323 +++++++++++++++++++++
src/lib/datasrc/memory/tests/Makefile.am | 3 +
src/lib/datasrc/memory/tests/rdataset_unittest.cc | 279 ++++++++++++++++++
8 files changed, 803 insertions(+), 9 deletions(-)
create mode 100644 src/lib/datasrc/memory/rdataset.cc
create mode 100644 src/lib/datasrc/memory/rdataset.h
create mode 100644 src/lib/datasrc/memory/tests/rdataset_unittest.cc
-----------------------------------------------------------------------
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index e0f49cc..7cdc736 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/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
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index 2c4bd59..202c1a9 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/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
diff --git a/src/lib/datasrc/memory/rdata_serialization.cc b/src/lib/datasrc/memory/rdata_serialization.cc
index 9c020b2..841bc13 100644
--- a/src/lib/datasrc/memory/rdata_serialization.cc
+++ b/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_) {
diff --git a/src/lib/datasrc/memory/rdata_serialization.h b/src/lib/datasrc/memory/rdata_serialization.h
index a039e60..183276f 100644
--- a/src/lib/datasrc/memory/rdata_serialization.h
+++ b/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.
diff --git a/src/lib/datasrc/memory/rdataset.cc b/src/lib/datasrc/memory/rdataset.cc
new file mode 100644
index 0000000..aae64f3
--- /dev/null
+++ b/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
diff --git a/src/lib/datasrc/memory/rdataset.h b/src/lib/datasrc/memory/rdataset.h
new file mode 100644
index 0000000..70cf733
--- /dev/null
+++ b/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:
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 6195cc7..128b8e7 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/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
diff --git a/src/lib/datasrc/memory/tests/rdataset_unittest.cc b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
new file mode 100644
index 0000000..1a83089
--- /dev/null
+++ b/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);
+}
+}
More information about the bind10-changes
mailing list