Commit ff7fa9a2 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

Merge branch 'trac2000'

parents 018edab6 c1c4ea8d
......@@ -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
......@@ -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
......
......@@ -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)));
}
}
......@@ -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
......
# Various kinds of OPT RDATA stored in an input buffer
#
# various kinds of OPT RDATA stored in an input buffer
#
# empty RDATA (which is okay)
# Empty RDATA (which is okay)
#
# 0 1 (bytes)
00 00
#
# an OPT RR containing an NSID Option
# 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
00 07 00 2a 00 03 00 01 02
#
# short buffer (this can be tested only at the end of the buffer)
# Short buffer (this can be tested only at the end of the buffer)
# 1 2 3 4 5
00 04 c0 00 02
# Short RDATA length
#
# OPT RDATA, RDLEN=1
0001
# 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
# 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...
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