From: Mukund Sivaraman Date: Wed, 22 Jan 2014 13:20:32 +0000 (+0530) Subject: [2000] Update OPT RDATA parser to handle EDNS options X-Git-Tag: bind10-1.2.0beta1-release~36^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b6875c7848cf648d45d0448ba8aa3f30a281d3c4;p=thirdparty%2Fkea.git [2000] Update OPT RDATA parser to handle EDNS options --- diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc index 136bdf931a..167c309659 100644 --- a/src/lib/dns/rdata/generic/opt_41.cc +++ b/src/lib/dns/rdata/generic/opt_41.cc @@ -14,25 +14,68 @@ #include -#include - #include #include #include #include +#include + +#include +#include + using namespace std; using namespace isc::util; // BEGIN_ISC_NAMESPACE // BEGIN_RDATA_NAMESPACE +/// \brief Constructor. +OPT::PseudoRR::PseudoRR(uint16_t code, + boost::shared_ptr >& 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 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,30 +85,80 @@ 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 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 ((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 > + option_data(new std::vector(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 @@ -79,13 +172,36 @@ OPT::toWire(AbstractMessageRenderer&) const { } int -OPT::compare(const Rdata& other) const { - //const OPT& other_opt = dynamic_cast(other); - // right now we don't need other_opt: - static_cast(dynamic_cast(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 ((impl_->rdlength_ + length) < impl_->rdlength_) { + isc_throw(isc::InvalidParameter, + "Option length " << length + << " would overflow OPT RR RDLEN (currently " + << impl_->rdlength_ << ")."); + } + + boost::shared_ptr > + option_data(new std::vector(length)); + std::memcpy(&(*option_data)[0], data, length); + impl_->pseudo_rrs_.push_back(PseudoRR(code, option_data)); + impl_->rdlength_ += length; +} + +const std::vector& +OPT::getPseudoRRs() const { + return (impl_->pseudo_rrs_); +} + // END_RDATA_NAMESPACE // END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/opt_41.h b/src/lib/dns/rdata/generic/opt_41.h index 0cb7043cca..88f2b4c2f6 100644 --- a/src/lib/dns/rdata/generic/opt_41.h +++ b/src/lib/dns/rdata/generic/opt_41.h @@ -18,6 +18,10 @@ #include +#include + +#include + // BEGIN_ISC_NAMESPACE // BEGIN_COMMON_DECLARATIONS @@ -25,15 +29,37 @@ // 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(); + + class PseudoRR { + public: + PseudoRR(uint16_t code, + boost::shared_ptr >& data); + + uint16_t getCode() const; + const uint8_t* getData() const; + uint16_t getLength() const; + + private: + uint16_t code_; + boost::shared_ptr > data_; + }; + + void appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length); + const std::vector& getPseudoRRs() const; + private: - // RR-type specific members are here. + OPTImpl* impl_; }; // END_RDATA_NAMESPACE diff --git a/src/lib/dns/tests/rdata_opt_unittest.cc b/src/lib/dns/tests/rdata_opt_unittest.cc index 20ccfe4994..900c672cf0 100644 --- a/src/lib/dns/tests/rdata_opt_unittest.cc +++ b/src/lib/dns/tests/rdata_opt_unittest.cc @@ -49,11 +49,10 @@ TEST_F(Rdata_OPT_Test, createFromWire) { "rdata_opt_fromWire")); EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::CH(), "rdata_opt_fromWire", 2)); - // short buffer case. EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), "rdata_opt_fromWire", 11), - InvalidRdataLength); + InvalidBufferPosition); } TEST_F(Rdata_OPT_Test, createFromLexer) { @@ -74,16 +73,42 @@ TEST_F(Rdata_OPT_Test, toWireRenderer) { } TEST_F(Rdata_OPT_Test, toText) { - EXPECT_EQ("", rdata_opt.toText()); + 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( + EXPECT_THROW(rdata_opt.compare( *rdataFactoryFromFile(RRType::OPT(), RRClass::CH(), - "rdata_opt_fromWire", 2))); + "rdata_opt_fromWire", 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, append) { + EXPECT_THROW(rdata_opt.toText(), + isc::InvalidOperation); +} + +TEST_F(Rdata_OPT_Test, getPseudoRRs) { + const generic::OPT rdf = + dynamic_cast + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire", 2)); + + const std::vector& rrs = rdf.getPseudoRRs(); + ASSERT_FALSE(rrs.empty()); + EXPECT_EQ(1, rrs.size()); + EXPECT_EQ(3, 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))); } }