Browse Source

Merge branch 'trac1396'

Mukund Sivaraman 11 years ago
parent
commit
2002e7a128

+ 3 - 3
src/bin/xfrout/xfrout.py.in

@@ -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.

+ 75 - 0
src/lib/datasrc/memory/treenode_rrset.cc

@@ -101,6 +101,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(),

+ 3 - 1
src/lib/datasrc/memory/treenode_rrset.h

@@ -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_);

+ 31 - 0
src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc

@@ -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);

+ 15 - 0
src/lib/dns/python/rrset_python.cc

@@ -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,
@@ -136,6 +139,18 @@ RRset_getRdataCount(PyObject* self, PyObject*) {
 }
 
 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 {
         return (createNameObject(static_cast<const s_RRset*>(self)->cppobj->

+ 18 - 0
src/lib/dns/python/tests/rrset_python_test.py

@@ -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())

+ 9 - 0
src/lib/dns/rdata.cc

@@ -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

+ 15 - 0
src/lib/dns/rdata.h

@@ -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 {

+ 59 - 0
src/lib/dns/rrset.cc

@@ -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);

+ 28 - 0
src/lib/dns/rrset.h

@@ -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.
     ///

+ 8 - 0
src/lib/dns/tests/rdata_unittest.cc

@@ -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());
+}
+
 }
 }
 }

+ 58 - 0
src/lib/dns/tests/rrset_unittest.cc

@@ -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());
+}
 }