Commit 23230706 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2440'

fixed conflicts:
	doc/differences.txt
parents f99b625b aa9f341b
......@@ -6,6 +6,7 @@ BIND 9
TODO: There are definitely more differences than just this.
DNS zone transfer:
* When an incoming zone transfer fails, for example because the
received zone doesn't contain a NS record, bind 9 stops serving the
zone and returns SERVFAIL to queries for that zone. Bind 10 still
......@@ -18,3 +19,24 @@ RDATA implementations:
should actually be NOT accepted per RFC 1035, but BIND 9 accepts them
probably because of compatibility reasons. Until our strict
(and more correct) behavior causes operations issues, we'll keep it.
DNS data sources:
* In-memory data source does not sort RDATA of each RRset (in the
DNSSEC order) while BIND 9 normally sorts them internally. The main
purpose of the BIND 9's behavior is to make the ordering
predictable, but if the RDATA are rotated in DNS responses (which
BIND 9 also does by default) the predictability wouldn't be that
useful for the clients. So we skip the sorting in the BIND 10
implementation to simplify the implementation (and possibly make it
a bit more efficient).
* If different RRs of the same RRset and their RRSIGs have different
TTL when loaded to the in-memory data source, the lowest TTL among
all RRs (whether it's the covered RRset or RRSIGs) will be used.
BIND 9 shows some inconsistent policy on this point for unknown
reason (sometimes the TTL of the first RR is used, sometimes the
latest one is used). We differ here firstly for consistency, and
because it seems to be more compliant to the sense of RFC2181.
In any case, the administrator should make the TTLs same, especially
if the zone is signed, as described in RFC4034 (and, that will be
normally ensured by zone signing tools).
......@@ -25,13 +25,20 @@
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <boost/static_assert.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <cassert>
#include <cstring>
#include <set>
#include <vector>
#include <boost/static_assert.hpp>
using namespace isc::dns;
using namespace isc::dns::rdata;
using std::vector;
using std::set;
namespace isc {
namespace datasrc {
......@@ -222,7 +229,6 @@ getRdataEncodeSpec(const RRClass& rrclass, const RRType& rrtype) {
}
namespace {
// This class is a helper for RdataEncoder to divide the content of RDATA
// fields for encoding by "abusing" the message rendering logic.
// The idea is to identify domain name fields in the writeName() method,
......@@ -368,16 +374,77 @@ private:
} // end of unnamed namespace
namespace {
// A trivial comparison function used for std::set<ConstRdataPtr> below.
bool
RdataLess(const ConstRdataPtr& rdata1, const ConstRdataPtr& rdata2) {
return (rdata1->compare(*rdata2) < 0);
}
}
struct RdataEncoder::RdataEncoderImpl {
RdataEncoderImpl() : encode_spec_(NULL), rrsig_buffer_(0),
rdata_count_(0)
old_varlen_count_(0), old_sig_count_(0),
old_data_len_(0), old_sig_len_(0),
old_length_fields_(NULL), old_data_(NULL),
old_sig_data_(NULL), olddata_buffer_(0),
rdatas_(RdataLess), rrsigs_(RdataLess)
{}
// Common initialization for RdataEncoder::start().
void start(RRClass rrclass, RRType rrtype) {
if (rrtype == RRType::RRSIG()) {
isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
}
encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
current_class_ = rrclass;
current_type_ = rrtype;
field_composer_.clearLocal(encode_spec_);
rrsig_buffer_.clear();
rrsig_lengths_.clear();
old_varlen_count_ = 0;
old_sig_count_ = 0;
old_data_len_ = 0;
old_sig_len_ = 0;
old_length_fields_ = NULL;
old_data_ = NULL;
old_sig_data_ = NULL;
olddata_buffer_.clear();
rdatas_.clear();
rrsigs_.clear();
}
const RdataEncodeSpec* encode_spec_; // encode spec of current RDATA set
RdataFieldComposer field_composer_;
util::OutputBuffer rrsig_buffer_;
size_t rdata_count_;
vector<uint16_t> rrsig_lengths_;
// Placeholder for the RR class and type of the current session;
// initially null, and will be (re)set at the beginning of each session.
boost::optional<RRClass> current_class_;
boost::optional<RRType> current_type_;
// Parameters corresponding to the previously encoded data in the
// merge mode.
size_t old_varlen_count_;
size_t old_sig_count_;
size_t old_data_len_;
size_t old_sig_len_;
const void* old_length_fields_;
const void* old_data_;
const void* old_sig_data_;
util::OutputBuffer olddata_buffer_;
// Temporary storage of Rdata and RRSIGs to be encoded. They are used
// to detect and ignore duplicate data.
typedef boost::function<bool(const ConstRdataPtr&, const ConstRdataPtr&)>
RdataCmp;
// added unique Rdatas
set<ConstRdataPtr, RdataCmp> rdatas_;
// added unique RRSIG Rdatas
set<ConstRdataPtr, RdataCmp> rrsigs_;
};
RdataEncoder::RdataEncoder() :
......@@ -390,36 +457,127 @@ RdataEncoder::~RdataEncoder() {
void
RdataEncoder::start(RRClass rrclass, RRType rrtype) {
if (rrtype == RRType::RRSIG()) {
isc_throw(BadValue, "RRSIG cannot be encoded as main RDATA type");
}
impl_->start(rrclass, rrtype);
}
impl_->encode_spec_ = &getRdataEncodeSpec(rrclass, rrtype);
impl_->field_composer_.clearLocal(impl_->encode_spec_);
impl_->rrsig_buffer_.clear();
impl_->rdata_count_ = 0;
impl_->rrsig_lengths_.clear();
namespace {
// Helper callbacks used in the merge mode of start(). These re-construct
// each RDATA and RRSIG in the wire-format, counting the total length of the
// encoded data fields.
void
decodeName(const LabelSequence& name_labels, RdataNameAttributes,
util::OutputBuffer* buffer, size_t* total_len)
{
size_t name_dlen;
const uint8_t* name_data = name_labels.getData(&name_dlen);
buffer->writeData(name_data, name_dlen);
*total_len += name_labels.getSerializedLength();
}
void
RdataEncoder::addRdata(const rdata::Rdata& rdata) {
decodeData(const void* data, size_t data_len, util::OutputBuffer* buffer,
size_t* total_len)
{
buffer->writeData(data, data_len);
*total_len += data_len;
}
}
void
RdataEncoder::start(RRClass rrclass, RRType rrtype, const void* old_data,
size_t old_rdata_count, size_t old_sig_count)
{
impl_->start(rrclass, rrtype);
// Identify start points of various fields of the encoded data and
// remember it in class variables.
const uint8_t* cp = static_cast<const uint8_t*>(old_data);
impl_->old_varlen_count_ =
impl_->encode_spec_->varlen_count * old_rdata_count;
if (impl_->old_varlen_count_ > 0 || old_sig_count > 0) {
impl_->old_length_fields_ = cp;
cp += (impl_->old_varlen_count_ + old_sig_count) * sizeof(uint16_t);
}
impl_->old_data_ = cp;
impl_->old_sig_count_ = old_sig_count;
// Re-construct RDATAs and RRSIGs in the form of Rdata objects, and
// keep them in rdatas_ and rrsigs_ so we can detect and ignore duplicate
// data with the existing one later. We'll also figure out the lengths
// of the RDATA and RRSIG part of the data by iterating over the data
// fields. Note that the given old_data shouldn't contain duplicate
// Rdata or RRSIG as they should have been generated by this own class,
// which ensures that condition; if this assumption doesn't hold, we throw.
size_t total_len = 0;
RdataReader reader(rrclass, rrtype, old_data, old_rdata_count,
old_sig_count,
boost::bind(decodeName, _1, _2, &impl_->olddata_buffer_,
&total_len),
boost::bind(decodeData, _1, _2, &impl_->olddata_buffer_,
&total_len));
while (reader.iterateRdata()) {
util::InputBuffer ibuffer(impl_->olddata_buffer_.getData(),
impl_->olddata_buffer_.getLength());
if (!impl_->rdatas_.insert(
createRdata(rrtype, rrclass, ibuffer,
impl_->olddata_buffer_.getLength())).second) {
isc_throw(Unexpected, "duplicate RDATA found in merging RdataSet");
}
impl_->olddata_buffer_.clear();
}
impl_->old_data_len_ = total_len;
total_len = 0;
while (reader.iterateSingleSig()) {
util::InputBuffer ibuffer(impl_->olddata_buffer_.getData(),
impl_->olddata_buffer_.getLength());
if (!impl_->rrsigs_.insert(
createRdata(RRType::RRSIG(), rrclass, ibuffer,
impl_->olddata_buffer_.getLength())).second) {
isc_throw(Unexpected, "duplicate RRSIG found in merging RdataSet");
}
impl_->olddata_buffer_.clear();
}
impl_->old_sig_len_ = total_len;
}
bool
RdataEncoder::addRdata(const Rdata& rdata) {
if (impl_->encode_spec_ == NULL) {
isc_throw(InvalidOperation,
"RdataEncoder::addRdata performed before start");
}
// Simply ignore duplicate RDATA. Creating RdataPtr also checks the
// given Rdata is of the correct RR type.
ConstRdataPtr rdatap = createRdata(*impl_->current_type_,
*impl_->current_class_, rdata);
if (impl_->rdatas_.find(rdatap) != impl_->rdatas_.end()) {
return (false);
}
impl_->field_composer_.startRdata();
rdata.toWire(impl_->field_composer_);
impl_->field_composer_.endRdata();
++impl_->rdata_count_;
impl_->rdatas_.insert(rdatap);
return (true);
}
void
RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
bool
RdataEncoder::addSIGRdata(const Rdata& sig_rdata) {
if (impl_->encode_spec_ == NULL) {
isc_throw(InvalidOperation,
"RdataEncoder::addSIGRdata performed before start");
}
// Ignore duplicate RRSIGs
ConstRdataPtr rdatap = createRdata(RRType::RRSIG(), *impl_->current_class_,
sig_rdata);
if (impl_->rrsigs_.find(rdatap) != impl_->rrsigs_.end()) {
return (false);
}
const size_t cur_pos = impl_->rrsig_buffer_.getLength();
sig_rdata.toWire(impl_->rrsig_buffer_);
const size_t rrsig_datalen = impl_->rrsig_buffer_.getLength() - cur_pos;
......@@ -427,7 +585,10 @@ RdataEncoder::addSIGRdata(const rdata::Rdata& sig_rdata) {
isc_throw(RdataEncodingError, "RRSIG is too large: "
<< rrsig_datalen << " bytes");
}
impl_->rrsigs_.insert(rdatap);
impl_->rrsig_lengths_.push_back(rrsig_datalen);
return (true);
}
size_t
......@@ -437,8 +598,11 @@ RdataEncoder::getStorageLength() const {
"RdataEncoder::getStorageLength performed before start");
}
return (sizeof(uint16_t) * impl_->field_composer_.data_lengths_.size() +
sizeof(uint16_t) * impl_->rrsig_lengths_.size() +
return (sizeof(uint16_t) * (impl_->old_varlen_count_ +
impl_->old_sig_count_ +
impl_->field_composer_.data_lengths_.size() +
impl_->rrsig_lengths_.size()) +
impl_->old_data_len_ + impl_->old_sig_len_ +
impl_->rrsig_buffer_.getLength() +
impl_->field_composer_.getLength());
}
......@@ -461,6 +625,12 @@ RdataEncoder::encode(void* buf, size_t buf_len) const {
uint8_t* dp = dp_beg;
uint16_t* lenp = reinterpret_cast<uint16_t*>(buf);
// Encode list of lengths for variable length fields for old data (if any)
const size_t old_varlen_fields_len =
impl_->old_varlen_count_ * sizeof(uint16_t);
std::memcpy(lenp, impl_->old_length_fields_, old_varlen_fields_len);
lenp += impl_->old_varlen_count_;
dp += old_varlen_fields_len;
// Encode list of lengths for variable length fields (if any)
if (!impl_->field_composer_.data_lengths_.empty()) {
const size_t varlen_fields_len =
......@@ -470,6 +640,12 @@ RdataEncoder::encode(void* buf, size_t buf_len) const {
lenp += impl_->field_composer_.data_lengths_.size();
dp += varlen_fields_len;
}
// Encode list of lengths for old RRSIGs (if any)
const size_t old_rrsigs_len = impl_->old_sig_count_ * sizeof(uint16_t);
std::memcpy(lenp, static_cast<const uint8_t*>(impl_->old_length_fields_) +
old_varlen_fields_len, old_rrsigs_len);
lenp += impl_->old_sig_count_;
dp += old_rrsigs_len;
// Encode list of lengths for RRSIGs (if any)
if (!impl_->rrsig_lengths_.empty()) {
const size_t rrsigs_len =
......@@ -477,10 +653,17 @@ RdataEncoder::encode(void* buf, size_t buf_len) const {
std::memcpy(lenp, &impl_->rrsig_lengths_[0], rrsigs_len);
dp += rrsigs_len;
}
// Encode main old RDATA, if any
std::memcpy(dp, impl_->old_data_, impl_->old_data_len_);
dp += impl_->old_data_len_;
// Encode main RDATA
std::memcpy(dp, impl_->field_composer_.getData(),
impl_->field_composer_.getLength());
dp += impl_->field_composer_.getLength();
// Encode old RRSIGs, if any
std::memcpy(dp, static_cast<const uint8_t*>(impl_->old_data_) +
impl_->old_data_len_, impl_->old_sig_len_);
dp += impl_->old_sig_len_;
// Encode RRSIGs, if any
std::memcpy(dp, impl_->rrsig_buffer_.getData(),
impl_->rrsig_buffer_.getLength());
......@@ -501,7 +684,7 @@ RdataReader::RdataReader(const RRClass& rrclass, const RRType& rrtype,
var_count_total_(spec_.varlen_count * rdata_count),
sig_count_(sig_count),
spec_count_(spec_.field_count * rdata_count),
// The lenghts are stored first
// The lengths are stored first
lengths_(reinterpret_cast<const uint16_t*>(data)),
// And the data just after all the lengths
data_(reinterpret_cast<const uint8_t*>(data) +
......
......@@ -25,8 +25,6 @@
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <vector>
/// \file rdata_serialization.h
///
/// This file defines a set of interfaces (classes, types, constants) to
......@@ -157,6 +155,47 @@ public:
/// \param rrtype The RR type of RDATA to be encoded in the session.
void start(dns::RRClass rrclass, dns::RRType rrtype);
/// \brief Start the encoding session in the merge mode.
///
/// This method is similar to the other version, but begins with a copy
/// of previously encoded data and merges Rdata and RRSIGs into it
/// that will be given via subsequent calls to \c addRdata() and
/// \c addSIGRdata(). \c old_data, \c old_rdata_count, and
/// \c old_sig_count correspond to parameters given to the
/// \c RdataReader constructor, and must have valid values for encoded
/// data by this class for the same \c rrclass and \c rrtype.
/// It's the caller's responsibility to ensure this condition; if it's
/// not met, the behavior will be undefined.
///
/// The caller must also ensure that previously encoded data (pointed
/// to by \c old_data) will be valid and intact throughout the encoding
/// session started by this method. The resulting encoded data (by
/// \c encode()) won't refer to the previous data, so once encoding the
/// merged data is completed (and unless this encoding session continues
/// for another attempt of encoding, which is unlikely), the caller can
/// modify or destroy the old data.
///
/// The caller must also ensure that \c old_data don't contain any
/// duplicate Rdata or RRSIG. Normally the caller doesn't have to do
/// anything special to meet this requirement, though, as the data
/// should have been generated by an \c RdataEncoder object before,
/// which guarantees that condition. But this method checks the
/// assumption in case it was crafted or otherwise broken data, and
/// throws an exception if that is the case.
///
/// \throw Unexpected Given encoded data contain duplicate Rdata or RRSIG
/// (normally shouldn't happen, see the description).
///
/// \param rrclass The RR class of RDATA to be encoded in the session.
/// \param rrtype The RR type of RDATA to be encoded in the session.
/// \param old_data Point to previously encoded data for the same RR
/// class and type.
/// \param old_rdata_count The number of RDATAs stored in \c old_data.
/// \param old_sig_count The number of RRSIGs stored in \c old_data.
void start(dns::RRClass rrclass, dns::RRType rrtype,
const void* old_data, size_t old_rdata_count,
size_t old_sig_count);
/// \brief Add an RDATA for encoding.
///
/// This method updates internal state of the \c RdataEncoder() with the
......@@ -168,6 +207,14 @@ public:
/// to some extent, but the check is not complete; this is generally
/// the responsibility of the caller.
///
/// This method checks if the given RDATA is a duplicate of already
/// added one (including ones encoded in the old data if the session
/// began with the merge mode). If it's a duplicate this method ignores
/// the given RDATA and returns false; otherwise it returns true.
/// The check is based on the comparison in the "canonical form" as
/// described in RFC4034 Section 6.2. In particular, domain name fields
/// of the RDATA are generally compared in case-insensitive manner.
///
/// The caller can destroy \c rdata after this call is completed.
///
/// \note This implementation does not support RDATA (or any subfield of
......@@ -183,12 +230,14 @@ public:
/// new session from \c start() should this method throws an exception.
///
/// \throw InvalidOperation called before start().
/// \throw BadValue inconsistent data found.
/// \throw std::bad_cast The given Rdata is of different RR type.
/// \throw RdataEncodingError A very unusual case, such as over 64KB RDATA.
/// \throw std::bad_alloc Internal memory allocation failure.
///
/// \param rdata An RDATA to be encoded in the session.
void addRdata(const dns::rdata::Rdata& rdata);
/// \return true if the given RDATA was added to encode; false if
/// it's a duplicate and ignored.
bool addRdata(const dns::rdata::Rdata& rdata);
/// \brief Add an RRSIG RDATA for encoding.
///
......@@ -204,6 +253,13 @@ public:
/// it could even accept any type of RDATA as opaque data. It's caller's
/// responsibility to ensure the assumption.
///
/// This method checks if the given RRSIG RDATA is a duplicate of already
/// added one (including ones encoded in the old data if the session
/// began with the merge mode). If it's a duplicate this method ignores
/// the given RRSIG and returns false; otherwise it returns true.
/// The check is based on the comparison in the "canonical form" as
/// described in RFC4034 Section 6.2.
///
/// The caller can destroy \c rdata after this call is completed.
///
/// \note Like addRdata(), this implementation does not support
......@@ -218,7 +274,9 @@ public:
///
/// \param sig_rdata An RDATA to be encoded in the session. Supposed to
/// be of type RRSIG.
void addSIGRdata(const dns::rdata::Rdata& sig_rdata);
/// \return true if the given RRSIG RDATA was added to encode; false if
/// it's a duplicate and ignored.
bool addSIGRdata(const dns::rdata::Rdata& sig_rdata);
/// \brief Return the length of space for encoding for the session.
///
......
......@@ -26,6 +26,7 @@
#include <boost/static_assert.hpp>
#include <stdint.h>
#include <algorithm>
#include <cstring>
#include <typeinfo> // for bad_cast
#include <new> // for the placement new
......@@ -48,11 +49,39 @@ getCoveredType(const Rdata& rdata) {
isc_throw(BadValue, "Non RRSIG is given where it's expected");
}
}
// A helper for lowestTTL: restore RRTTL object from wire-format 32-bit data.
RRTTL
restoreTTL(const void* ttl_data) {
isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
return (RRTTL(b));
}
// A helper function for create(): return the TTL that has lowest value
// amount the given those of given rdataset (if non NULL), rrset, sig_rrset.
RRTTL
lowestTTL(const RdataSet* rdataset, ConstRRsetPtr& rrset,
ConstRRsetPtr& sig_rrset)
{
if (rrset && sig_rrset) {
const RRTTL tmp(std::min(rrset->getTTL(), sig_rrset->getTTL()));
return (rdataset ?
std::min(restoreTTL(rdataset->getTTLData()), tmp) : tmp);
} else if (rrset) {
return (rdataset ? std::min(restoreTTL(rdataset->getTTLData()),
rrset->getTTL()) : rrset->getTTL());
} else {
return (rdataset ? std::min(restoreTTL(rdataset->getTTLData()),
sig_rrset->getTTL()) :
sig_rrset->getTTL());
}
}
}
RdataSet*
RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset)
ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
const RdataSet* old_rdataset)
{
// Check basic validity
if (!rrset && !sig_rrset) {
......@@ -68,31 +97,40 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
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();
if (old_rdataset && old_rdataset->type != rrtype) {
isc_throw(BadValue, "RR type doesn't match for merging RdataSet");
}
const RRTTL rrttl = lowestTTL(old_rdataset, rrset, sig_rrset);
if (old_rdataset) {
encoder.start(rrclass, rrtype, old_rdataset->getDataBuf(),
old_rdataset->getRdataCount(),
old_rdataset->getSigRdataCount());
} else {
encoder.start(rrclass, rrtype);
}
encoder.start(rrclass, rrtype);
// Store RDATAs to be added and check assumptions on the number of them
size_t rdata_count = old_rdataset ? old_rdataset->getRdataCount() : 0;
if (rrset) {
for (RdataIteratorPtr it = rrset->getRdataIterator();
!it->isLast();
it->next()) {
encoder.addRdata(it->getCurrent());
if (encoder.addRdata(it->getCurrent())) {
++rdata_count;
}
}
}
if (rdata_count > MAX_RDATA_COUNT) {
isc_throw(RdataSetError, "Too many RDATAs for RdataSet: "
<< rrset->getRdataCount() << ", must be <= "
<< MAX_RDATA_COUNT);
}
// Same for RRSIG
size_t rrsig_count = old_rdataset ? old_rdataset->getSigRdataCount() : 0;
if (sig_rrset) {
for (RdataIteratorPtr it = sig_rrset->getRdataIterator();
!it->isLast();
......@@ -101,19 +139,24 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
if (getCoveredType(it->getCurrent()) != rrtype) {
isc_throw(BadValue, "Type covered doesn't match");
}
encoder.addSIGRdata(it->getCurrent());
if (encoder.addSIGRdata(it->getCurrent())) {
++rrsig_count;
}
}
}
if (rrsig_count > MAX_RRSIG_COUNT) {
isc_throw(RdataSetError, "Too many RRSIGs for RdataSet: "
<< sig_rrset->getRdataCount() << ", must be <= "
<< MAX_RRSIG_COUNT);
}
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);
RdataSet* rdataset = new(p) RdataSet(rrtype, rdata_count, rrsig_count,
rrttl);
if (rrsig_count >= MANY_RRSIG_COUNT) {
*rdataset->getExtSIGCountBuf() = rrsig_count;
}
......
......@@ -120,6 +120,27 @@ public:
/// RRSIG from the given memory segment, constructs the object, and
/// returns a pointer to it.
///
/// If the optional \c old_rdataset parameter is set to non NULL,
/// The given \c RdataSet, RRset, RRSIG will be merged in the new