Commit 0e519f4d authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[trac806] initial implementation of the RP RDATA support with tests

parent 5861ae7c
// 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <string>
#include <sstream>
#include <dns/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
using namespace isc::dns;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
/// \brief Constructor from string.
///
/// \c rp_str must be formatted as follows:
/// \code <mailbox name> <text name>
/// \endcode
/// where both fields must represent a valid domain name.
///
/// \exception InvalidRdataText The number of RDATA fields (must be 2) is
/// incorrect.
/// \exception Other The constructor of the \c Name class will throw if the
/// given name is invalid.
/// \exception std::bad_alloc Memory allocation for names fails.
RP::RP(const std::string& rp_str) :
// We cannot construct both names in the initialization list due to the
// necessary text processing, so we have to initialize them with a dummy
// name and replace them later.
mailbox_(Name::ROOT_NAME()), text_(Name::ROOT_NAME())
{
istringstream iss(rp_str);
string mailbox_str, text_str;
iss >> mailbox_str >> text_str;
// Validation: A valid RP RR must have exactly two fields.
if (iss.bad() || iss.fail()) {
isc_throw(InvalidRdataText, "Invalid RP text: " << rp_str);
}
if (!iss.eof()) {
isc_throw(InvalidRdataText, "Invalid RP text (redundant field): "
<< rp_str);
}
mailbox_ = Name(mailbox_str);
text_ = Name(text_str);
}
/// \brief Constructor from wire-format data.
///
/// This constructor doesn't check the validity of the second parameter (rdata
/// length) for parsing.
/// If necessary, the caller will check consistency.
///
/// \exception std::bad_alloc Memory allocation for names fails.
/// \exception Other The constructor of the \c Name class will throw if the
/// names in the wire is invalid.
RP::RP(InputBuffer& buffer, size_t) : mailbox_(buffer), text_(buffer) {
}
/// \brief Convert the \c RP to a string.
///
/// The output of this method is formatted as described in the "from string"
/// constructor (\c RP(const std::string&))).
///
/// \exception std::bad_alloc Internal resource allocation fails.
///
/// \return A \c string object that represents the \c RP object.
std::string
RP::toText() const {
return (mailbox_.toText() + " " + text_.toText());
}
void
RP::toWire(OutputBuffer& buffer) const {
mailbox_.toWire(buffer);
text_.toWire(buffer);
}
void
RP::toWire(MessageRenderer& renderer) const {
// Type RP is not "well-known", and name compression must be disabled
// per RFC3597.
renderer.writeName(mailbox_, false);
renderer.writeName(text_, false);
}
int
RP::compare(const Rdata& other) const {
const RP& other_rp = dynamic_cast<const RP&>(other);
const int cmp = compareNames(mailbox_, other_rp.mailbox_);
if (cmp != 0) {
return (cmp);
}
return (compareNames(text_, other_rp.text_));
}
// END_RDATA_NAMESPACE
// END_ISC_NAMESPACE
// 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// BEGIN_HEADER_GUARD
#include <string>
#include <dns/name.h>
#include <dns/rdata.h>
// BEGIN_ISC_NAMESPACE
// BEGIN_COMMON_DECLARATIONS
// END_COMMON_DECLARATIONS
// BEGIN_RDATA_NAMESPACE
/// \brief \c rdata::generic::RP class represents the RP RDATA as defined in
/// RFC1183.
///
/// This class implements the basic interfaces inherited from the abstract
/// \c rdata::Rdata class, and provides trivial accessors specific to the
/// RP RDATA.
class RP : public Rdata {
public:
// BEGIN_COMMON_MEMBERS
// END_COMMON_MEMBERS
/// We use the default copy constructor and assignment operator.
/// \brief Constructor from RDATA field parameters.
///
/// The parameters are a straightforward mapping of %RP RDATA
/// fields as defined in RFC1183.
RP(const Name& mailbox, const Name& text) :
mailbox_(mailbox), text_(text)
{}
/// \brief Return the value of the mailbox field.
///
/// This method normally does not throw an exception, but if resource
/// allocation for the returned \c Name object fails, a corresponding
/// standard exception will be thrown.
///
/// \note
/// Unlike the case of some other RDATA classes (such as
/// \c NS::getNSName()), this method constructs a new \c Name object
/// and returns it, instead of returning a reference to a \c Name object
/// internally maintained in the class (which is a private member).
/// This is based on the observation that this method will be rarely used
/// and even when it's used it will not be in a performance context
/// (for example, a recursive resolver won't need this field in its
/// resolution process). By returning a new object we have flexibility of
/// changing the internal representation without the risk of changing
/// the interface or method property.
/// The same note applies to the \c getText() method.
Name getMailbox() const { return (mailbox_); }
/// \brief Return the value of the text field.
///
/// This method normally does not throw an exception, but if resource
/// allocation for the returned \c Name object fails, a corresponding
/// standard exception will be thrown.
Name getText() const { return (text_); }
private:
Name mailbox_;
Name text_;
};
// END_RDATA_NAMESPACE
// END_ISC_NAMESPACE
// END_HEADER_GUARD
// Local Variables:
// mode: c++
// End:
......@@ -39,6 +39,7 @@ run_unittests_SOURCES += rdata_nsec3_unittest.cc
run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc
run_unittests_SOURCES += rdata_nsec3param_unittest.cc
run_unittests_SOURCES += rdata_rrsig_unittest.cc
run_unittests_SOURCES += rdata_rp_unittest.cc
run_unittests_SOURCES += rdata_tsig_unittest.cc
run_unittests_SOURCES += rrset_unittest.cc rrsetlist_unittest.cc
run_unittests_SOURCES += question_unittest.cc
......
// 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <string>
#include <dns/buffer.h>
#include <dns/exceptions.h>
#include <dns/rdataclass.h>
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
using isc::UnitTestUtil;
using namespace std;
using namespace isc::dns;
using namespace isc::dns::rdata;
namespace {
class Rdata_RP_Test : public RdataTest {
protected:
Rdata_RP_Test() :
mailbox_name("root.example.com."),
text_name("rp-text.example.com."),
// this also serves as a test for "from text" constructor in a normal
// case.
rdata_rp("root.example.com. rp-text.example.com."),
obuffer(0), renderer(obuffer)
{}
const Name mailbox_name, text_name;
const generic::RP rdata_rp; // commonly used test RDATA
OutputBuffer obuffer;
MessageRenderer renderer;
vector<uint8_t> expected_wire;
};
TEST_F(Rdata_RP_Test, createFromText) {
EXPECT_EQ(mailbox_name, rdata_rp.getMailbox());
EXPECT_EQ(text_name, rdata_rp.getText());
// Invalid textual input cases follow:
// names are invalid
EXPECT_THROW(generic::RP("bad..name. rp-text.example.com"), EmptyLabel);
EXPECT_THROW(generic::RP("mailbox.example.com. bad..name"), EmptyLabel);
// missing field
EXPECT_THROW(generic::RP("mailbox.example.com."), InvalidRdataText);
// redundant field
EXPECT_THROW(generic::RP("mailbox.example.com. rp-text.example.com. "
"redundant.example."), InvalidRdataText);
}
TEST_F(Rdata_RP_Test, createFromWire) {
RdataPtr rdata(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
"rdata_rp_fromWire1.wire"));
EXPECT_EQ(mailbox_name, dynamic_cast<generic::RP&>(*rdata).getMailbox());
EXPECT_EQ(text_name, dynamic_cast<generic::RP&>(*rdata).getText());
// a similar test with names being compressed
rdata = rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
"rdata_rp_fromWire2.wire", 30);
EXPECT_EQ(mailbox_name, dynamic_cast<generic::RP&>(*rdata).getMailbox());
EXPECT_EQ(Name("rp-text.example.net"),
dynamic_cast<generic::RP&>(*rdata).getText());
}
TEST_F(Rdata_RP_Test, badFromWire) {
// RDLEN is too short
EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
"rdata_rp_fromWire3.wire"),
InvalidRdataLength);
// RDLEN is too long
EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
"rdata_rp_fromWire4.wire"),
InvalidRdataLength);
// bogus mailbox name
EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
"rdata_rp_fromWire5.wire"),
DNSMessageFORMERR);
// bogus text name
EXPECT_THROW(rdataFactoryFromFile(RRType::RP(), RRClass::IN(),
"rdata_rp_fromWire6.wire"),
DNSMessageFORMERR);
}
TEST_F(Rdata_RP_Test, createFromParams) {
EXPECT_EQ(mailbox_name, generic::RP(mailbox_name, text_name).getMailbox());
EXPECT_EQ(text_name, generic::RP(mailbox_name, text_name).getText());
}
TEST_F(Rdata_RP_Test, toWireBuffer) {
// construct expected data
UnitTestUtil::readWireData("rdata_rp_toWire1.wire", expected_wire);
// construct actual data
rdata_rp.toWire(obuffer);
// then compare them
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
obuffer.getData(), obuffer.getLength(),
&expected_wire[0], expected_wire.size());
}
TEST_F(Rdata_RP_Test, toWireRenderer) {
// similar to toWireBuffer, but names in RDATA should be compressed.
UnitTestUtil::readWireData("rdata_rp_toWire2.wire", expected_wire);
renderer.writeName(Name("a.example.com"));
renderer.writeName(Name("b.example.net"));
generic::RP(mailbox_name, Name("rp-text.example.net")).toWire(renderer);
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
renderer.getData(), renderer.getLength(),
&expected_wire[0], expected_wire.size());
}
TEST_F(Rdata_RP_Test, toText) {
// there's not much to test for this method. Only checking a simple case.
EXPECT_EQ("root.example.com. rp-text.example.com.", rdata_rp.toText());
}
TEST_F(Rdata_RP_Test, compare) {
// check reflexivity
EXPECT_EQ(0, rdata_rp.compare(rdata_rp));
// names must be compared in case-insensitive manner
EXPECT_EQ(0, rdata_rp.compare(generic::RP("ROOT.example.com. "
"rp-text.EXAMPLE.com.")));
// another RP whose mailbox name is larger than that of rdata_rp.
const generic::RP large1_rp("zzzz.example.com. rp-text.example.com.");
EXPECT_GT(0, rdata_rp.compare(large1_rp));
EXPECT_LT(0, large1_rp.compare(rdata_rp));
// yet another RP whose text name is larger than that of rdata_rp.
const generic::RP large2_rp("root.example.com. zzzzzzz.example.com.");
EXPECT_GT(0, rdata_rp.compare(large2_rp));
EXPECT_LT(0, large2_rp.compare(rdata_rp));
// comparison attempt between incompatible RR types should be rejected
EXPECT_THROW(rdata_rp.compare(*RdataTest::rdata_nomatch), bad_cast);
}
}
......@@ -16,6 +16,10 @@ BUILT_SOURCES += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire
BUILT_SOURCES += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire
BUILT_SOURCES += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire
BUILT_SOURCES += rdata_rrsig_fromWire2.wire
BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire
BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire
BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire
BUILT_SOURCES += rdata_rp_toWire1.wire rdata_rp_toWire2.wire
BUILT_SOURCES += rdata_soa_toWireUncompressed.wire
BUILT_SOURCES += rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire
BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire
......@@ -67,6 +71,10 @@ EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec
EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec
EXTRA_DIST += rdata_opt_fromWire 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
EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec
EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec
EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec
EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec
EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec
......
#
# A simplest form of RP: all default parameters
#
[custom]
sections: rp
[rp]
#
# A simplest form of RP: names are compressed.
#
[custom]
sections: name/1:name/2:rp
[name/1]
name: a.example.com
[name/2]
name: b.example.net
[rp]
mailbox: root.ptr=2
text: rp-text.ptr=17
#
# RP-like RDATA but RDLEN is too short.
#
[custom]
sections: rp
[rp]
rdlen: 38
#
# RP-like RDATA but RDLEN is too long.
#
[custom]
sections: rp
[rp]
rdlen: 40
#
# RP-like RDATA but mailbox name is broken.
#
[custom]
sections: rp
[rp]
mailbox: "01234567890123456789012345678901234567890123456789012345678901234"
#
# RP-like RDATA but text name is broken.
#
[custom]
sections: rp
[rp]
text: "01234567890123456789012345678901234567890123456789012345678901234"
#
# A simplest form of RP for toWire test: all default parameters except rdlen,
# which is to be omitted.
#
[custom]
sections: rp
[rp]
rdlen: -1
#
# A simple form of RP: names could be compressed (but MUST NOT).
# rdlen is omitted for the "to wire" test.
#
[custom]
sections: name/1:name/2:rp
[name/1]
name: a.example.com
[name/2]
name: b.example.net
[rp]
rdlen: -1
mailbox: root.example.com
text: rp-text.example.net
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