Commit 7430591b authored by Michal 'vorner' Vaner's avatar Michal 'vorner' Vaner
Browse files

Merge #2751

Support for RdataSet::subtract.
parents 807d78bb 4bee63ee
......@@ -12,6 +12,9 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include "rdataset.h"
#include "rdata_serialization.h"
#include <exceptions/exceptions.h>
#include <dns/rdata.h>
......@@ -19,11 +22,10 @@
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrset.h>
#include "rdataset.h"
#include "rdata_serialization.h"
#include <util/buffer.h>
#include <boost/static_assert.hpp>
#include <boost/bind.hpp>
#include <stdint.h>
#include <algorithm>
......@@ -74,12 +76,16 @@ lowestTTL(const RdataSet* rdataset, ConstRRsetPtr& rrset,
sig_rrset->getTTL());
}
}
}
RdataSet*
RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
const RdataSet* old_rdataset)
// Do some sanity checks on params of create and substract. Return the
// target rrclass and rrtype (as they are produced as side effect of the
// checks).
//
// The only reason for this function is to reuse common code of the
// methods.
std::pair<RRClass, RRType>
sanityChecks(const ConstRRsetPtr& rrset, const ConstRRsetPtr &sig_rrset,
const RdataSet *old_rdataset)
{
// Check basic validity
if (!rrset && !sig_rrset) {
......@@ -91,6 +97,9 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
if (sig_rrset && sig_rrset->getRdataCount() == 0) {
isc_throw(BadValue, "Empty SIG RRset");
}
if (sig_rrset && sig_rrset->getType() != RRType::RRSIG()) {
isc_throw(BadValue, "SIG RRset doesn't have type RRSIG");
}
if (rrset && sig_rrset && rrset->getClass() != sig_rrset->getClass()) {
isc_throw(BadValue, "RR class doesn't match between RRset and RRSIG");
}
......@@ -98,9 +107,45 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
const RRClass rrclass = rrset ? rrset->getClass() : sig_rrset->getClass();
const RRType rrtype = rrset ? rrset->getType() :
getCoveredType(sig_rrset->getRdataIterator()->getCurrent());
if (old_rdataset && old_rdataset->type != rrtype) {
isc_throw(BadValue, "RR type doesn't match for merging RdataSet");
isc_throw(BadValue, "RR type doesn't match between RdataSets");
}
return (std::pair<RRClass, RRType>(rrclass, rrtype));
}
} // Anonymous namespace
RdataSet*
RdataSet::packSet(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
size_t rdata_count, size_t rrsig_count, const RRType& rrtype,
const RRTTL& rrttl)
{
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, rdata_count, rrsig_count,
rrttl);
if (rrsig_count >= RdataSet::MANY_RRSIG_COUNT) {
*rdataset->getExtSIGCountBuf() = rrsig_count;
}
encoder.encode(rdataset->getDataBuf(), data_len);
return (rdataset);
}
RdataSet*
RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
ConstRRsetPtr rrset, ConstRRsetPtr sig_rrset,
const RdataSet* old_rdataset)
{
const std::pair<RRClass, RRType>& rrparams =
sanityChecks(rrset, sig_rrset, old_rdataset);
const RRClass& rrclass = rrparams.first;
const RRType& rrtype = rrparams.second;
const RRTTL rrttl = lowestTTL(old_rdataset, rrset, sig_rrset);
if (old_rdataset) {
encoder.start(rrclass, rrtype, old_rdataset->getDataBuf(),
......@@ -148,18 +193,99 @@ RdataSet::create(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
<< MAX_RRSIG_COUNT);
}
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, rdata_count, rrsig_count,
rrttl);
if (rrsig_count >= MANY_RRSIG_COUNT) {
*rdataset->getExtSIGCountBuf() = rrsig_count;
return (packSet(mem_sgmt, encoder, rdata_count, rrsig_count, rrtype,
rrttl));
}
namespace {
void writeName(util::OutputBuffer* buffer, const LabelSequence& name,
RdataNameAttributes)
{
size_t len;
const uint8_t* data = name.getData(&len);
buffer->writeData(data, len);
}
void writeData(util::OutputBuffer* buffer, const void* data, size_t len) {
buffer->writeData(data, len);
}
size_t subtractIterate(const dns::ConstRRsetPtr& subtract,
const RRClass& rrclass, const RRType& rrtype,
boost::function<bool ()> iterator,
boost::function<void (const Rdata& rdata)> inserter,
util::OutputBuffer& buffer)
{
size_t count = 0;
while (iterator()) {
util::InputBuffer input(buffer.getData(), buffer.getLength());
const RdataPtr& rdata(createRdata(rrtype, rrclass, input,
buffer.getLength()));
buffer.clear();
bool insert = true;
if (subtract) {
for (RdataIteratorPtr it = subtract->getRdataIterator();
!it->isLast(); it->next()) {
if (rdata->compare(it->getCurrent()) == 0) {
insert = false;
break;
}
}
}
if (insert) {
inserter(*rdata);
++count;
}
}
encoder.encode(rdataset->getDataBuf(), data_len);
return (rdataset);
return (count);
}
} // Anonymous namespace
RdataSet*
RdataSet::subtract(util::MemorySegment& mem_sgmt, RdataEncoder& encoder,
const dns::ConstRRsetPtr& rrset,
const dns::ConstRRsetPtr& sig_rrset,
const RdataSet& old_rdataset)
{
const std::pair<RRClass, RRType>& rrparams =
sanityChecks(rrset, sig_rrset, &old_rdataset);
const RRClass& rrclass = rrparams.first;
const RRType& rrtype = rrparams.second;
// Do the encoding
encoder.start(rrclass, rrtype);
util::OutputBuffer buffer(1024);
RdataReader reader(rrclass, rrtype, old_rdataset.getDataBuf(),
old_rdataset.getRdataCount(),
old_rdataset.getSigRdataCount(),
boost::bind(writeName, &buffer, _1, _2),
boost::bind(writeData, &buffer, _1, _2));
// Copy over the Rdata (except for the subtracted)
const size_t rdata_count =
subtractIterate(rrset, rrclass, rrtype,
boost::bind(&RdataReader::iterateRdata, &reader),
boost::bind(&RdataEncoder::addRdata, &encoder, _1),
buffer);
// Copy over the signatures (except for the subtracted)
const size_t rrsig_count =
subtractIterate(sig_rrset, rrclass, RRType::RRSIG(),
boost::bind(&RdataReader::iterateSingleSig, &reader),
boost::bind(&RdataEncoder::addSIGRdata, &encoder, _1),
buffer);
// Note that we don't need to check for overflow, if it fitted before, it
// fits after removal of something too.
if (rdata_count == 0 && rrsig_count == 0) {
return (NULL); // It is left empty
}
return (packSet(mem_sgmt, encoder, rdata_count, rrsig_count, rrtype,
restoreTTL(old_rdataset.getTTLData())));
}
void
......
......@@ -207,6 +207,53 @@ public:
dns::ConstRRsetPtr sig_rrset,
const RdataSet* old_rdataset = NULL);
/// \brief Subtract some RDATAs and RRSIGs from an RdataSet
///
/// Allocate and construct a new RdataSet that contains all the
/// data from the \c old_rdataset except for the ones in rrset
/// and sig_rrset.
///
/// The interface is almost the same as with \c create, as well
/// as the restrictions and internal format. The most significant
/// difference in the interface is old_rdataset is mandatory here.
///
/// This ignores RDATAs present in rrset and not in old_rdataset
/// (similarly for RRSIGs). If an RDATA in rrset and not in
/// old_rdataset is an error condition or needs other special
/// handling, it is up to the caller to check the old_rdataset
/// first.
///
/// There'll be no memory leak on exception. However, the memory
/// allocated from the mem_sgmt may move when
/// \c util::MemorySegmentGrown is thrown. Note that it may happen
/// even when subtracting data from the old_rdataset, since a new
/// copy is being created.
///
/// The old_rdataset is not destroyed and it is up to the caller to
/// manage its allocation.
///
/// \throw util::MemorySegmentGrown The memory segment has grown, possibly
/// relocating data.
/// \throw isc::BadValue Given RRset(s) are invalid.
/// \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 is to be created.
/// \param rrset A (non-RRSIG) RRset containing the RDATA that are not
/// to be present in the result. Can be NULL if sig_rrset is not.
/// \param sig_rrset An RRSIG RRset containing the RRSIGs that are not
/// to be present in the result. Can be NULL if rrset is not.
/// \param old_rdataset The data from which to subtract.
///
/// \return A pointer to the created \c RdataSet.
static RdataSet* subtract(util::MemorySegment& mem_sgmt,
RdataEncoder& encoder,
const dns::ConstRRsetPtr& rrset,
const dns::ConstRRsetPtr& sig_rrset,
const RdataSet& old_rdataset);
/// \brief Destruct and deallocate \c RdataSet
///
/// Note that this method needs to know the expected RR class of the
......@@ -311,6 +358,12 @@ private:
// field for the real number of RRSIGs. It's 2^3 - 1 = 7.
static const size_t MANY_RRSIG_COUNT = (1 << 3) - 1;
// Common code for packing the result in create and subtract.
static RdataSet* packSet(util::MemorySegment& mem_sgmt,
RdataEncoder& encoder, size_t rdata_count,
size_t rrsig_count, const dns::RRType& rrtype,
const dns::RRTTL& rrttl);
public:
/// \brief Return the bare pointer to the next node.
///
......
......@@ -103,8 +103,10 @@ checkData(const void* data, size_t size, const RRType* rrtype,
ASSERT_TRUE(*it != it_end); // shouldn't reach the end yet
isc::util::InputBuffer b(data, size);
EXPECT_EQ(0, createRdata(*rrtype, RRClass::IN(), b, size)->compare(
*createRdata(*rrtype, RRClass::IN(), **it)));
const RdataPtr& actual(createRdata(*rrtype, RRClass::IN(), b, size));
const RdataPtr& expected(createRdata(*rrtype, RRClass::IN(), **it));
EXPECT_EQ(0, actual->compare(*expected)) << actual->toText() <<
" vs. " << expected->toText();
++(*it); // move to the next expected data
}
......@@ -224,6 +226,91 @@ TEST_F(RdataSetTest, mergeCreate) {
}
}
TEST_F(RdataSetTest, subtract) {
// Prepare test data
const char* const a_rdatas[] = { "192.0.2.1", "192.0.2.2" };
const char* const sig_rdatas[] = {
"A 5 2 3600 20120814220826 20120715220826 1234 example.com. FAKE",
"A 5 2 3600 20120814220826 20120715220826 4321 example.com. FAKE" };
ConstRRsetPtr a_rrsets = textToRRset("www.example.com. 1076895760 IN A "
+ string(a_rdatas[0]) + "\n"
+ "www.example.com. 1076895760 IN A "
+ string(a_rdatas[1]));
ConstRRsetPtr rrsig_rrsets =
textToRRset("www.example.com. 1076895760 IN RRSIG "
+ string(sig_rdatas[0]) + "\n"
+ "www.example.com. 1076895760 IN RRSIG "
+ string(sig_rdatas[1]));
ConstRRsetPtr null_rrset; // convenience shortcut
// Prepare the data to subtract (they have one common and one differing
// element each).
const char* const a_rdatas_rm[] = { "192.0.2.1", "192.0.2.3" };
const char* const sig_rdatas_rm[] = {
"A 5 2 3600 20120814220826 20120715220826 1234 example.com. FAKE",
"A 5 2 3600 20120814220826 20120715220826 5678 example.com. FAKE" };
ConstRRsetPtr a_rrsets_rm =
textToRRset("www.example.com. 1076895760 IN A "
+ string(a_rdatas_rm[0]) + "\n"
+ "www.example.com. 1076895760 IN A "
+ string(a_rdatas_rm[1]));
ConstRRsetPtr rrsig_rrsets_rm =
textToRRset("www.example.com. 1076895760 IN RRSIG "
+ string(sig_rdatas_rm[0]) + "\n"
+ "www.example.com. 1076895760 IN RRSIG "
+ string(sig_rdatas_rm[1]));
// A similar cycle as in the mergeCreate test.
for (int i = 1; i < 4; ++i) {
for (int j = 1; j < 4; ++j) {
SCOPED_TRACE("creating subtract case " + lexical_cast<string>(i) +
", " + lexical_cast<string>(j));
// Create old rdataset
SegmentObjectHolder<RdataSet, RRClass> holder1(mem_sgmt_, rrclass);
holder1.set(RdataSet::create(mem_sgmt_, encoder_,
(i & 1) ? a_rrsets : null_rrset,
(i & 2) ? rrsig_rrsets : null_rrset));
// Create subtracted rdataset, from the old one and RRsets
SegmentObjectHolder<RdataSet, RRClass> holder2(mem_sgmt_, rrclass);
holder2.set(RdataSet::subtract(mem_sgmt_, encoder_,
(j & 1) ? a_rrsets_rm : null_rrset,
(j & 2) ? rrsig_rrsets_rm : null_rrset,
*holder1.get()));
// Set up the expected data for the case.
vector<string> expected_rdata;
if (i & 1) {
if (!(j & 1)) { // Not removed the other
expected_rdata.push_back(a_rdatas[0]);
}
expected_rdata.push_back(a_rdatas[1]);
}
vector<string> expected_sigs;
if (i & 2) {
if (!(j & 2)) { // Not removed the other
expected_sigs.push_back(sig_rdatas[0]);
}
expected_sigs.push_back(sig_rdatas[1]);
}
// Then perform the check
checkRdataSet(*holder2.get(), expected_rdata, expected_sigs);
}
}
// Reusing the data we have, test some corner cases.
SegmentObjectHolder<RdataSet, RRClass> holder_old(mem_sgmt_, rrclass);
holder_old.set(RdataSet::create(mem_sgmt_, encoder_, a_rrsets,
rrsig_rrsets));
// It throws if no Rdata passed.
EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, null_rrset,
null_rrset, *holder_old.get()),
isc::BadValue);
// If we remove everything, it returns NULL
EXPECT_EQ(NULL, RdataSet::subtract(mem_sgmt_, encoder_, a_rrsets,
rrsig_rrsets, *holder_old.get()));
}
TEST_F(RdataSetTest, duplicate) {
// Create RRset and RRSIG containing duplicate RDATA.
ConstRRsetPtr dup_rrset =
......@@ -610,4 +697,58 @@ TEST_F(RdataSetTest, varyingTTL) {
EXPECT_EQ(RRTTL(5), restoreTTL(rdataset->getTTLData()));
RdataSet::destroy(mem_sgmt_, rdataset, rrclass);
}
// Creation of rdataset with bad params, with create and subtract
TEST_F(RdataSetTest, badParams) {
const ConstRRsetPtr empty_rrset(new RRset(Name("www.example.com"),
RRClass::IN(), RRType::A(),
RRTTL(3600)));
const ConstRRsetPtr empty_rrsig(new RRset(Name("www.example.com"),
RRClass::IN(), RRType::RRSIG(),
RRTTL(3600)));
const ConstRRsetPtr a_rrset = textToRRset("www.example.com. 3600 IN A "
"192.0.2.1");
const ConstRRsetPtr aaaa_rrset = textToRRset("www.example.com. 3600 IN AAAA "
"2001:db8::1");
const ConstRRsetPtr sig_rrset = textToRRset("www.example.com. 3600 IN RRSIG "
"A 5 2 3600 20120814220826 "
"20120715220826 1234 "
"example.com. FAKE");
const ConstRRsetPtr sig_rrset_ch = textToRRset("www.example.com. 3600 CH RRSIG "
"A 5 2 3600 20120814220826 "
"20120715220826 1234 "
"example.com. FAKE",
RRClass::CH());
SegmentObjectHolder<RdataSet, RRClass> holder(mem_sgmt_, rrclass);
holder.set(RdataSet::create(mem_sgmt_, encoder_, a_rrset, sig_rrset));
// Empty RRset as rdata
EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, sig_rrset),
isc::BadValue);
// The same for rrsig
EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset, empty_rrsig),
isc::BadValue);
// Mismatched type
EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset, a_rrset),
isc::BadValue);
// Similar for subtract
EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, empty_rrset,
sig_rrset, *holder.get()),
isc::BadValue);
EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, a_rrset, empty_rrset,
*holder.get()),
isc::BadValue);
// Class mismatch
EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset, sig_rrset_ch),
isc::BadValue);
EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, a_rrset,
sig_rrset_ch, *holder.get()),
isc::BadValue);
// Bad rrtype
EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset,
ConstRRsetPtr(), holder.get()),
isc::BadValue);
EXPECT_THROW(RdataSet::subtract(mem_sgmt_, encoder_, aaaa_rrset,
ConstRRsetPtr(), *holder.get()),
isc::BadValue);
}
}
......@@ -367,7 +367,6 @@ public:
using AbstractMessageRenderer::CASE_INSENSITIVE;
using AbstractMessageRenderer::CASE_SENSITIVE;
/// \brief Constructor from an output buffer.
MessageRenderer();
virtual ~MessageRenderer();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment