#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");
}
///
/// \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 ((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
}
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 ((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
"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) {
}
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<const generic::OPT&>
+ (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"),
+ "rdata_opt_fromWire", 2));
+
+ const std::vector<generic::OPT::PseudoRR>& 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)));
}
}