2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-09-03 15:35:17 +00:00

added the concatenate() method

fixed signed/unsigned bug
added some more test cases


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@361 e5f2f494-b856-4b98-b285-d166d9295462
This commit is contained in:
JINMEI Tatuya
2009-12-12 02:44:57 +00:00
parent 6e28ebbdbd
commit 201aa42b67
3 changed files with 158 additions and 76 deletions

View File

@@ -16,6 +16,7 @@
#include <cctype> #include <cctype>
#include <cassert> #include <cassert>
#include <iterator>
#include "buffer.h" #include "buffer.h"
#include "name.h" #include "name.h"
@@ -76,11 +77,6 @@ typedef enum {
Name::Name(const std::string &namestring, bool downcase) Name::Name(const std::string &namestring, bool downcase)
{ {
char c;
std::vector<char> offsets;
offsets.reserve(128);
offsets.push_back(0);
// //
// Initialize things to make the compiler happy; they're not required. // Initialize things to make the compiler happy; they're not required.
// //
@@ -97,13 +93,20 @@ Name::Name(const std::string &namestring, bool downcase)
bool is_root = false; bool is_root = false;
ft_state state = ft_init; ft_state state = ft_init;
std::vector<unsigned char> offsets;
offsets.reserve(128);
offsets.push_back(0);
std::string ndata;
ndata.reserve(Name::MAX_WIRE);
// should we refactor this code using, e.g, the state pattern? Probably // should we refactor this code using, e.g, the state pattern? Probably
// not at this point, as this is based on proved code (derived from BIND9) // not at this point, as this is based on proved code (derived from BIND9)
// and it's less likely that we'll have more variations in the domain name // and it's less likely that we'll have more variations in the domain name
// syntax. If this ever happens next time, we should consider refactor // syntax. If this ever happens next time, we should consider refactor
// the code, rather than adding more states and cases below. // the code, rather than adding more states and cases below.
while (ndata_.size() < Name::MAX_WIRE && s != send && !done) { while (ndata.size() < Name::MAX_WIRE && s != send && !done) {
c = *s++; char c = *s++;
switch (state) { switch (state) {
case ft_init: case ft_init:
@@ -121,31 +124,31 @@ Name::Name(const std::string &namestring, bool downcase)
} }
if (is_root) { if (is_root) {
ndata_.push_back(0); ndata.push_back(0);
done = true; done = true;
break; break;
} }
// FALLTHROUGH // FALLTHROUGH
case ft_start: // begin of a label case ft_start: // begin of a label
ndata_.push_back(0); // placeholder for the label length field ndata.push_back(0); // placeholder for the label length field
count = 0; count = 0;
if (c == '\\') { if (c == '\\') {
state = ft_initialescape; state = ft_initialescape;
break; break;
} }
state = ft_ordinary; state = ft_ordinary;
assert(ndata_.size() < Name::MAX_WIRE); assert(ndata.size() < Name::MAX_WIRE);
// FALLTHROUGH // FALLTHROUGH
case ft_ordinary: // parsing a normal label case ft_ordinary: // parsing a normal label
if (c == '.') { if (c == '.') {
if (count == 0) { if (count == 0) {
dns_throw(EmptyLabel, "duplicate period"); dns_throw(EmptyLabel, "duplicate period");
} }
ndata_[offsets.back()] = count; ndata.at(offsets.back()) = count;
offsets.push_back(ndata_.size()); offsets.push_back(ndata.size());
if (s == send) { if (s == send) {
ndata_.push_back(0); ndata.push_back(0);
done = true; done = true;
} }
state = ft_start; state = ft_start;
@@ -155,7 +158,7 @@ Name::Name(const std::string &namestring, bool downcase)
if (++count > Name::MAX_LABELLEN) { if (++count > Name::MAX_LABELLEN) {
dns_throw(TooLongLabel, "label is too long"); dns_throw(TooLongLabel, "label is too long");
} }
ndata_.push_back(downcase ? maptolower[c] : c); ndata.push_back(downcase ? maptolower[c] : c);
} }
break; break;
case ft_initialescape: // just found '\' case ft_initialescape: // just found '\'
@@ -171,7 +174,7 @@ Name::Name(const std::string &namestring, bool downcase)
if (++count > Name::MAX_LABELLEN) { if (++count > Name::MAX_LABELLEN) {
dns_throw(TooLongLabel, "label is too long"); dns_throw(TooLongLabel, "label is too long");
} }
ndata_.push_back(downcase ? maptolower[c] : c); ndata.push_back(downcase ? maptolower[c] : c);
state = ft_ordinary; state = ft_ordinary;
break; break;
} }
@@ -193,7 +196,7 @@ Name::Name(const std::string &namestring, bool downcase)
if (++count > Name::MAX_LABELLEN) { if (++count > Name::MAX_LABELLEN) {
dns_throw(TooLongLabel, "label is too long"); dns_throw(TooLongLabel, "label is too long");
} }
ndata_.push_back(downcase ? maptolower[value] : value); ndata.push_back(downcase ? maptolower[value] : value);
state = ft_ordinary; state = ft_ordinary;
} }
break; break;
@@ -204,7 +207,7 @@ Name::Name(const std::string &namestring, bool downcase)
} }
if (!done) { // no trailing '.' was found. if (!done) { // no trailing '.' was found.
if (ndata_.size() == Name::MAX_WIRE) { if (ndata.size() == Name::MAX_WIRE) {
dns_throw(TooLongName, "name is too long for termination"); dns_throw(TooLongName, "name is too long for termination");
} }
assert(s == send); assert(s == send);
@@ -213,16 +216,17 @@ Name::Name(const std::string &namestring, bool downcase)
} }
if (state == ft_ordinary) { if (state == ft_ordinary) {
assert(count != 0); assert(count != 0);
ndata_[offsets.back()] = count; ndata.at(offsets.back()) = count;
offsets.push_back(ndata_.size()); offsets.push_back(ndata.size());
// add a trailing \0 // add a trailing \0
ndata_.push_back('\0'); ndata.push_back('\0');
} }
} }
labels_ = offsets.size(); labels_ = offsets.size();
assert(labels_ <= 127); assert(labels_ > 0 && labels_ <= Name::MAX_LABELS);
ndata_.assign(ndata.data(), ndata.size());
length_ = ndata_.size(); length_ = ndata_.size();
offsets_.assign(offsets.begin(), offsets.end()); offsets_.assign(offsets.begin(), offsets.end());
} }
@@ -236,8 +240,8 @@ typedef enum {
Name::Name(InputBuffer& buffer, bool downcase) Name::Name(InputBuffer& buffer, bool downcase)
{ {
unsigned int new_current; unsigned int new_current;
std::vector<char> offsets; std::vector<unsigned char> offsets;
offsets.reserve(128); offsets.reserve(Name::MAX_WIRE / 2);
/* /*
* Initialize things to make the compiler happy; they're not required. * Initialize things to make the compiler happy; they're not required.
@@ -357,10 +361,11 @@ Name::toText(bool omit_final_dot) const
return ("."); return (".");
} }
unsigned int count;
std::string::const_iterator np = ndata_.begin(); std::string::const_iterator np = ndata_.begin();
std::string::const_iterator np_end = ndata_.end(); std::string::const_iterator np_end = ndata_.end();
unsigned int labels = labels_; // use for integrity check unsigned int labels = labels_; // use for integrity check
// init with an impossible value to catch error cases in the end:
unsigned int count = Name::MAX_LABELLEN + 1;
// result string: it will roughly have the same length as the wire format // result string: it will roughly have the same length as the wire format
// name data. reserve that length to minimize reallocation. // name data. reserve that length to minimize reallocation.
@@ -428,12 +433,6 @@ Name::toText(bool omit_final_dot) const
NameComparisonResult NameComparisonResult
Name::compare(const Name& other) const Name::compare(const Name& other) const
{ {
unsigned int count1, count2, count;
int cdiff, chdiff;
unsigned char label1, label2;
size_t pos1, pos2;
NameComparisonResult::NameRelation namereln;
// Determine the relative ordering under the DNSSEC order relation of // Determine the relative ordering under the DNSSEC order relation of
// 'this' and 'other', and also determine the hierarchical relationship // 'this' and 'other', and also determine the hierarchical relationship
// of the names. // of the names.
@@ -448,32 +447,30 @@ Name::compare(const Name& other) const
--l; --l;
--l1; --l1;
--l2; --l2;
pos1 = offsets_[l1]; size_t pos1 = offsets_[l1];
pos2 = other.offsets_[l2]; size_t pos2 = other.offsets_[l2];
count1 = ndata_[pos1++]; unsigned int count1 = ndata_[pos1++];
count2 = other.ndata_[pos2++]; unsigned int count2 = other.ndata_[pos2++];
label1 = ndata_[pos1];
label2 = other.ndata_[pos2];
// We don't support any extended label types including now-obsolete // We don't support any extended label types including now-obsolete
// bitstring labels. // bitstring labels.
assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN); assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN);
cdiff = (int)count1 - (int)count2; int cdiff = (int)count1 - (int)count2;
if (cdiff < 0) unsigned int count = (cdiff < 0) ? count1 : count2;
count = count1;
else
count = count2;
while (count > 0) { while (count > 0) {
chdiff = (int)maptolower[label1] - (int)maptolower[label2]; unsigned char label1 = ndata_[pos1];
unsigned char label2 = other.ndata_[pos2];
int chdiff = (int)maptolower[label1] - (int)maptolower[label2];
if (chdiff != 0) { if (chdiff != 0) {
return (NameComparisonResult(chdiff, nlabels, return (NameComparisonResult(chdiff, nlabels,
NameComparisonResult::COMMONANCESTOR)); NameComparisonResult::COMMONANCESTOR));
} }
--count; --count;
label1 = ndata_[++pos1]; ++pos1;
label2 = other.ndata_[++pos2]; ++pos2;
} }
if (cdiff != 0) { if (cdiff != 0) {
return (NameComparisonResult(cdiff, nlabels, return (NameComparisonResult(cdiff, nlabels,
@@ -493,32 +490,28 @@ Name::compare(const Name& other) const
return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL)); return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL));
} }
// Are 'this' name and 'other' equal?
bool bool
Name::operator==(const Name& other) const Name::equals(const Name& other) const
{ {
unsigned int l;
unsigned char c, count;
std::string::const_iterator label1, label2;
if (length_ != other.length_ || labels_ != other.labels_) { if (length_ != other.length_ || labels_ != other.labels_) {
return (false); return (false);
} }
l = labels_; for (unsigned int l = labels_, pos = 0; l > 0; --l) {
label1 = ndata_.begin(); unsigned char count = ndata_[pos];
label2 = other.ndata_.begin(); if (count != other.ndata_[pos]) {
while (l > 0) {
l--;
count = *label1++;
if (count != *label2++) {
return (false); return (false);
} }
++pos;
while (count-- > 0) { while (count-- > 0) {
c = maptolower[(unsigned char)*label1++]; // XXX should avoid cast unsigned char label1 = ndata_[pos];
if (c != maptolower[(unsigned char)*label2++]) unsigned char label2 = other.ndata_[pos];
if (maptolower[label1] != maptolower[label2]) {
return (false); return (false);
}
++pos;
} }
} }
@@ -531,6 +524,51 @@ Name::isWildcard() const
return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*'); return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*');
} }
namespace { // hide the local class
struct OffsetAdjuster : public std::binary_function<unsigned char,
unsigned int,
unsigned char> {
unsigned char operator()(unsigned char ch, unsigned int offset) const
{
return (ch + offset);
}
};
}
Name
Name::concatenate(const Name& suffix) const
{
assert(this->length_ > 0 && suffix.length_ > 0);
assert(this->labels_ > 0 && suffix.labels_ > 0);
unsigned int length = this->length_ + suffix.length_ - 1;
if (length > Name::MAX_WIRE) {
dns_throw(TooLongName, "names are too long to concatenate");
}
Name retname;
retname.ndata_.reserve(length);
retname.ndata_.assign(this->ndata_, 0, this->length_ - 1);
retname.ndata_.insert(retname.ndata_.end(),
suffix.ndata_.begin(), suffix.ndata_.end());
assert(retname.ndata_.size() == length);
retname.length_ = length;
unsigned int labels = this->labels_ + suffix.labels_ - 1;
assert(labels <= Name::MAX_LABELS);
retname.offsets_.reserve(labels);
retname.offsets_.assign(&this->offsets_[0],
&this->offsets_[0] + this->labels_ - 1);
transform(suffix.offsets_.begin(), suffix.offsets_.end(),
back_inserter(retname.offsets_),
bind2nd(OffsetAdjuster(), this->length_ - 1));
assert(retname.offsets_.back() == retname.length_ - 1);
assert(retname.offsets_.size() == labels);
retname.labels_ = labels;
return (retname);
}
std::ostream& std::ostream&
operator<<(std::ostream& os, const Name& name) operator<<(std::ostream& os, const Name& name)
{ {

View File

@@ -185,13 +185,14 @@ private:
/// names as a special case. /// names as a special case.
/// ///
class Name { class Name {
public:
/// ///
/// \name Constructors and Destructor /// \name Constructors and Destructor
/// ///
//@{ //@{
private:
/// The default constructor /// The default constructor
Name() : length_(0), labels_(0) {} Name() : length_(0), labels_(0) {}
public:
/// Constructor from a string /// Constructor from a string
/// ///
/// \param namestr A string representation of the name to be constructed. /// \param namestr A string representation of the name to be constructed.
@@ -275,7 +276,7 @@ public:
/// returns the result in the form of a <code>NameComparisonResult</code> /// returns the result in the form of a <code>NameComparisonResult</code>
/// object. /// object.
/// ///
/// Note that this is a case-insensitive comparison. /// Note that this is case-insensitive comparison.
/// ///
/// \param other the right-hand operand to compare against. /// \param other the right-hand operand to compare against.
/// \return a <code>NameComparisonResult</code> object representing the /// \return a <code>NameComparisonResult</code> object representing the
@@ -284,29 +285,33 @@ public:
/// \brief Return true iff two names are equal. /// \brief Return true iff two names are equal.
/// ///
/// The comparison is based on the result of the compare() method. /// Semantically this could be implemented based on the result of the
/// \c compare() method, but the actual implementation uses different code
/// that simply performs character-by-character comparison (case
/// insensitive for the name label parts) on the two names. This is because
/// it would be much faster and the simple equality check would be pretty
/// common.
///
/// \param other the <code>Name</code> object to compare against. /// \param other the <code>Name</code> object to compare against.
/// \return true if <code>compare(other).get_order()</code> is 0; /// \return true if the two names are equal; otherwise false.
/// otherwise false.
bool equals(const Name& other) const; bool equals(const Name& other) const;
/// Same as equals() /// Same as equals()
bool operator==(const Name& other) const; bool operator==(const Name& other) const { return (this->equals(other)); }
/// \brief Return true iff two names are not equal. /// \brief Return true iff two names are not equal.
/// ///
/// The comparison is based on the result of the compare() method. /// This method simply negates the result of \c equal() method, and in that
/// \param other the <code>Name</code> object to compare against. /// sense it's redundant. The separate method is provided just for
/// \return true if <code>compare(other).get_order()</code> is non 0; /// convenience.
/// otherwise false. bool nequals(const Name& other) const { return !(this->equals(other)); }
bool nequals(const Name& other) const;
/// Same as nequals() /// Same as nequals()
bool operator!=(const Name& other) const { return (!(*this == other)); } bool operator!=(const Name& other) const { return (this->nequals(other)); }
/// \brief Less-than or equal comparison for Name against <code>other</code> /// \brief Less-than or equal comparison for Name against <code>other</code>
/// ///
/// The comparison is based on the result of the compare() method. /// The comparison is based on the result of the \c compare() method.
/// \param other the <code>Name</code> object to compare against. /// \param other the <code>Name</code> object to compare against.
/// \return true if <code>compare(other).get_order() <= 0</code>; /// \return true if <code>compare(other).get_order() <= 0</code>;
/// otherwise false. /// otherwise false.
@@ -318,7 +323,7 @@ public:
/// \brief Greater-than or equal comparison for Name against /// \brief Greater-than or equal comparison for Name against
/// <code>other</code> /// <code>other</code>
/// ///
/// The comparison is based on the result of the compare() method. /// The comparison is based on the result of the \c compare() method.
/// \param other the <code>Name</code> object to compare against. /// \param other the <code>Name</code> object to compare against.
/// \return true if <code>compare(other).get_order() >= 0</code>; /// \return true if <code>compare(other).get_order() >= 0</code>;
/// otherwise false. /// otherwise false.
@@ -329,7 +334,7 @@ public:
/// \brief Less-than comparison for Name against <code>other</code> /// \brief Less-than comparison for Name against <code>other</code>
/// ///
/// The comparison is based on the result of the compare() method. /// The comparison is based on the result of the \c compare() method.
/// \param other the <code>Name</code> object to compare against. /// \param other the <code>Name</code> object to compare against.
/// \return true if <code>compare(other).get_order() < 0</code>; /// \return true if <code>compare(other).get_order() < 0</code>;
/// otherwise false. /// otherwise false.
@@ -340,7 +345,7 @@ public:
/// \brief Greater-than comparison for Name against <code>other</code> /// \brief Greater-than comparison for Name against <code>other</code>
/// ///
/// The comparison is based on the result of the compare() method. /// The comparison is based on the result of the \c compare() method.
/// \param other the <code>Name</code> object to compare against. /// \param other the <code>Name</code> object to compare against.
/// \return true if <code>compare(other).get_order() > 0</code>; /// \return true if <code>compare(other).get_order() > 0</code>;
/// otherwise false. /// otherwise false.
@@ -393,13 +398,19 @@ public:
/// \brief Max allowable length of domain names. /// \brief Max allowable length of domain names.
static const size_t MAX_WIRE = 255; static const size_t MAX_WIRE = 255;
/// \brief Max allowable labels of domain names.
///
/// This is <code>ceil(MAX_WIRE / 2)</code>, and is equal to the number of
/// labels of name "a.a.a.a....a." (127 "a"'s and trailing dot).
static const size_t MAX_LABELS = 128;
/// \brief Max allowable length of labels of a domain name. /// \brief Max allowable length of labels of a domain name.
static const size_t MAX_LABELLEN = 63; static const size_t MAX_LABELLEN = 63;
//@} //@}
private: private:
std::string ndata_; std::string ndata_;
std::vector<char> offsets_; std::vector<unsigned char> offsets_;
unsigned int length_; unsigned int length_;
unsigned int labels_; unsigned int labels_;

View File

@@ -38,6 +38,7 @@ protected:
NameTest() : example_name("www.example.com") {} NameTest() : example_name("www.example.com") {}
Name example_name; Name example_name;
static const size_t MAX_LABELS = Name::MAX_LABELS;
// //
// helper methods // helper methods
// //
@@ -128,6 +129,17 @@ TEST_F(NameTest, fromText)
"123")); "123"));
// \DDD must consist of 3 digits. // \DDD must consist of 3 digits.
EXPECT_THROW(Name("\\12"), isc::dns::BadLabelType); EXPECT_THROW(Name("\\12"), isc::dns::BadLabelType);
// a name with the max number of labels. should be constructed without
// an error, and its length should be the max value.
Name maxlabels = Name("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 40
"0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 80
"0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 120
"0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 160
"0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 200
"0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 240
"0.1.2.3.4.5.6.");
EXPECT_EQ(MAX_LABELS, maxlabels.getLabels());
} }
TEST_F(NameTest, fromWire) TEST_F(NameTest, fromWire)
@@ -250,10 +262,31 @@ TEST_F(NameTest, compare)
} }
} }
TEST_F(NameTest, equal)
{
EXPECT_TRUE(example_name == Name("WWW.EXAMPLE.COM."));
EXPECT_TRUE(example_name.equals(Name("WWW.EXAMPLE.COM.")));
EXPECT_TRUE(example_name != Name("www.example.org."));
EXPECT_TRUE(example_name.nequals(Name("www.example.org.")));
}
TEST_F(NameTest, isWildcard) TEST_F(NameTest, isWildcard)
{ {
EXPECT_EQ(false, example_name.isWildcard()); EXPECT_EQ(false, example_name.isWildcard());
EXPECT_EQ(true, Name("*.a.example.com").isWildcard()); EXPECT_EQ(true, Name("*.a.example.com").isWildcard());
EXPECT_EQ(false, Name("a.*.example.com").isWildcard()); EXPECT_EQ(false, Name("a.*.example.com").isWildcard());
} }
TEST_F(NameTest, concatenate)
{
NameComparisonResult result =
Name("aaa.www.example.com.").compare(Name("aaa").concatenate(example_name));
EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation());
result = example_name.compare(Name(".").concatenate(example_name));
EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation());
result = example_name.compare(example_name.concatenate(Name(".")));
EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation());
}
} }