Browse Source

Merge branch 'trac2000'

Mukund Sivaraman 11 years ago
parent
commit
ff7fa9a202

+ 158 - 24
src/lib/dns/rdata/generic/opt_41.cc

@@ -14,25 +14,68 @@
 
 #include <config.h>
 
-#include <string>
-
 #include <util/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 
+#include <boost/foreach.hpp>
+
+#include <string>
+#include <string.h>
+
 using namespace std;
 using namespace isc::util;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
+/// \brief Constructor.
+OPT::PseudoRR::PseudoRR(uint16_t code,
+                        boost::shared_ptr<std::vector<uint8_t> >& data) :
+    code_(code),
+    data_(data)
+{
+}
+
+uint16_t
+OPT::PseudoRR::getCode() const {
+    return (code_);
+}
+
+const uint8_t*
+OPT::PseudoRR::getData() const {
+    return (&(*data_)[0]);
+}
+
+uint16_t
+OPT::PseudoRR::getLength() const {
+    return (data_->size());
+}
+
+struct OPTImpl {
+    OPTImpl() :
+        rdlength_(0)
+    {}
+
+    uint16_t rdlength_;
+    std::vector<OPT::PseudoRR> pseudo_rrs_;
+};
+
+/// \brief Default constructor.
+OPT::OPT() :
+    impl_(new OPTImpl)
+{
+}
+
 /// \brief Constructor from string.
 ///
 /// This constructor cannot be used, and always throws an exception.
 ///
 /// \throw InvalidRdataText OPT RR cannot be constructed from text.
-OPT::OPT(const std::string&) {
+OPT::OPT(const std::string&) :
+    impl_(NULL)
+{
     isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text");
 }
 
@@ -42,50 +85,141 @@ OPT::OPT(const std::string&) {
 ///
 /// \throw InvalidRdataText OPT RR cannot be constructed from text.
 OPT::OPT(MasterLexer&, const Name*,
-       MasterLoader::Options, MasterLoaderCallbacks&)
+         MasterLoader::Options, MasterLoaderCallbacks&) :
+    impl_(NULL)
 {
     isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text");
 }
 
-OPT::OPT(InputBuffer& buffer, size_t rdata_len) {
-    // setPosition() will throw against a short buffer anyway, but it's safer
-    // to check it explicitly here.
-    if (buffer.getLength() - buffer.getPosition() < rdata_len) {
-        isc_throw(InvalidRdataLength, "RDLEN of OPT is too large");
+OPT::OPT(InputBuffer& buffer, size_t rdata_len) :
+    impl_(NULL)
+{
+    std::auto_ptr<OPTImpl> impl_ptr(new OPTImpl);
+
+    while (true) {
+        if (rdata_len == 0) {
+            break;
+        }
+
+        if (rdata_len < 4) {
+            isc_throw(InvalidRdataLength,
+                      "Pseudo OPT RR record too short: "
+                      << rdata_len << " bytes");
+        }
+
+        const uint16_t option_code = buffer.readUint16();
+        const uint16_t option_length = buffer.readUint16();
+        rdata_len -= 4;
+
+        if (static_cast<uint16_t>(impl_ptr->rdlength_ + option_length) <
+            impl_ptr->rdlength_)
+        {
+            isc_throw(InvalidRdataText,
+                      "Option length " << option_length
+                      << " would overflow OPT RR RDLEN (currently "
+                      << impl_ptr->rdlength_ << ").");
+        }
+
+        if (rdata_len < option_length) {
+            isc_throw(InvalidRdataLength, "Corrupt pseudo OPT RR record");
+        }
+
+        boost::shared_ptr<std::vector<uint8_t> >
+            option_data(new std::vector<uint8_t>(option_length));
+        buffer.readData(&(*option_data)[0], option_length);
+        impl_ptr->pseudo_rrs_.push_back(PseudoRR(option_code, option_data));
+        impl_ptr->rdlength_ += option_length;
+        rdata_len -= option_length;
+    }
+
+    impl_ = impl_ptr.release();
+}
+
+OPT::OPT(const OPT& other) :
+    Rdata(), impl_(new OPTImpl(*other.impl_))
+{
+}
+
+OPT&
+OPT::operator=(const OPT& source) {
+    if (this == &source) {
+        return (*this);
     }
 
-    // This simple implementation ignores any options
-    buffer.setPosition(buffer.getPosition() + rdata_len);
+    OPTImpl* newimpl = new OPTImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
 }
 
-OPT::OPT(const OPT&) : Rdata() {
-    // there's nothing to copy in this simple implementation.
+OPT::~OPT() {
+    delete impl_;
 }
 
 std::string
 OPT::toText() const {
-    // OPT records do not have a text format.
-    return ("");
+    isc_throw(isc::InvalidOperation,
+              "OPT RRs do not have a presentation format");
 }
 
 void
-OPT::toWire(OutputBuffer&) const {
-    // nothing to do, as this simple version doesn't support any options.
+OPT::toWire(OutputBuffer& buffer) const {
+    BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) {
+        buffer.writeUint16(pseudo_rr.getCode());
+        const uint16_t length = pseudo_rr.getLength();
+        buffer.writeUint16(length);
+        if (length > 0) {
+            buffer.writeData(pseudo_rr.getData(), length);
+        }
+    }
 }
 
 void
-OPT::toWire(AbstractMessageRenderer&) const {
-    // nothing to do, as this simple version doesn't support any options.
+OPT::toWire(AbstractMessageRenderer& renderer) const {
+    BOOST_FOREACH(const PseudoRR& pseudo_rr, impl_->pseudo_rrs_) {
+        renderer.writeUint16(pseudo_rr.getCode());
+        const uint16_t length = pseudo_rr.getLength();
+        renderer.writeUint16(length);
+        if (length > 0) {
+            renderer.writeData(pseudo_rr.getData(), length);
+        }
+    }
 }
 
 int
-OPT::compare(const Rdata& other) const {
-    //const OPT& other_opt = dynamic_cast<const OPT&>(other);
-    // right now we don't need other_opt:
-    static_cast<void>(dynamic_cast<const OPT&>(other));
-
+OPT::compare(const Rdata&) const {
+    isc_throw(isc::InvalidOperation,
+              "It is meaningless to compare a set of OPT pseudo RRs; "
+              "they have unspecified order");
     return (0);
 }
 
+void
+OPT::appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length) {
+    // See if it overflows 16-bit length field. We only worry about the
+    // pseudo-RR length here, not the whole message length (which should
+    // be checked and enforced elsewhere).
+    if (static_cast<uint16_t>(impl_->rdlength_ + length) <
+        impl_->rdlength_)
+    {
+        isc_throw(isc::InvalidParameter,
+                  "Option length " << length
+                  << " would overflow OPT RR RDLEN (currently "
+                  << impl_->rdlength_ << ").");
+    }
+
+    boost::shared_ptr<std::vector<uint8_t> >
+        option_data(new std::vector<uint8_t>(length));
+    std::memcpy(&(*option_data)[0], data, length);
+    impl_->pseudo_rrs_.push_back(PseudoRR(code, option_data));
+    impl_->rdlength_ += length;
+}
+
+const std::vector<OPT::PseudoRR>&
+OPT::getPseudoRRs() const {
+    return (impl_->pseudo_rrs_);
+}
+
 // END_RDATA_NAMESPACE
 // END_ISC_NAMESPACE

+ 55 - 2
src/lib/dns/rdata/generic/opt_41.h

@@ -18,6 +18,10 @@
 
 #include <dns/rdata.h>
 
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+
 // BEGIN_ISC_NAMESPACE
 
 // BEGIN_COMMON_DECLARATIONS
@@ -25,15 +29,64 @@
 
 // BEGIN_RDATA_NAMESPACE
 
+struct OPTImpl;
+
 class OPT : public Rdata {
 public:
     // BEGIN_COMMON_MEMBERS
     // END_COMMON_MEMBERS
 
     // The default constructor makes sense for OPT as it can be empty.
-    OPT() {}
+    OPT();
+    OPT& operator=(const OPT& source);
+    ~OPT();
+
+    /// \brief A class representing a pseudo RR (or option) within an
+    /// OPT RR (see RFC 6891).
+    class PseudoRR {
+    public:
+        /// \brief Constructor.
+        /// \param code The OPTION-CODE field of the pseudo RR.
+        /// \param data The OPTION-DATA field of the pseudo
+        /// RR. OPTION-LENGTH is set to the length of this vector.
+        PseudoRR(uint16_t code,
+                 boost::shared_ptr<std::vector<uint8_t> >& data);
+
+        /// \brief Return the option code of this pseudo RR.
+        uint16_t getCode() const;
+
+        /// \brief Return the option data of this pseudo RR.
+        const uint8_t* getData() const;
+
+        /// \brief Return the length of the option data of this
+        /// pseudo RR.
+        uint16_t getLength() const;
+
+    private:
+        uint16_t code_;
+        boost::shared_ptr<std::vector<uint8_t> > data_;
+    };
+
+    /// \brief Append a pseudo RR (option) in this OPT RR.
+    ///
+    /// \param code The OPTION-CODE field of the pseudo RR.
+    /// \param data The OPTION-DATA field of the pseudo RR.
+    /// \param length The size of the \c data argument. OPTION-LENGTH is
+    /// set to this size.
+    /// \throw \c isc::InvalidParameter if this pseudo RR would cause
+    /// the OPT RDATA to overflow its RDLENGTH.
+    void appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length);
+
+    /// \brief Return a vector of the pseudo RRs (options) in this
+    /// OPT RR.
+    ///
+    /// Note: The returned reference is only valid during the lifetime
+    /// of this \c generic::OPT object. It should not be used
+    /// afterwards.
+    const std::vector<PseudoRR>& getPseudoRRs() const;
+
 private:
-    // RR-type specific members are here.
+    OPTImpl* impl_;
 };
 
 // END_RDATA_NAMESPACE

+ 129 - 14
src/lib/dns/tests/rdata_opt_unittest.cc

@@ -23,19 +23,28 @@
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/rdata_unittest.h>
+#include <util/unittests/wiredata.h>
 
-using isc::UnitTestUtil;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::util;
 using namespace isc::dns::rdata;
+using isc::UnitTestUtil;
+using isc::util::unittests::matchWireData;
 
 namespace {
 class Rdata_OPT_Test : public RdataTest {
     // there's nothing to specialize
 };
 
-const generic::OPT rdata_opt;
+const uint8_t rdata_opt_wiredata[] = {
+    // Option code
+    0x00, 0x2a,
+    // Option length
+    0x00, 0x03,
+    // Option data
+    0x00, 0x01, 0x02
+};
 
 TEST_F(Rdata_OPT_Test, createFromText) {
     // OPT RR cannot be created from text.
@@ -46,14 +55,28 @@ TEST_F(Rdata_OPT_Test, createFromWire) {
     // Valid cases: in the simple implementation with no supported options,
     // we can only check these don't throw.
     EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass("CLASS4096"),
-                                         "rdata_opt_fromWire"));
+                                         "rdata_opt_fromWire1"));
     EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::CH(),
-                                         "rdata_opt_fromWire", 2));
+                                         "rdata_opt_fromWire1", 2));
 
-    // short buffer case.
+    // Short RDLEN. This throws InvalidRdataLength even if subsequent
+    // pseudo RRs cause RDLEN size to be exhausted.
     EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(),
-                                      "rdata_opt_fromWire", 11),
+                                      "rdata_opt_fromWire2"),
                  InvalidRdataLength);
+    EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(),
+                                      "rdata_opt_fromWire3"),
+                 InvalidRdataLength);
+    // Option lengths can add up and overflow RDLEN. Unlikely when
+    // parsed from wire data, but we'll check for it anyway.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(),
+                                      "rdata_opt_fromWire4"),
+                 InvalidRdataText);
+
+    // short buffer case.
+    EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(),
+                                      "rdata_opt_fromWire1", 11),
+                 InvalidBufferPosition);
 }
 
 TEST_F(Rdata_OPT_Test, createFromLexer) {
@@ -64,26 +87,118 @@ TEST_F(Rdata_OPT_Test, createFromLexer) {
 }
 
 TEST_F(Rdata_OPT_Test, toWireBuffer) {
+    const generic::OPT rdata_opt =
+        dynamic_cast<const generic::OPT&>
+        (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"),
+                               "rdata_opt_fromWire1", 2));
+
+    obuffer.clear();
     rdata_opt.toWire(obuffer);
-    EXPECT_EQ(0, obuffer.getLength());
+
+    matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata),
+                  obuffer.getData(), obuffer.getLength());
 }
 
 TEST_F(Rdata_OPT_Test, toWireRenderer) {
+    const generic::OPT rdata_opt =
+        dynamic_cast<const generic::OPT&>
+        (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"),
+                               "rdata_opt_fromWire1", 2));
+
+    renderer.clear();
     rdata_opt.toWire(renderer);
-    EXPECT_EQ(0, obuffer.getLength());
+
+    matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata),
+                  renderer.getData(), renderer.getLength());
 }
 
 TEST_F(Rdata_OPT_Test, toText) {
-    EXPECT_EQ("", rdata_opt.toText());
+    // empty OPT
+    const generic::OPT rdata_opt;
+
+    EXPECT_THROW(rdata_opt.toText(),
+                 isc::InvalidOperation);
 }
 
 TEST_F(Rdata_OPT_Test, compare) {
-    // This simple implementation always returns "true"
-    EXPECT_EQ(0, rdata_opt.compare(
+    // empty OPT
+    const generic::OPT rdata_opt;
+
+    EXPECT_THROW(rdata_opt.compare(
                   *rdataFactoryFromFile(RRType::OPT(), RRClass::CH(),
-                                        "rdata_opt_fromWire", 2)));
+                                        "rdata_opt_fromWire1", 2)),
+                 isc::InvalidOperation);
+
+    // comparison attempt between incompatible RR types also results in
+    // isc::InvalidOperation.
+    EXPECT_THROW(rdata_opt.compare(*RdataTest::rdata_nomatch),
+                 isc::InvalidOperation);
+}
+
+TEST_F(Rdata_OPT_Test, appendPseudoRR) {
+    generic::OPT rdata_opt;
+
+    // Append empty option data
+    rdata_opt.appendPseudoRR(0x0042, NULL, 0);
+
+    // Append simple option data
+    const uint8_t option_data[] = {'H', 'e', 'l', 'l', 'o'};
+    rdata_opt.appendPseudoRR(0x0043, option_data, sizeof(option_data));
+
+    // Duplicate option codes are okay.
+    rdata_opt.appendPseudoRR(0x0042, option_data, sizeof(option_data));
+
+    // When option length may overflow RDLEN, append should throw.
+    const std::vector<uint8_t> buffer((1 << 16) - 1);
+    EXPECT_THROW(rdata_opt.appendPseudoRR(0x0044, &buffer[0], buffer.size()),
+                 isc::InvalidParameter);
+
+    const uint8_t rdata_opt_wiredata2[] = {
+        // OPTION #1
+        // ` Option code
+        0x00, 0x42,
+        // ` Option length
+        0x00, 0x00,
+
+        // OPTION #2
+        // ` Option code
+        0x00, 0x43,
+        // ` Option length
+        0x00, 0x05,
+        // ` Option data
+        'H', 'e', 'l', 'l', 'o',
+
+        // OPTION #3
+        // ` Option code
+        0x00, 0x42,
+        // ` Option length
+        0x00, 0x05,
+        // ` Option data
+        'H', 'e', 'l', 'l', 'o'
+    };
+
+    obuffer.clear();
+    rdata_opt.toWire(obuffer);
+
+    matchWireData(rdata_opt_wiredata2, sizeof(rdata_opt_wiredata2),
+                  obuffer.getData(), obuffer.getLength());
+}
+
+TEST_F(Rdata_OPT_Test, getPseudoRRs) {
+    const generic::OPT rdf =
+        dynamic_cast<const generic::OPT&>
+        (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"),
+                               "rdata_opt_fromWire1", 2));
+
+    const std::vector<generic::OPT::PseudoRR>& rrs = rdf.getPseudoRRs();
+    ASSERT_FALSE(rrs.empty());
+    EXPECT_EQ(1, rrs.size());
+    EXPECT_EQ(0x2a, rrs.at(0).getCode());
+    EXPECT_EQ(3, rrs.at(0).getLength());
 
-    // comparison attempt between incompatible RR types should be rejected
-    EXPECT_THROW(rdata_opt.compare(*RdataTest::rdata_nomatch), bad_cast);
+    const uint8_t expected_data[] = {0x00, 0x01, 0x02};
+    const uint8_t* actual_data = rrs.at(0).getData();
+    EXPECT_EQ(0, std::memcmp(expected_data, actual_data,
+                             sizeof(expected_data)));
 }
 }

+ 3 - 1
src/lib/dns/tests/testdata/Makefile.am

@@ -132,7 +132,9 @@ EXTRA_DIST += rdata_nsec3_fromWire10.spec rdata_nsec3_fromWire11.spec
 EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec
 EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec
 EXTRA_DIST += rdata_nsec3_fromWire16.spec rdata_nsec3_fromWire17.spec
-EXTRA_DIST += rdata_opt_fromWire rdata_rrsig_fromWire1
+EXTRA_DIST += rdata_opt_fromWire1 rdata_opt_fromWire2
+EXTRA_DIST += rdata_opt_fromWire3 rdata_opt_fromWire4
+EXTRA_DIST += rdata_rrsig_fromWire1
 EXTRA_DIST += rdata_rrsig_fromWire2.spec
 EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec
 EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec

+ 0 - 16
src/lib/dns/tests/testdata/rdata_opt_fromWire

@@ -1,16 +0,0 @@
-#
-# various kinds of OPT RDATA stored in an input buffer
-#
-# empty RDATA (which is okay)
-#
-# 0  1 (bytes)
- 00 00
-#
-# an OPT RR containing an NSID Option
-#      code=3 len=3 ID value (opaque)
-# 2  3  4  5  6  7  8  9 10
- 00 07 00 03 00 03 00 01 02
-#
-# short buffer (this can be tested only at the end of the buffer)
-# 1  2  3  4  5
- 00 04 c0 00 02

+ 15 - 0
src/lib/dns/tests/testdata/rdata_opt_fromWire1

@@ -0,0 +1,15 @@
+# Various kinds of OPT RDATA stored in an input buffer
+#
+# Empty RDATA (which is okay)
+#
+# 0  1 (bytes)
+ 00 00
+#
+# An OPT RR containing an NSID Option
+#      code=3 len=3 ID value (opaque)
+# 2  3  4  5  6  7  8  9 10
+ 00 07 00 2a 00 03 00 01 02
+#
+# Short buffer (this can be tested only at the end of the buffer)
+# 1  2  3  4  5
+ 00 04 c0 00 02

+ 4 - 0
src/lib/dns/tests/testdata/rdata_opt_fromWire2

@@ -0,0 +1,4 @@
+# Short RDATA length
+#
+# OPT RDATA, RDLEN=1
+0001

+ 8 - 0
src/lib/dns/tests/testdata/rdata_opt_fromWire3

@@ -0,0 +1,8 @@
+# Short RDATA length (in second pseudo RR)
+#
+# OPT RDATA, RDLEN=8
+0008
+# Pseudo RR 1 of size 7 (code=3, len=3)
+00 03 00 03 00 01 02
+# Pseudo RR 2 of size 7 exhausts RDLEN (code=4, len=3)
+00 04 00 03 00 01 02

+ 9 - 0
src/lib/dns/tests/testdata/rdata_opt_fromWire4

@@ -0,0 +1,9 @@
+# Sum of option lengths would overflow RDLEN
+#
+# OPT RDATA, RDLEN=14 (0x000e)
+000e
+# Pseudo RR 1 (code=3, len=3)
+00 03 00 03 00 01 02
+# Pseudo RR 2 (code=4, len=65535 overflows RDLEN)
+00 04 ff ff 00 01 02
+# Rest of option data is omitted...