Commit 6744c100 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[master] Merge branch 'trac2420'

parents 3b408810 0c36cd61
......@@ -27,6 +27,7 @@ libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
libdatasrc_memory_la_SOURCES += zone_writer.h
libdatasrc_memory_la_SOURCES += zone_writer_local.h zone_writer_local.cc
libdatasrc_memory_la_SOURCES += load_action.h
libdatasrc_memory_la_SOURCES += util_internal.h
nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
......
......@@ -122,8 +122,8 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
}
void
RdataSet::destroy(util::MemorySegment& mem_sgmt, RRClass rrclass,
RdataSet* rdataset)
RdataSet::destroy(util::MemorySegment& mem_sgmt, RdataSet* rdataset,
RRClass rrclass)
{
const size_t data_len =
RdataReader(rrclass, rdataset->type,
......
......@@ -187,12 +187,12 @@ public:
///
/// \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
/// \param rrclass The RR class of the \c RdataSet to be destroyed.
/// 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);
static void destroy(util::MemorySegment& mem_sgmt, RdataSet* rdataset,
dns::RRClass rrclass);
/// \brief Find \c RdataSet of given RR type from a list (const version).
///
......@@ -205,6 +205,11 @@ public:
/// if not found in the entire list, it returns NULL. The head pointer
/// can be NULL, in which case this function will simply return NULL.
///
/// By default, this method ignores an RdataSet that only contains an
/// RRSIG (i.e., missing the covered RdataSet); if the optional
/// sigonly_ok parameter is explicitly set to true, it matches such
/// RdataSet and returns it if found.
///
/// \note This function is defined as a (static) class method to
/// clarify its an operation for \c RdataSet objects and to make the
/// name shorter. But its implementation does not depend on private
......@@ -215,10 +220,14 @@ public:
/// \param rdata_head A pointer to \c RdataSet from which the search
/// starts. It can be NULL.
/// \param type The RRType of \c RdataSet to find.
/// \param sigonly_ok Whether it should find an RdataSet that only has
/// RRSIG
/// \return A pointer to the found \c RdataSet or NULL if none found.
static const RdataSet*
find(const RdataSet* rdataset_head, const dns::RRType& type) {
return (find<const RdataSet>(rdataset_head, type));
find(const RdataSet* rdataset_head, const dns::RRType& type,
bool sigonly_ok = false)
{
return (find<const RdataSet>(rdataset_head, type, sigonly_ok));
}
/// \brief Find \c RdataSet of given RR type from a list (non const
......@@ -227,8 +236,10 @@ public:
/// This is similar to the const version, except it takes and returns non
/// const pointers.
static RdataSet*
find(RdataSet* rdataset_head, const dns::RRType& type) {
return (find<RdataSet>(rdataset_head, type));
find(RdataSet* rdataset_head, const dns::RRType& type,
bool sigonly_ok = false)
{
return (find<RdataSet>(rdataset_head, type, sigonly_ok));
}
typedef boost::interprocess::offset_ptr<RdataSet> RdataSetPtr;
......@@ -347,12 +358,14 @@ private:
// Shared by both mutable and immutable versions of find()
template <typename RdataSetType>
static RdataSetType*
find(RdataSetType* rdataset_head, const dns::RRType& type) {
find(RdataSetType* rdataset_head, const dns::RRType& type, bool sigonly_ok)
{
for (RdataSetType* rdataset = rdataset_head;
rdataset != NULL;
rdataset = rdataset->getNext()) // use getNext() for efficiency
{
if (rdataset->type == type) {
if (rdataset->type == type &&
(rdataset->getRdataCount() > 0 || sigonly_ok)) {
return (rdataset);
}
}
......
// 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_UTIL_INTERNAL_H
#define DATASRC_MEMORY_UTIL_INTERNAL_H 1
#include <dns/rdataclass.h>
#include <dns/rrset.h>
#include <dns/rrtype.h>
namespace isc {
namespace datasrc {
namespace memory {
namespace detail {
/// \brief Return the covered RR type of an RRSIG RRset.
///
/// This is a commonly used helper to extract the type covered field of an
/// RRSIG RRset and return it in the form of an RRType object.
///
/// Normally, an empty RRSIG shouldn't be passed to this function, whether
/// it comes from a master file or another data source iterator, but it could
/// still happen in some buggy situations. This function catches and rejects
/// such cases.
inline dns::RRType
getCoveredType(const dns::ConstRRsetPtr& sig_rrset) {
dns::RdataIteratorPtr it = sig_rrset->getRdataIterator();
if (it->isLast()) {
isc_throw(isc::Unexpected,
"Empty RRset is passed in-memory loader, name: "
<< sig_rrset->getName());
}
return (dynamic_cast<const dns::rdata::generic::RRSIG&>(it->getCurrent()).
typeCovered());
}
} // namespace detail
} // namespace memory
} // namespace datasrc
} // namespace isc
#endif // DATASRC_MEMORY_UTIL_INTERNAL_H
// Local Variables:
// mode: c++
// End:
......@@ -49,7 +49,7 @@ rdataSetDeleter(RRClass rrclass, util::MemorySegment* mem_sgmt,
rdataset = rdataset_next)
{
rdataset_next = rdataset->getNext();
RdataSet::destroy(*mem_sgmt, rrclass, rdataset);
RdataSet::destroy(*mem_sgmt, rdataset, rrclass);
}
}
......
......@@ -16,6 +16,7 @@
#include <datasrc/memory/zone_data_updater.h>
#include <datasrc/memory/logger.h>
#include <datasrc/memory/segment_object_holder.h>
#include <datasrc/memory/util_internal.h>
#include <dns/rdataclass.h>
#include <dns/rrset.h>
......@@ -35,6 +36,7 @@ namespace datasrc {
namespace memory {
using detail::SegmentObjectHolder;
using detail::getCoveredType;
namespace { // unnamed namespace
......@@ -75,8 +77,6 @@ private:
typedef NodeRRsets::value_type NodeRRsetsVal;
// A helper to identify the covered type of an RRSIG.
static isc::dns::RRType getCoveredType
(const isc::dns::ConstRRsetPtr& sig_rrset);
const isc::dns::Name& getCurrentName() const;
private:
......@@ -126,34 +126,17 @@ ZoneDataLoader::flushNodeRRsets() {
updater_.add(val.second, sig_rrset);
}
// Right now, we don't accept RRSIG without covered RRsets (this
// should eventually allowed, but to do so we'll need to update the
// finder).
if (!node_rrsigsets_.empty()) {
isc_throw(ZoneDataUpdater::AddError,
"RRSIG is added without covered RRset for "
<< getCurrentName());
// Normally rrsigsets map should be empty at this point, but it's still
// possible that an RRSIG that don't has covered RRset is added; they
// still remain in the map. We add them to the zone separately.
BOOST_FOREACH(NodeRRsetsVal val, node_rrsigsets_) {
updater_.add(ConstRRsetPtr(), val.second);
}
node_rrsets_.clear();
node_rrsigsets_.clear();
}
RRType
ZoneDataLoader::getCoveredType(const ConstRRsetPtr& sig_rrset) {
RdataIteratorPtr it = sig_rrset->getRdataIterator();
// Empty RRSIG shouldn't be passed either via a master file or
// another data source iterator, but it could still happen if the
// iterator has a bug. We catch and reject such cases.
if (it->isLast()) {
isc_throw(isc::Unexpected,
"Empty RRset is passed in-memory loader, name: "
<< sig_rrset->getName());
}
return (dynamic_cast<const generic::RRSIG&>(it->getCurrent()).
typeCovered());
}
const Name&
ZoneDataLoader::getCurrentName() const {
if (!node_rrsets_.empty()) {
......
......@@ -12,12 +12,18 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <exceptions/exceptions.h>
#include <datasrc/memory/zone_data_updater.h>
#include <datasrc/memory/logger.h>
#include <datasrc/memory/util_internal.h>
#include <datasrc/zone.h>
#include <dns/rdataclass.h>
#include <cassert>
#include <string>
using namespace isc::dns;
using namespace isc::dns::rdata;
......@@ -25,6 +31,8 @@ namespace isc {
namespace datasrc {
namespace memory {
using detail::getCoveredType;
void
ZoneDataUpdater::addWildcards(const Name& name) {
Name wname(name);
......@@ -99,9 +107,7 @@ ZoneDataUpdater::contextCheck(const AbstractRRset& rrset,
void
ZoneDataUpdater::validate(const isc::dns::ConstRRsetPtr rrset) const {
if (!rrset) {
isc_throw(NullRRset, "The rrset provided is NULL");
}
assert(rrset);
if (rrset->getRdataCount() == 0) {
isc_throw(AddError,
......@@ -241,31 +247,46 @@ ZoneDataUpdater::setupNSEC3(const ConstRRsetPtr rrset) {
}
void
ZoneDataUpdater::addNSEC3(const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig)
ZoneDataUpdater::addNSEC3(const Name& name, const ConstRRsetPtr rrset,
const ConstRRsetPtr rrsig)
{
setupNSEC3<generic::NSEC3>(rrset);
if (rrset) {
setupNSEC3<generic::NSEC3>(rrset);
}
NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
if (nsec3_data == NULL) {
// This is some tricky case: an RRSIG for NSEC3 is given without the
// covered NSEC3, and we don't even know any NSEC3 related data.
// This situation is not necessarily broken, but in our current
// implementation it's very difficult to deal with. So we reject it;
// hopefully this case shouldn't happen in practice, at least unless
// zone is really broken.
assert(!rrset);
isc_throw(NotImplemented,
"RRSIG for NSEC3 cannot be added - no known NSEC3 data");
}
ZoneNode* node;
nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
nsec3_data->insertName(mem_sgmt_, name, &node);
RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, rrset, rrsig);
RdataSet* old_rdataset = node->setData(rdataset);
if (old_rdataset != NULL) {
RdataSet::destroy(mem_sgmt_, rrclass_, old_rdataset);
RdataSet::destroy(mem_sgmt_, old_rdataset, rrclass_);
}
}
void
ZoneDataUpdater::addRdataSet(const ConstRRsetPtr rrset,
ZoneDataUpdater::addRdataSet(const Name& name, const RRType& rrtype,
const ConstRRsetPtr rrset,
const ConstRRsetPtr rrsig)
{
if (rrset->getType() == RRType::NSEC3()) {
addNSEC3(rrset, rrsig);
if (rrtype == RRType::NSEC3()) {
addNSEC3(name, rrset, rrsig);
} else {
ZoneNode* node;
zone_data_.insertName(mem_sgmt_, rrset->getName(), &node);
zone_data_.insertName(mem_sgmt_, name, &node);
RdataSet* rdataset_head = node->getData();
......@@ -273,13 +294,14 @@ ZoneDataUpdater::addRdataSet(const ConstRRsetPtr rrset,
// fails and the exception is thrown, it may break strong
// exception guarantee. At the moment we prefer code simplicity
// and don't bother to introduce complicated recovery code.
contextCheck(*rrset, rdataset_head);
if (rrset) { // this check is only for covered RRset, not RRSIG
contextCheck(*rrset, rdataset_head);
}
if (RdataSet::find(rdataset_head, rrset->getType()) != NULL) {
if (RdataSet::find(rdataset_head, rrtype, true) != NULL) {
isc_throw(AddError,
"RRset of the type already exists: "
<< rrset->getName() << " (type: "
<< rrset->getType() << ")");
<< name << " (type: " << rrtype << ")");
}
RdataSet* rdataset_new = RdataSet::create(mem_sgmt_, encoder_,
......@@ -289,23 +311,25 @@ ZoneDataUpdater::addRdataSet(const ConstRRsetPtr rrset,
// Ok, we just put it in.
// Convenient (and more efficient) shortcut to check RRsets at origin
const bool is_origin = (node == zone_data_.getOriginNode());
// If this RRset creates a zone cut at this node, mark the node
// indicating the need for callback in find().
if (rrset->getType() == RRType::NS() &&
rrset->getName() != zone_name_) {
// indicating the need for callback in find(). Note that we do this
// only when non RRSIG RRset of that type is added.
if (rrset && rrtype == RRType::NS() && !is_origin) {
node->setFlag(ZoneNode::FLAG_CALLBACK);
// If it is DNAME, we have a callback as well here
} else if (rrset->getType() == RRType::DNAME()) {
} else if (rrset && rrtype == RRType::DNAME()) {
node->setFlag(ZoneNode::FLAG_CALLBACK);
}
// If we've added NSEC3PARAM at zone origin, set up NSEC3
// specific data or check consistency with already set up
// parameters.
if (rrset->getType() == RRType::NSEC3PARAM() &&
rrset->getName() == zone_name_) {
if (rrset && rrtype == RRType::NSEC3PARAM() && is_origin) {
setupNSEC3<generic::NSEC3PARAM>(rrset);
} else if (rrset->getType() == RRType::NSEC()) {
} else if (rrset && rrtype == RRType::NSEC() && is_origin) {
// If it is NSEC signed zone, we mark the zone as signed
// (conceptually "signed" is a broader notion but our
// current zone finder implementation regards "signed" as
......@@ -319,27 +343,37 @@ void
ZoneDataUpdater::add(const ConstRRsetPtr& rrset,
const ConstRRsetPtr& sig_rrset)
{
// Validate input. This will cause an exception to be thrown if the
// input RRset is empty.
validate(rrset);
// Validate input.
if (!rrset && !sig_rrset) {
isc_throw(NullRRset,
"ZoneDataUpdater::add is given 2 NULL pointers");
}
if (rrset) {
validate(rrset);
}
if (sig_rrset) {
validate(sig_rrset);
}
const Name& name = rrset ? rrset->getName() : sig_rrset->getName();
const RRType& rrtype = rrset ? rrset->getType() :
getCoveredType(sig_rrset);
// OK, can add the RRset.
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
arg(rrset->getName()).arg(rrset->getType()).arg(zone_name_);
LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).arg(name).
arg(rrset ? rrtype.toText() : "RRSIG(" + rrtype.toText() + ")").
arg(zone_name_);
// Add wildcards possibly contained in the owner name to the domain
// tree. This can only happen for the normal (non-NSEC3) tree.
// Note: this can throw an exception, breaking strong exception
// guarantee. (see also the note for the call to contextCheck()
// above).
if (rrset->getType() != RRType::NSEC3()) {
addWildcards(rrset->getName());
if (rrtype != RRType::NSEC3()) {
addWildcards(name);
}
addRdataSet(rrset, sig_rrset);
addRdataSet(name, rrtype, rrset, sig_rrset);
}
} // namespace memory
......
......@@ -110,10 +110,32 @@ public:
/// populated with the record data and added to the ZoneData for the
/// name in the RRset.
///
/// This method throws an \c NullRRset exception (see above) if
/// \c rrset is empty. It throws \c AddError if any of a variety of
/// validation checks fail for the \c rrset and its associated
/// \c sig_rrset.
/// At least one of \c rrset or \c sig_rrset must be non NULL.
/// \c sig_rrset can be reasonably NULL when \c rrset is not signed in
/// the zone; it's unusual that \c rrset is NULL, but is still possible
/// if these RRsets are given separately to the loader, or if even the
/// zone is half broken and really contains an RRSIG that doesn't have
/// any covered RRset. This implementation supports these cases (but
/// see the note below).
///
/// There is one tricky case: Due to a limitation of the current
/// implementation, it cannot accept an RRSIG for NSEC3 without the covered
/// NSEC3, unless at least one NSEC3 or NSEC3PARAM has been added.
/// In this case an isc::NotImplemented exception will be thrown. It
/// should be very rare in practice, and hopefully wouldn't be a real
/// issue.
///
/// \note Due to limitations of the current implementation, if a
/// (non RRSIG) RRset and its RRSIG are added separately in different
/// calls to this method, the second attempt will be rejected due to
/// an \c AddError exception. This will be loosened in Trac
/// ticket #2441.
///
/// \throw NullRRset Both \c rrset and sig_rrset is NULL
/// \throw AddError any of a variety of validation checks fail for the
/// \c rrset and its associated \c sig_rrset.
/// \throw NotImplemented RRSIG for NSEC3 cannot be added due to internal
/// restriction.
///
/// \param rrset The RRset to be added.
/// \param sig_rrset An associated RRSIG RRset for the \c rrset. It
......@@ -152,9 +174,12 @@ private:
const isc::dns::NSEC3Hash* getNSEC3Hash();
template <typename T>
void setupNSEC3(const isc::dns::ConstRRsetPtr rrset);
void addNSEC3(const isc::dns::ConstRRsetPtr rrset,
void addNSEC3(const isc::dns::Name& name,
const isc::dns::ConstRRsetPtr rrset,
const isc::dns::ConstRRsetPtr rrsig);
void addRdataSet(const isc::dns::ConstRRsetPtr rrset,
void addRdataSet(const isc::dns::Name& name,
const isc::dns::RRType& rrtype,
const isc::dns::ConstRRsetPtr rrset,
const isc::dns::ConstRRsetPtr rrsig);
util::MemorySegment& mem_sgmt_;
......
......@@ -216,6 +216,14 @@ createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
assert(rdataset != NULL);
assert(rdataset->type == RRType::NSEC3());
// Check for the rare case of RRSIG-only record; in theory it could exist
// but we simply consider it broken for NSEC3.
if (rdataset->getRdataCount() == 0) {
uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
isc_throw(DataSourceError, "Broken zone: RRSIG-only NSEC3 record at "
<< node->getAbsoluteLabels(labels_buf) << "/" << rrclass);
}
// Create the RRset. Note the DNSSEC flag: NSEC3 implies DNSSEC.
return (createTreeNodeRRset(node, rdataset, rrclass,
ZoneFinder::FIND_DNSSEC));
......@@ -627,7 +635,10 @@ private:
// This can be a bit more optimized, but unless we have many
// requested types the effect is probably marginal. For now we
// keep it simple.
if (std::find(type_beg, type_end, rdset->type) != type_end) {
// Check for getRdataCount is necessary not to include RRSIG-only
// records accidentally (should be rare, but possible).
if (std::find(type_beg, type_end, rdset->type) != type_end &&
rdset->getRdataCount() > 0) {
result->push_back(createTreeNodeRRset(node, rdset, rrclass_,
options, real_name));
}
......
......@@ -94,6 +94,7 @@ endif
EXTRA_DIST = testdata/brokendb.sqlite3
EXTRA_DIST += testdata/contexttest.zone
EXTRA_DIST += testdata/contexttest-almost-obsolete.zone
EXTRA_DIST += testdata/diffs.sqlite3
EXTRA_DIST += testdata/duplicate_rrset.zone
EXTRA_DIST += testdata/example2.com
......
......@@ -32,6 +32,8 @@ run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
run_unittests_SOURCES += memory_segment_test.h
run_unittests_SOURCES += segment_object_holder_unittest.cc
run_unittests_SOURCES += memory_client_unittest.cc
run_unittests_SOURCES += zone_data_loader_unittest.cc
run_unittests_SOURCES += zone_data_updater_unittest.cc
run_unittests_SOURCES += zone_table_segment_test.h
run_unittests_SOURCES += zone_table_segment_unittest.cc
run_unittests_SOURCES += zone_writer_unittest.cc
......
......@@ -576,16 +576,6 @@ TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
// Teardown checks for memory segment leaks
}
TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
// This causes the situation where an RRSIG is added without a covered
// RRset. Such cases are currently rejected.
EXPECT_THROW(client_->load(Name("example.org"),
TEST_DATA_DIR
"/example.org-rrsig-follows-nothing.zone"),
ZoneDataUpdater::AddError);
// Teardown checks for memory segment leaks
}
TEST_F(MemoryClientTest, loadRRSIGs) {
client_->load(Name("example.org"),
TEST_DATA_DIR "/example.org-rrsigs.zone");
......
......@@ -24,6 +24,7 @@
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <datasrc/memory/segment_object_holder.h>
#include <datasrc/memory/rdata_serialization.h>
#include <datasrc/memory/rdataset.h>
......@@ -39,6 +40,7 @@ using namespace isc::dns;
using namespace isc::dns::rdata;
using namespace isc::datasrc::memory;
using namespace isc::testutils;
using isc::datasrc::memory::detail::SegmentObjectHolder;
using boost::lexical_cast;
namespace {
......@@ -112,7 +114,7 @@ TEST_F(RdataSetTest, create) {
RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
ConstRRsetPtr());
checkRdataSet(*rdataset, true, false);
RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());
}
TEST_F(RdataSetTest, getNext) {
......@@ -131,7 +133,62 @@ TEST_F(RdataSetTest, getNext) {
rdataset->next = rdataset;
EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
RdataSet::destroy(mem_sgmt_, rdataset, RRClass::IN());
}
TEST_F(RdataSetTest, find) {
// Create some RdataSets and make a chain of them.
SegmentObjectHolder<RdataSet, RRClass> holder1(
mem_sgmt_,
RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr()),
RRClass::IN());
ConstRRsetPtr aaaa_rrset =
textToRRset("www.example.com. 1076895760 IN AAAA 2001:db8::1");
SegmentObjectHolder<RdataSet, RRClass> holder2(
mem_sgmt_,
RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset, ConstRRsetPtr()),
RRClass::IN());
ConstRRsetPtr sigonly_rrset =
textToRRset("www.example.com. 1076895760 IN RRSIG "
"TXT 5 2 3600 20120814220826 20120715220826 "
"1234 example.com. FAKE");
SegmentObjectHolder<RdataSet, RRClass> holder3(
mem_sgmt_,
RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(), sigonly_rrset),
RRClass::IN());
RdataSet* rdataset_a = holder1.get();
RdataSet* rdataset_aaaa = holder2.get();
RdataSet* rdataset_sigonly = holder3.get();
RdataSet* rdataset_null = NULL;
rdataset_a->next = rdataset_aaaa;
rdataset_aaaa->next = rdataset_sigonly;
// If a non-RRSIG part of rdataset exists for the given type, it will be
// returned regardless of the value of sigonly_ok. If it's RRSIG-only
// rdataset, it returns non NULL iff sigonly_ok is explicitly set to true.
EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA()));
EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA(), true));
EXPECT_EQ(rdataset_aaaa, RdataSet::find(rdataset_a, RRType::AAAA(), false));
EXPECT_EQ(rdataset_null, RdataSet::find(rdataset_a, RRType::TXT()));