Commit 2002e7a1 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'trac1396'

parents f0a7b4ec 1e762fa3
......@@ -143,11 +143,11 @@ def format_addrinfo(addrinfo):
raise TypeError("addrinfo argument to format_addrinfo() does not "
"appear to be consisting of (family, socktype, (addr, port))")
# This function is not inlined as it is replaced with a mock function
# during testing.
def get_rrset_len(rrset):
"""Returns the wire length of the given RRset"""
bytes = bytearray()
rrset.to_wire(bytes)
return len(bytes)
return rrset.get_length()
def get_soa_serial(soa_rdata):
'''Extract the serial field of an SOA RDATA and returns it as an Serial object.
......
......@@ -100,6 +100,19 @@ TreeNodeRRset::toText() const {
}
namespace {
void
sizeupName(const LabelSequence& name_labels, RdataNameAttributes,
size_t* length)
{
*length += name_labels.getDataLength();
}
void
sizeupData(const void*, size_t data_len, size_t* length)
{
*length += data_len;
}
void
renderName(const LabelSequence& name_labels, RdataNameAttributes attr,
AbstractMessageRenderer* renderer)
......@@ -114,6 +127,35 @@ renderData(const void* data, size_t data_len,
renderer->writeData(data, data_len);
}
// Helper for calculating wire data length of a single (etiher main or
// RRSIG) RRset.
uint16_t
getLengthHelper(size_t* rlength, size_t rr_count, uint16_t name_labels_size,
RdataReader& reader, bool (RdataReader::* rdata_iterate_fn)())
{
uint16_t length = 0;
for (size_t i = 0; i < rr_count; ++i) {
size_t rrlen = 0;
rrlen += name_labels_size;
rrlen += 2; // TYPE field
rrlen += 2; // CLASS field
rrlen += 4; // TTL field
rrlen += 2; // RDLENGTH field
*rlength = 0;
const bool rendered = (reader.*rdata_iterate_fn)();
assert(rendered == true);
rrlen += *rlength;
assert(length + rrlen < 65536);
length += rrlen;
}
return (length);
}
// Common code logic for rendering a single (either main or RRSIG) RRset.
size_t
writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
......@@ -149,6 +191,39 @@ writeRRs(AbstractMessageRenderer& renderer, size_t rr_count,
}
}
uint16_t
TreeNodeRRset::getLength() const {
size_t rlength = 0;
RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
rdataset_->getRdataCount(), rrsig_count_,
boost::bind(sizeupName, _1, _2, &rlength),
boost::bind(sizeupData, _1, _2, &rlength));
// Get the owner name of the RRset in the form of LabelSequence.
uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
const LabelSequence name_labels = getOwnerLabels(labels_buf);
const uint16_t name_labels_size = name_labels.getDataLength();
// Find the length of the main (non RRSIG) RRs
const uint16_t rrset_length =
getLengthHelper(&rlength, rdataset_->getRdataCount(), name_labels_size,
reader, &RdataReader::iterateRdata);
rlength = 0;
const bool rendered = reader.iterateRdata();
assert(rendered == false); // we should've reached the end
// Find the length of any RRSIGs, if we supposed to do so
const uint16_t rrsig_length = dnssec_ok_ ?
getLengthHelper(&rlength, rrsig_count_, name_labels_size,
reader, &RdataReader::iterateSingleSig) : 0;
// the uint16_ts are promoted to ints during addition below, so it
// won't overflow a 16-bit register.
assert(rrset_length + rrsig_length < 65536);
return (rrset_length + rrsig_length);
}
unsigned int
TreeNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
RdataReader reader(rrclass_, rdataset_->type, rdataset_->getDataBuf(),
......
......@@ -155,7 +155,7 @@ public:
node_(node), rdataset_(rdataset),
rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname)),
ttl_data_(rdataset->getTTLData()), ttl_(NULL)
ttl_data_(rdataset->getTTLData()), ttl_(NULL)
{}
virtual ~TreeNodeRRset() {
......@@ -168,6 +168,8 @@ public:
return (rdataset_->getRdataCount());
}
virtual uint16_t getLength() const;
virtual const dns::Name& getName() const;
virtual const dns::RRClass& getClass() const {
return (rrclass_);
......
......@@ -333,6 +333,37 @@ checkToWireResult(OutputType& expected_output, OutputType& actual_output,
actual_output.getData(), actual_output.getLength());
}
TEST_F(TreeNodeRRsetTest, getLength) {
// A RR
// www.example.com = 1 + 3 + 1 + 7 + 1 + 3 + 1 = 17 octets
// TYPE field = 2 octets
// CLASS field = 2 octets
// TTL field = 4 octets
// RDLENGTH field = 2 octets
// A RDATA = 4 octets
// Total = 17 + 2 + 2 + 4 + 2 + 4 = 31 octets
// RRSIG RR
// www.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 17 octets
// TYPE field = 2 octets
// CLASS field = 2 octets
// TTL field = 4 octets
// RDLENGTH field = 2 octets
// RRSIG RDATA = 18 + [1 + 7 + 1 + 3 + 1 (example.com)] + 3 (base64
// decode of FAKE) octets
// Total = 17 + 2 + 2 + 4 + 2 + 34 = 61 octets
// 1. with RRSIG, DNSSEC not OK
// ` 2 A RRs + 0 RRSIG RRs
const TreeNodeRRset rrset1(rrclass_, www_node_, a_rdataset_, false);
EXPECT_EQ(31 + 31, rrset1.getLength());
// 2. with RRSIG, DNSSEC OK
// ` 2 A RRs + 1 RRSIG RR
const TreeNodeRRset rrset2(rrclass_, www_node_, a_rdataset_, true);
EXPECT_EQ(31 + 31 + 61, rrset2.getLength());
}
TEST_F(TreeNodeRRsetTest, toWire) {
MessageRenderer expected_renderer, actual_renderer;
OutputBuffer expected_buffer(0), actual_buffer(0);
......
......@@ -53,6 +53,7 @@ int RRset_init(s_RRset* self, PyObject* args);
void RRset_destroy(s_RRset* self);
PyObject* RRset_getRdataCount(PyObject* self, PyObject* args);
PyObject* RRset_getLength(PyObject* self, PyObject* args);
PyObject* RRset_getName(PyObject* self, PyObject* args);
PyObject* RRset_getClass(PyObject* self, PyObject* args);
PyObject* RRset_getType(PyObject* self, PyObject* args);
......@@ -70,6 +71,8 @@ PyObject* RRset_removeRRsig(PyObject* self, PyObject* args);
PyMethodDef RRset_methods[] = {
{ "get_rdata_count", RRset_getRdataCount, METH_NOARGS,
"Returns the number of rdata fields." },
{ "get_length", RRset_getLength, METH_NOARGS,
"Returns the wire format length of the RRset." },
{ "get_name", RRset_getName, METH_NOARGS,
"Returns the name of the RRset, as a Name object." },
{ "get_class", RRset_getClass, METH_NOARGS,
......@@ -135,6 +138,18 @@ RRset_getRdataCount(PyObject* self, PyObject*) {
getRdataCount()));
}
PyObject*
RRset_getLength(PyObject* self, PyObject*) {
try {
return (Py_BuildValue("H", static_cast<const s_RRset*>(self)->cppobj->
getLength()));
} catch (const EmptyRRset& ers) {
PyErr_Clear();
PyErr_SetString(po_EmptyRRset, ers.what());
return (NULL);
}
}
PyObject*
RRset_getName(PyObject* self, PyObject*) {
try {
......
......@@ -45,6 +45,24 @@ class TestModuleSpec(unittest.TestCase):
self.assertEqual(i, self.rrset_a_empty.get_rdata_count())
self.rrset_a_empty.add_rdata(Rdata(RRType("A"), RRClass("IN"), "192.0.2.1"))
def test_get_length(self):
# Empty RRset should throw
self.assertRaises(EmptyRRset, self.rrset_a_empty.get_length);
# Unless it is type ANY or NONE:
# test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
# TYPE field = 2 octets
# CLASS field = 2 octets
# TTL field = 4 octets
# RDLENGTH field = 2 octets
# Total = 18 + 2 + 2 + 4 + 2 = 28 octets
self.assertEqual(28, self.rrset_any_a_empty.get_length())
# Single A RR:
# 28 octets (above) + 4 octets (A RDATA) = 32 octets
# With 2 A RRs:
self.assertEqual(32 + 32, self.rrset_a.get_length())
def test_get_name(self):
self.assertEqual(self.test_name, self.rrset_a.get_name())
self.assertEqual(self.test_domain, self.rrset_ns.get_name())
......
......@@ -46,6 +46,15 @@ namespace isc {
namespace dns {
namespace rdata {
uint16_t
Rdata::getLength() const {
OutputBuffer obuffer(0);
toWire(obuffer);
return (obuffer.getLength());
}
// XXX: we need to specify std:: for string to help doxygen match the
// function signature with that given in the header file.
RdataPtr
......
......@@ -221,6 +221,21 @@ public:
/// \return > 0 if \c this would be sorted after \c other.
virtual int compare(const Rdata& other) const = 0;
//@}
/// \brief Get the wire format length of an Rdata.
///
/// IMPLEMENTATION NOTE: Currently this base class implementation is
/// non-optimal as it renders the wire data to a buffer and returns
/// the buffer's length. What would perform better is to add
/// implementations of \c getLength() method to every RDATA
/// type. This is why this method is virtual. Once all Rdata types
/// have \c getLength() implementations, this base class
/// implementation must be removed and the method should become a
/// pure interface.
///
/// \return The length of the wire format representation of the
/// RDATA.
virtual uint16_t getLength() const;
};
namespace generic {
......
......@@ -289,6 +289,50 @@ BasicRRset::toText() const {
return (AbstractRRset::toText());
}
uint16_t
BasicRRset::getLength() const {
uint16_t length = 0;
RdataIteratorPtr it = getRdataIterator();
if (it->isLast()) {
// empty rrsets are only allowed for classes ANY and NONE
if (getClass() != RRClass::ANY() &&
getClass() != RRClass::NONE()) {
isc_throw(EmptyRRset, "getLength() is attempted for an empty RRset");
}
// For an empty RRset, write the name, type, class and TTL once,
// followed by empty rdata.
length += getName().getLength();
length += 2; // TYPE field
length += 2; // CLASS field
length += 4; // TTL field
length += 2; // RDLENGTH field (=0 in wire format)
return (length);
}
do {
// This is a size_t as some of the following additions may
// overflow due to a programming mistake somewhere.
size_t rrlen = 0;
rrlen += getName().getLength();
rrlen += 2; // TYPE field
rrlen += 2; // CLASS field
rrlen += 4; // TTL field
rrlen += 2; // RDLENGTH field
rrlen += it->getCurrent().getLength();
assert(length + rrlen < 65536);
length += rrlen;
it->next();
} while (!it->isLast());
return (length);
}
unsigned int
BasicRRset::toWire(OutputBuffer& buffer) const {
return (AbstractRRset::toWire(buffer));
......@@ -322,6 +366,21 @@ RRset::getRRsigDataCount() const {
}
}
uint16_t
RRset::getLength() const {
uint16_t length = BasicRRset::getLength();
if (rrsig_) {
const uint16_t rrsigs_length = rrsig_->getLength();
// the uint16_ts are promoted to ints during addition below, so
// it won't overflow a 16-bit register.
assert(length + rrsigs_length < 65536);
length += rrsigs_length;
}
return (length);
}
unsigned int
RRset::toWire(OutputBuffer& buffer) const {
unsigned int rrs_written = BasicRRset::toWire(buffer);
......
......@@ -206,6 +206,20 @@ public:
/// \return The number of \c Rdata objects contained.
virtual unsigned int getRdataCount() const = 0;
/// \brief Get the wire format length of the \c AbstractRRset.
///
/// This method returns the wire format length of the
/// \c AbstractRRset, which is calculated by summing the individual
/// lengths of the various fields that make up each RR.
///
/// NOTE: When including name lengths, the allocation for
/// uncompressed name wire format representation is used.
///
/// \return The length of the wire format representation of the
/// \c AbstractRRset.
/// \throw \c EmptyRRset if the \c AbstractRRset is empty.
virtual uint16_t getLength() const = 0;
/// \brief Returns the owner name of the \c RRset.
///
/// \return A reference to a \c Name class object corresponding to the
......@@ -649,6 +663,13 @@ public:
/// \return The number of \c Rdata objects contained.
virtual unsigned int getRdataCount() const;
/// \brief Get the wire format length of the \c BasicRRset.
///
/// \return The length of the wire format representation of the
/// \c BasicRRset.
/// \throw \c EmptyRRset if the \c BasicRRset is empty.
virtual uint16_t getLength() const;
/// \brief Returns the owner name of the \c RRset.
///
/// This method never throws an exception.
......@@ -820,6 +841,13 @@ public:
virtual ~RRset();
/// \brief Get the wire format length of the \c RRset.
///
/// \return The length of the wire format representation of the
/// \c RRset.
/// \throw \c EmptyRRset if the \c RRset is empty.
virtual uint16_t getLength() const;
/// \brief Render the RRset in the wire format with name compression and
/// truncation handling.
///
......
......@@ -211,6 +211,14 @@ TEST_F(RdataTest, createRdataWithLexer) {
"file does not end with newline");
}
TEST_F(RdataTest, getLength) {
const in::AAAA aaaa_rdata("2001:db8::1");
EXPECT_EQ(16, aaaa_rdata.getLength());
const generic::TXT txt_rdata("Hello World");
EXPECT_EQ(12, txt_rdata.getLength());
}
}
}
}
......
......@@ -212,6 +212,30 @@ TEST_F(RRsetTest, toText) {
rrset_none_a_empty.toText());
}
TEST_F(RRsetTest, getLength) {
// Empty RRset should throw
EXPECT_THROW(rrset_a_empty.getLength(), EmptyRRset);
// Unless it is type ANY or NONE:
// test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
// TYPE field = 2 octets
// CLASS field = 2 octets
// TTL field = 4 octets
// RDLENGTH field = 2 octets
// Total = 18 + 2 + 2 + 4 + 2 = 28 octets
EXPECT_EQ(28, rrset_any_a_empty.getLength());
EXPECT_EQ(28, rrset_none_a_empty.getLength());
// RRset with single RDATA
// 28 (above) + 4 octets (A RDATA) = 32 octets
rrset_a_empty.addRdata(in::A("192.0.2.1"));
EXPECT_EQ(32, rrset_a_empty.getLength());
// 2 A RRs
rrset_a_empty.addRdata(in::A("192.0.2.2"));
EXPECT_EQ(32 + 32, rrset_a_empty.getLength());
}
TEST_F(RRsetTest, toWireBuffer) {
rrset_a.toWire(buffer);
......@@ -373,4 +397,38 @@ TEST_F(RRsetRRSIGTest, toText) {
"20100322084538 20100220084538 1 example.com. FAKEFAKEFAKEFAKE\n",
rrset_aaaa->toText());
}
TEST_F(RRsetRRSIGTest, getLength) {
// A RR
// test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
// TYPE field = 2 octets
// CLASS field = 2 octets
// TTL field = 4 octets
// RDLENGTH field = 2 octets
// A RDATA = 4 octets
// Total = 18 + 2 + 2 + 4 + 2 + 4 = 32 octets
// 2 A RRs
EXPECT_EQ(32 + 32, rrset_a->getLength());
// RRSIG
// test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets
// TYPE field = 2 octets
// CLASS field = 2 octets
// TTL field = 4 octets
// RDLENGTH field = 2 octets
// RRSIG RDATA = 40 octets
// Total = 18 + 2 + 2 + 4 + 2 + 40 = 68 octets
RRsetPtr my_rrsig(new RRset(test_name, RRClass::IN(),
RRType::RRSIG(), RRTTL(3600)));
my_rrsig->addRdata(generic::RRSIG("A 4 3 3600 "
"20000101000000 20000201000000 "
"12345 example.com. FAKEFAKEFAKE"));
EXPECT_EQ(68, my_rrsig->getLength());
// RRset with attached RRSIG
rrset_a->addRRsig(my_rrsig);
EXPECT_EQ(32 + 32 + 68, rrset_a->getLength());
}
}
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