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

[2098] supported truncation case

also cleaned up the code by unifying the common rendering logic for main
and RRSIG RRs.
parent 82327580
......@@ -92,21 +92,24 @@ protected:
};
TEST_F(TreeNodeRRsetTest, create) {
const TreeNodeRRset rrset1(RRClass::IN(), www_node_, a_rdataset_, true);
EXPECT_EQ(RRClass::IN(), rrset1.getClass());
const TreeNodeRRset rrset1(rrclass_, www_node_, a_rdataset_, true);
EXPECT_EQ(rrclass_, rrset1.getClass());
EXPECT_EQ(RRType::A(), rrset1.getType());
EXPECT_EQ(www_name_, rrset1.getName());
EXPECT_EQ(2, rrset1.getRdataCount());
EXPECT_EQ(1, rrset1.getRRsigDataCount());
const TreeNodeRRset rrset2(RRClass::IN(), www_node_, a_rdataset_, false);
EXPECT_EQ(RRClass::IN(), rrset2.getClass());
const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, false);
EXPECT_EQ(rrclass_, rrset2.getClass());
EXPECT_EQ(RRType::A(), rrset2.getType());
EXPECT_EQ(www_name_, rrset2.getName());
EXPECT_EQ(2, rrset2.getRdataCount());
EXPECT_EQ(0, rrset2.getRRsigDataCount());
}
// Templated if and when we support OutputBuffer version of toWire().
// Right now, we take a minimalist approach, only implementing testing the
// renderer version.
template <typename OutputType>
void
checkToWireResult(OutputType& expected_output, OutputType& actual_output,
......@@ -140,38 +143,97 @@ checkToWireResult(OutputType& expected_output, OutputType& actual_output,
TEST_F(TreeNodeRRsetTest, toWire) {
MessageRenderer expected_renderer, actual_renderer;
//OutputBuffer expected_buffer(0), actual_buffer(0);
// 1. with RRSIG, DNSSEC OK
const TreeNodeRRset rrset1(RRClass::IN(), www_node_, a_rdataset_, true);
checkToWireResult(expected_renderer, actual_renderer, rrset1, www_name_,
a_rrset_, rrsig_rrset_, true);
#ifdef notyet
checkToWireResult(expected_buffer, actual_buffer, rrset1, www_name_,
a_rrset_, rrsig_rrset_, true);
#endif
// 2. with RRSIG, DNSSEC not OK
const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, false);
checkToWireResult(expected_renderer, actual_renderer, rrset2, www_name_,
a_rrset_, rrsig_rrset_, false);
// 3. without RRSIG, DNSSEC OK
const TreeNodeRRset rrset3(rrclass_, origin_node_, ns_rdataset_, true);
checkToWireResult(expected_renderer, actual_renderer, rrset3, origin_name_,
ns_rrset_, ConstRRsetPtr(), true);
// 4. without RRSIG, DNSSEC not OK
const TreeNodeRRset rrset4(rrclass_, origin_node_, ns_rdataset_, false);
checkToWireResult(expected_renderer, actual_renderer, rrset4, origin_name_,
ns_rrset_, ConstRRsetPtr(), false);
// RDATA of DNAME RR shouldn't be compressed. Prepending "example.org"
// will check that.
const TreeNodeRRset rrset5(rrclass_, origin_node_, dname_rdataset_, false);
checkToWireResult(expected_renderer, actual_renderer, rrset5,
Name("example.org"), dname_rrset_, ConstRRsetPtr(),
false);
{
SCOPED_TRACE("with RRSIG, DNSSEC OK");
const TreeNodeRRset rrset1(rrclass_, www_node_, a_rdataset_, true);
checkToWireResult(expected_renderer, actual_renderer, rrset1,
www_name_, a_rrset_, rrsig_rrset_, true);
EXPECT_FALSE(actual_renderer.isTruncated());
}
{
SCOPED_TRACE("with RRSIG, DNSSEC not OK");
const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, false);
checkToWireResult(expected_renderer, actual_renderer, rrset2,
www_name_, a_rrset_, rrsig_rrset_, false);
EXPECT_FALSE(actual_renderer.isTruncated());
}
{
SCOPED_TRACE("without RRSIG, DNSSEC OK");
const TreeNodeRRset rrset3(rrclass_, origin_node_, ns_rdataset_, true);
checkToWireResult(expected_renderer, actual_renderer, rrset3,
origin_name_, ns_rrset_, ConstRRsetPtr(), true);
EXPECT_FALSE(actual_renderer.isTruncated());
}
{
SCOPED_TRACE("without RRSIG, DNSSEC not OK");
const TreeNodeRRset rrset4(rrclass_, origin_node_, ns_rdataset_,
false);
checkToWireResult(expected_renderer, actual_renderer, rrset4,
origin_name_, ns_rrset_, ConstRRsetPtr(), false);
EXPECT_FALSE(actual_renderer.isTruncated());
}
{
// RDATA of DNAME DR shouldn't be compressed. Prepending "example.org"
// will check that.
SCOPED_TRACE("uncompressed RDATA");
const TreeNodeRRset rrset5(rrclass_, origin_node_, dname_rdataset_,
false);
checkToWireResult(expected_renderer, actual_renderer, rrset5,
Name("example.org"), dname_rrset_, ConstRRsetPtr(),
false);
EXPECT_FALSE(actual_renderer.isTruncated());
}
}
void
checkTruncationResult(MessageRenderer& expected_renderer,
MessageRenderer& actual_renderer,
const TreeNodeRRset& actual_rrset,
ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
bool dnssec_ok, size_t len_limit, size_t expected_result)
{
expected_renderer.clear();
actual_renderer.clear();
actual_renderer.setLengthLimit(len_limit);
EXPECT_EQ(expected_result, actual_rrset.toWire(actual_renderer));
EXPECT_TRUE(actual_renderer.isTruncated()); // always true in this test
expected_renderer.setLengthLimit(len_limit);
rrset->toWire(expected_renderer);
if (!expected_renderer.isTruncated() && dnssec_ok && rrsig_rrset) {
rrsig_rrset->toWire(expected_renderer);
}
matchWireData(expected_renderer.getData(), expected_renderer.getLength(),
actual_renderer.getData(), actual_renderer.getLength());
}
TEST_F(TreeNodeRRsetTest, toWireTruncated) {
MessageRenderer expected_renderer, actual_renderer;
// Set the truncation limit to name len + 14 bytes of fixed data for A RR
// (type, class, TTL, rdlen, and 4-byte IPv4 address). Then we can only
// render just one RR, without any garbage trailing data.
checkTruncationResult(expected_renderer, actual_renderer,
TreeNodeRRset(rrclass_, www_node_, a_rdataset_,
true),
a_rrset_, rrsig_rrset_, true,
www_name_.getLength() + 14,
1);
// The first normal RRs should fit in the renderer (the name will be
// fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
checkTruncationResult(expected_renderer, actual_renderer,
TreeNodeRRset(rrclass_, www_node_, a_rdataset_,
true),
a_rrset_, rrsig_rrset_, true,
www_name_.getLength() + 14 + 2 + 14, 2);
}
void
......
......@@ -88,6 +88,40 @@ renderData(const void* data, size_t data_len,
{
renderer->writeData(data, data_len);
}
// Common code logic for rendering a single (either main or RRSIG) RRset.
size_t
writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
const LabelSequence& name_labels, const RRType& rrtype,
const RRClass& rrclass, const void* ttl_data,
RdataReader& reader, bool (RdataReader::* rdata_iterate_fn)())
{
for (size_t i = 0; i < rr_count; ++i) {
const size_t pos0 = renderer.getLength();
// Name, type, class, TTL
renderer.writeName(name_labels, true);
rrtype.toWire(renderer);
rrclass.toWire(renderer);
renderer.writeData(ttl_data, sizeof(uint32_t));
// RDLEN and RDATA
const size_t pos = renderer.getLength();
renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
const bool rendered = (reader.*rdata_iterate_fn)();
assert(rendered == true);
renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
pos);
// Check if truncation would happen
if (renderer.getLength() > renderer.getLengthLimit()) {
renderer.trim(renderer.getLength() - pos0);
renderer.setTruncated();
return (i);
}
}
return (rr_count);
}
}
unsigned int
......@@ -101,47 +135,24 @@ TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
const LabelSequence node_labels = node_->getAbsoluteLabels(labels_buf);
size_t i = 0;
for (; i < rdataset_->getRdataCount(); ++i) {
//const size_t pos0 = output.getLength();
renderer.writeName(node_labels, true);
rdataset_->type.toWire(renderer);
rrclass_.toWire(renderer);
renderer.writeData(rdataset_->getTTLData(), sizeof(uint32_t));
const size_t pos = renderer.getLength();
renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
const bool rendered = reader.iterateRdata();
assert(rendered == true);
renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
pos);
// for truncation processing
// Render the main (non RRSIG) RRs
const size_t rendered_rdata_count =
writeRRs(renderer, rdataset_->getRdataCount(), node_labels,
rdataset_->type, rrclass_, rdataset_->getTTLData(), reader,
&RdataReader::iterateRdata);
if (renderer.isTruncated()) {
return (rendered_rdata_count);
}
const bool rendered = reader.iterateRdata();
assert(rendered == false); // we should've reached the end
size_t j = 0;
if (dnssec_ok_) {
for (; j < rrsig_count_; ++j) {
// TBD: truncation consideration
renderer.writeName(node_labels, true);
RRType::RRSIG().toWire(renderer);
rrclass_.toWire(renderer);
renderer.writeData(rdataset_->getTTLData(), sizeof(uint32_t));
const size_t pos = renderer.getLength();
renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
assert(reader.iterateSingleSig() == true);
renderer.writeUint16At(renderer.getLength() - pos -
sizeof(uint16_t), pos);
}
assert(reader.iterateSingleSig() == false);
}
// Render any RRSIGs, if we supposed to do so
const size_t rendered_rrsig_count = dnssec_ok_ ?
writeRRs(renderer, rrsig_count_, node_labels, RRType::RRSIG(),
rrclass_, rdataset_->getTTLData(), reader,
&RdataReader::iterateSingleSig) : 0;
return (i + j);
return (rendered_rdata_count + rendered_rrsig_count);
}
unsigned int
......
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