Commit 8eb6232b authored by Dima Volodin's avatar Dima Volodin
Browse files

Merge branch 'trac1140'

Conflicts:
	ChangeLog
parents 911cb21f 14693407
297. [func] dvv
Implement the SPF rrtype according to RFC4408.
(Trac #1140, git 146934075349f94ee27f23bf9ff01711b94e369e)
296. [build] jreed
Do not install the unittest libraries. At this time, they
are not useful without source tree (and they may or may
......
......@@ -23,8 +23,24 @@
using namespace std;
using namespace isc::util;
/// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT
/// and SPF types.
///
/// This class implements the basic interfaces inherited by the TXT and SPF
/// classes from the abstract \c rdata::Rdata class, and provides trivial
/// accessors to TXT-like RDATA.
template<class Type, uint16_t typeCode>class TXTLikeImpl {
public:
/// \brief Constructor from wire-format data.
///
/// \param buffer A buffer storing the wire format data.
/// \param rdata_len The length of the RDATA in bytes, normally expected
/// to be the value of the RDLENGTH field of the corresponding RR.
///
/// <b>Exceptions</b>
///
/// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum.
/// \c DNSMessageFORMERR is thrown if the RR is misformed.
TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) {
if (rdata_len > MAX_RDLENGTH) {
isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len);
......@@ -52,6 +68,14 @@ public:
} while (rdata_len > 0);
}
/// \brief Constructor from string.
///
/// <b>Exceptions</b>
///
/// \c CharStringTooLong is thrown if the parameter string length exceeds
/// maximum.
/// \c InvalidRdataText is thrown if the method cannot process the
/// parameter data.
explicit TXTLikeImpl(const std::string& txtstr) {
// TBD: this is a simple, incomplete implementation that only supports
// a single character-string.
......@@ -86,10 +110,17 @@ public:
string_list_.push_back(data);
}
/// \brief The copy constructor.
///
/// Trivial for now, we could've used the default one.
TXTLikeImpl(const TXTLikeImpl& other) :
string_list_(other.string_list_)
{}
/// \brief Render the TXT-like data in the wire format to an OutputBuffer
/// object.
///
/// \param buffer An output buffer to store the wire data.
void
toWire(OutputBuffer& buffer) const {
for (vector<vector<uint8_t> >::const_iterator it =
......@@ -101,6 +132,11 @@ public:
}
}
/// \brief Render the TXT-like data in the wire format to an
/// AbstractMessageRenderer object.
///
/// \param buffer An output AbstractMessageRenderer to send the wire data
/// to.
void
toWire(AbstractMessageRenderer& renderer) const {
for (vector<vector<uint8_t> >::const_iterator it =
......@@ -112,6 +148,9 @@ public:
}
}
/// \brief Convert the TXT-like data to a string.
///
/// \return A \c string object that represents the TXT-like data.
string
toText() const {
string s;
......@@ -134,20 +173,33 @@ public:
return (s);
}
/// \brief Compare two instances of TXT-like RDATA.
///
/// It is up to the caller to make sure that \c other is an object of the
/// same \c TXTLikeImpl class.
///
/// \param other the right-hand operand to compare against.
/// \return < 0 if \c this would be sorted before \c other.
/// \return 0 if \c this is identical to \c other in terms of sorting
/// order.
/// \return > 0 if \c this would be sorted after \c other.
int
compare(const TXTLikeImpl& other) const {
// This implementation is not efficient. Revisit this (TBD).
OutputBuffer this_buffer(0);
toWire(this_buffer);
uint8_t const* const this_data = (uint8_t const*)this_buffer.getData();
size_t this_len = this_buffer.getLength();
OutputBuffer other_buffer(0);
other.toWire(other_buffer);
uint8_t const* const other_data
= (uint8_t const*)other_buffer.getData();
const size_t other_len = other_buffer.getLength();
const size_t cmplen = min(this_len, other_len);
const int cmp = memcmp(this_buffer.getData(), other_buffer.getData(),
cmplen);
const int cmp = memcmp(this_data, other_data, cmplen);
if (cmp != 0) {
return (cmp);
} else {
......
......@@ -30,8 +30,17 @@ using namespace isc::util;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
/// This class implements the basic interfaces inherited from the abstract
/// \c rdata::Rdata class. The semantics of the class is provided by
/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
#include <dns/rdata/generic/detail/txt_like.h>
/// \brief The assignment operator
///
/// It internally allocates a resource, and if it fails a corresponding
/// standard exception will be thrown.
/// This method never throws an exception otherwise.
SPF&
SPF::operator=(const SPF& source) {
if (impl_ == source.impl_) {
......@@ -45,37 +54,72 @@ SPF::operator=(const SPF& source) {
return (*this);
}
/// \brief The destructor
SPF::~SPF() {
delete impl_;
}
/// \brief Constructor from wire-format data.
///
/// It internally allocates a resource, and if it fails a corresponding
/// standard exception will be thrown.
SPF::SPF(InputBuffer& buffer, size_t rdata_len) :
impl_(new SPFImpl(buffer, rdata_len))
{}
/// \brief Constructor from string.
///
/// It internally allocates a resource, and if it fails a corresponding
/// standard exception will be thrown.
SPF::SPF(const std::string& txtstr) :
impl_(new SPFImpl(txtstr))
{}
/// \brief Copy constructor
///
/// It internally allocates a resource, and if it fails a corresponding
/// standard exception will be thrown.
SPF::SPF(const SPF& other) :
Rdata(), impl_(new SPFImpl(*other.impl_))
{}
/// \brief Render the \c SPF in the wire format to a OutputBuffer object
///
/// \return is the return of the corresponding implementation method.
void
SPF::toWire(OutputBuffer& buffer) const {
impl_->toWire(buffer);
}
/// \brief Render the \c SPF in the wire format to an AbstractMessageRenderer
/// object
///
/// \return is the return of the corresponding implementation method.
void
SPF::toWire(AbstractMessageRenderer& renderer) const {
impl_->toWire(renderer);
}
/// \brief Convert the \c SPF to a string.
///
/// \return is the return of the corresponding implementation method.
string
SPF::toText() const {
return (impl_->toText());
}
/// \brief Compare two instances of \c SPF RDATA.
///
/// This method compares \c this and the \c other \c SPF objects.
///
/// This method is expected to be used in a polymorphic way, and the
/// parameter to compare against is therefore of the abstract \c Rdata class.
/// However, comparing two \c Rdata objects of different RR types
/// is meaningless, and \c other must point to a \c SPF object;
/// otherwise, the standard \c bad_cast exception will be thrown.
///
/// \param other the right-hand operand to compare against.
/// \return is the return of the corresponding implementation method.
int
SPF::compare(const Rdata& other) const {
const SPF& other_txt = dynamic_cast<const SPF&>(other);
......
......@@ -30,14 +30,40 @@
template<class Type, uint16_t typeCode> class TXTLikeImpl;
/// \brief \c rdata::SPF class represents the SPF RDATA as defined %in
/// RFC4408.
///
/// This class implements the basic interfaces inherited from the abstract
/// \c rdata::Rdata class. The semantics of the class is provided by
/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
class SPF : public Rdata {
public:
// BEGIN_COMMON_MEMBERS
// END_COMMON_MEMBERS
/// \brief Assignment operator.
///
/// It internally allocates a resource, and if it fails a corresponding
/// standard exception will be thrown.
/// This operator never throws an exception otherwise.
///
/// This operator provides the strong exception guarantee: When an
/// exception is thrown the content of the assignment target will be
/// intact.
SPF& operator=(const SPF& source);
/// \brief The destructor.
~SPF();
///
/// Specialized methods
///
/// \brief Return a reference to the data strings
///
/// This method never throws an exception.
const std::vector<std::vector<uint8_t> >& getString() const;
private:
typedef TXTLikeImpl<SPF, 99> SPFImpl;
SPFImpl* impl_;
......
......@@ -29,7 +29,8 @@ run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
run_unittests_SOURCES += rdatafields_unittest.cc
run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
run_unittests_SOURCES += rdata_txt_unittest.cc rdata_mx_unittest.cc
run_unittests_SOURCES += rdata_txt_like_unittest.cc
run_unittests_SOURCES += rdata_mx_unittest.cc
run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc
run_unittests_SOURCES += rdata_dname_unittest.cc
run_unittests_SOURCES += rdata_afsdb_unittest.cc
......
// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -12,14 +12,11 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// This is the common code for TXT and SPF tests.
#include <util/buffer.h>
#include <dns/exceptions.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
......@@ -31,54 +28,84 @@ using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
template<class T>
class RRTYPE : public RRType {
public:
RRTYPE();
};
template<> RRTYPE<generic::TXT>::RRTYPE() : RRType(RRType::TXT()) {}
template<> RRTYPE<generic::SPF>::RRTYPE() : RRType(RRType::SPF()) {}
namespace {
const generic::TXT rdata_txt("Test String");
const generic::TXT rdata_txt_empty("");
const generic::TXT rdata_txt_quoated("\"Test String\"");
const uint8_t wiredata_txt[] = {
const uint8_t wiredata_txt_like[] = {
sizeof("Test String") - 1,
'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
};
const uint8_t wiredata_nulltxt[] = { 0 };
vector<uint8_t> wiredata_longesttxt(256, 'a');
class Rdata_TXT_Test : public RdataTest {
template<class TXT_LIKE>
class Rdata_TXT_LIKE_Test : public RdataTest {
protected:
Rdata_TXT_Test() {
Rdata_TXT_LIKE_Test() {
wiredata_longesttxt[0] = 255; // adjust length
}
static const TXT_LIKE rdata_txt_like;
static const TXT_LIKE rdata_txt_like_empty;
static const TXT_LIKE rdata_txt_like_quoted;
};
TEST_F(Rdata_TXT_Test, createFromText) {
template<class TXT_LIKE>
const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like("Test String");
template<class TXT_LIKE>
const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_empty("");
template<class TXT_LIKE>
const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_quoted
("\"Test String\"");
// The list of types we want to test.
typedef testing::Types<generic::TXT, generic::SPF> Implementations;
TYPED_TEST_CASE(Rdata_TXT_LIKE_Test, Implementations);
TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) {
// normal case is covered in toWireBuffer.
// surrounding double-quotes shouldn't change the result.
EXPECT_EQ(0, rdata_txt.compare(rdata_txt_quoated));
EXPECT_EQ(0, this->rdata_txt_like.compare(this->rdata_txt_like_quoted));
// Null character-string.
obuffer.clear();
generic::TXT(string("")).toWire(obuffer);
this->obuffer.clear();
TypeParam(string("")).toWire(this->obuffer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
obuffer.getData(), obuffer.getLength(),
this->obuffer.getData(),
this->obuffer.getLength(),
wiredata_nulltxt, sizeof(wiredata_nulltxt));
// Longest possible character-string.
obuffer.clear();
generic::TXT(string(255, 'a')).toWire(obuffer);
this->obuffer.clear();
TypeParam(string(255, 'a')).toWire(this->obuffer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
obuffer.getData(), obuffer.getLength(),
this->obuffer.getData(),
this->obuffer.getLength(),
&wiredata_longesttxt[0], wiredata_longesttxt.size());
// Too long text for a valid character-string.
EXPECT_THROW(generic::TXT(string(256, 'a')), CharStringTooLong);
EXPECT_THROW(TypeParam(string(256, 'a')), CharStringTooLong);
// The escape character makes the double quote a part of character-string,
// so this is invalid input and should be rejected.
EXPECT_THROW(generic::TXT("\"Test String\\\""), InvalidRdataText);
EXPECT_THROW(TypeParam("\"Test String\\\""), InvalidRdataText);
// Terminating double-quote is provided, so this is valid, but in this
// version of implementation we reject escaped characters.
EXPECT_THROW(generic::TXT("\"Test String\\\"\""), InvalidRdataText);
EXPECT_THROW(TypeParam("\"Test String\\\"\""), InvalidRdataText);
}
void
......@@ -100,67 +127,135 @@ makeLargest(vector<uint8_t>& data) {
assert(data.size() == 65535);
}
TEST_F(Rdata_TXT_Test, createFromWire) {
EXPECT_EQ(0, rdata_txt.compare(
*rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
EXPECT_EQ(0, this->rdata_txt_like.compare(
*rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
"rdata_txt_fromWire1")));
// Empty character string
EXPECT_EQ(0, rdata_txt_empty.compare(
*rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
EXPECT_EQ(0, this->rdata_txt_like_empty.compare(
*rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
"rdata_txt_fromWire2.wire")));
// Multiple character strings
obuffer.clear();
rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
"rdata_txt_fromWire3.wire")->toWire(obuffer);
this->obuffer.clear();
rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
"rdata_txt_fromWire3.wire")->toWire(this->obuffer);
// the result should be 'wiredata_txt' repeated twice
vector<uint8_t> expected_data(wiredata_txt, wiredata_txt +
sizeof(wiredata_txt));
expected_data.insert(expected_data.end(), wiredata_txt,
wiredata_txt + sizeof(wiredata_txt));
vector<uint8_t> expected_data(wiredata_txt_like, wiredata_txt_like +
sizeof(wiredata_txt_like));
expected_data.insert(expected_data.end(), wiredata_txt_like,
wiredata_txt_like + sizeof(wiredata_txt_like));
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
obuffer.getData(), obuffer.getLength(),
this->obuffer.getData(),
this->obuffer.getLength(),
&expected_data[0], expected_data.size());
// Largest length of data. There's nothing special, but should be
// constructed safely, and the content should be identical to the original
// data.
vector<uint8_t> largest_txt_data;
makeLargest(largest_txt_data);
InputBuffer ibuffer(&largest_txt_data[0], largest_txt_data.size());
generic::TXT largest_txt(ibuffer, largest_txt_data.size());
obuffer.clear();
largest_txt.toWire(obuffer);
vector<uint8_t> largest_txt_like_data;
makeLargest(largest_txt_like_data);
InputBuffer ibuffer(&largest_txt_like_data[0],
largest_txt_like_data.size());
TypeParam largest_txt_like(ibuffer, largest_txt_like_data.size());
this->obuffer.clear();
largest_txt_like.toWire(this->obuffer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
obuffer.getData(), obuffer.getLength(),
&largest_txt_data[0], largest_txt_data.size());
this->obuffer.getData(),
this->obuffer.getLength(),
&largest_txt_like_data[0],
largest_txt_like_data.size());
// rdlen parameter is out of range. This is a rare event because we'd
// normally call the constructor via a polymorphic wrapper, where the
// length is validated. But this should be checked explicitly.
InputBuffer ibuffer2(&largest_txt_data[0], largest_txt_data.size());
EXPECT_THROW(generic::TXT(ibuffer2, 65536), InvalidRdataLength);
InputBuffer ibuffer2(&largest_txt_like_data[0],
largest_txt_like_data.size());
EXPECT_THROW(TypeParam(ibuffer2, 65536), InvalidRdataLength);
// RDATA is empty, which is invalid for TXT.
EXPECT_THROW(rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
// RDATA is empty, which is invalid for TXT_LIKE.
EXPECT_THROW(rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
"rdata_txt_fromWire4.wire"),
DNSMessageFORMERR);
// character-string length is too large, which could cause overrun.
EXPECT_THROW(rdataFactoryFromFile(RRType("TXT"), RRClass("IN"),
EXPECT_THROW(rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
"rdata_txt_fromWire5.wire"),
DNSMessageFORMERR);
}
TEST_F(Rdata_TXT_Test, toWireBuffer) {
rdata_txt.toWire(obuffer);
TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) {
this->rdata_txt_like.toWire(this->obuffer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
this->obuffer.getData(),
this->obuffer.getLength(),
wiredata_txt_like, sizeof(wiredata_txt_like));
}
TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) {
this->rdata_txt_like.toWire(this->renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
obuffer.getData(), obuffer.getLength(),
wiredata_txt, sizeof(wiredata_txt));
this->renderer.getData(),
this->renderer.getLength(),
wiredata_txt_like, sizeof(wiredata_txt_like));
}
TEST_F(Rdata_TXT_Test, toText) {
EXPECT_EQ("\"Test String\"", rdata_txt.toText());
TYPED_TEST(Rdata_TXT_LIKE_Test, toText) {
EXPECT_EQ("\"Test String\"", this->rdata_txt_like.toText());
}
TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) {
TypeParam rdata1("assignment1");
TypeParam rdata2("assignment2");
rdata1 = rdata2;
EXPECT_EQ(0, rdata2.compare(rdata1));
// Check if the copied data is valid even after the original is deleted
TypeParam* rdata3 = new TypeParam(rdata1);
TypeParam rdata4("assignment3");
rdata4 = *rdata3;
delete rdata3;
EXPECT_EQ(0, rdata4.compare(rdata1));
// Self assignment
rdata2 = rdata2;
EXPECT_EQ(0, rdata2.compare(rdata1));
}
TYPED_TEST(Rdata_TXT_LIKE_Test, compare) {
string const txt1("aaaaaaaa");
string const txt2("aaaaaaaaaa");
string const txt3("bbbbbbbb");
string const txt4(129, 'a');
string const txt5(128, 'b');
EXPECT_EQ(TypeParam(txt1).compare(TypeParam(txt1)), 0);
EXPECT_LT(TypeParam("").compare(TypeParam(txt1)), 0);
EXPECT_GT(TypeParam(txt1).compare(TypeParam("")), 0);
EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt2)), 0);
EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt1)), 0);
EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt3)), 0);
EXPECT_GT(TypeParam(txt3).compare(TypeParam(txt1)), 0);
// we're comparing the data raw, starting at the length octet, so a shorter
// string sorts before a longer one no matter the lexicopraphical order
EXPECT_LT(TypeParam(txt3).compare(TypeParam(txt2)), 0);
EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt3)), 0);
// to make sure the length octet compares unsigned
EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt4)), 0);
EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt1)), 0);
EXPECT_LT(TypeParam(txt5).compare(TypeParam(txt4)), 0);
EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt5)), 0);
// comparison attempt between incompatible RR types should be rejected
EXPECT_THROW(TypeParam(txt1).compare(*this->rdata_nomatch),
bad_cast);
}
}
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