[svn] commit: r354 - in /branches/jinmei-dnsmessageapi: doc/ src/lib/dns/cpp/ src/lib/dns/cpp/testdata/
BIND 10 source code commits
bind10-changes at lists.isc.org
Thu Dec 10 23:03:29 UTC 2009
Author: jinmei
Date: Thu Dec 10 23:03:29 2009
New Revision: 354
Log:
mostly complete name class implementation. still need some more work to be
review-able, but committed for share.
Added:
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/messagerenderer.cc
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/messagerenderer.h
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/messagerenderer_unittest.cc
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name_unittest.cc
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire1
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire2
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire3_1
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire3_2
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire4
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire6
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire7
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_fromWire8
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_toWire1
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_toWire2
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/testdata/name_toWire3
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/unittest_util.cc
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/unittest_util.h
Modified:
branches/jinmei-dnsmessageapi/doc/Doxyfile
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/Makefile.am
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer.h
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer_unittest.cc
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/exceptions.h
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.cc
branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.h
Modified: branches/jinmei-dnsmessageapi/doc/Doxyfile
==============================================================================
--- branches/jinmei-dnsmessageapi/doc/Doxyfile (original)
+++ branches/jinmei-dnsmessageapi/doc/Doxyfile Thu Dec 10 23:03:29 2009
@@ -611,7 +611,7 @@
# against the file with absolute path, so to exclude all test directories
# for example use the pattern */test/*
-EXCLUDE_PATTERNS = *unittest.h
+EXCLUDE_PATTERNS = *unittest*.h
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
Modified: branches/jinmei-dnsmessageapi/src/lib/dns/cpp/Makefile.am
==============================================================================
--- branches/jinmei-dnsmessageapi/src/lib/dns/cpp/Makefile.am (original)
+++ branches/jinmei-dnsmessageapi/src/lib/dns/cpp/Makefile.am Thu Dec 10 23:03:29 2009
@@ -1,10 +1,13 @@
lib_LIBRARIES = libdns.a
-libdns_a_SOURCES = buffer.h name.cc name.h exceptions.h
+libdns_a_SOURCES = buffer.h name.cc name.h messagerenderer.h messagerenderer.cc
+libdns_a_SOURCES += exceptions.h
TESTS =
if HAVE_GTEST
TESTS += run_unittests
-run_unittests_SOURCES = buffer_unittest.cc exceptions_unittest.cc
+run_unittests_SOURCES = unittest_util.h unittest_util.cc
+run_unittests_SOURCES += buffer_unittest.cc name_unittest.cc
+run_unittests_SOURCES += messagerenderer_unittest.cc exceptions_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(GTEST_LDFLAGS)
Modified: branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer.h
==============================================================================
--- branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer.h (original)
+++ branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer.h Thu Dec 10 23:03:29 2009
@@ -283,6 +283,19 @@
const void* getData() const { return (&data_[0]); }
/// \brief Return the length of data written in the buffer.
size_t getLength() const { return (data_.size()); }
+ /// \brief Return the value of the buffer at the specified position.
+ ///
+ /// \c pos must specify the valid position of the buffer; otherwise an
+ /// exception class of \c InvalidBufferPosition will be thrown.
+ ///
+ /// \param pos The position in the buffer to be returned.
+ uint8_t operator[](size_t pos) const
+ {
+ if (pos >= data_.size()) {
+ dns_throw(InvalidBufferPosition, "read at invalid position");
+ }
+ return (data_[pos]);
+ }
//@}
///
@@ -296,6 +309,11 @@
/// that is to be filled in later, e.g, by \ref writeUint16At().
/// \param len The length of the gap to be inserted in bytes.
void skip(size_t len) { data_.insert(data_.end(), len, 0); }
+ /// \brief Clear buffer content.
+ ///
+ /// This method can be used to re-initialize and reuse the buffer without
+ /// constructing a new one.
+ void clear() { data_.clear(); }
/// \brief Write an unsigned 8-bit integer into the buffer.
///
/// \param data The 8-bit integer to be written into the buffer.
Modified: branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer_unittest.cc
==============================================================================
--- branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer_unittest.cc (original)
+++ branches/jinmei-dnsmessageapi/src/lib/dns/cpp/buffer_unittest.cc Thu Dec 10 23:03:29 2009
@@ -148,4 +148,20 @@
obuffer.skip(2);
EXPECT_EQ(6, obuffer.getLength());
}
+
+TEST_F(BufferTest, output_buffer_readat)
+{
+ obuffer.writeData(testdata, sizeof(testdata));
+ for (int i = 0; i < sizeof(testdata); i ++) {
+ EXPECT_EQ(testdata[i], obuffer[i]);
+ }
+ EXPECT_THROW(obuffer[sizeof(testdata)], isc::dns::InvalidBufferPosition);
}
+
+TEST_F(BufferTest, output_buffer_clear)
+{
+ obuffer.writeData(testdata, sizeof(testdata));
+ obuffer.clear();
+ EXPECT_EQ(0, obuffer.getLength());
+}
+}
Modified: branches/jinmei-dnsmessageapi/src/lib/dns/cpp/exceptions.h
==============================================================================
--- branches/jinmei-dnsmessageapi/src/lib/dns/cpp/exceptions.h (original)
+++ branches/jinmei-dnsmessageapi/src/lib/dns/cpp/exceptions.h Thu Dec 10 23:03:29 2009
@@ -79,6 +79,16 @@
};
///
+/// \brief A standard DNS module exception that is thrown if a parameter give
+/// to a method would refer to or modify out-of-range data.
+///
+class OutOfRange : public Exception {
+public:
+ OutOfRange(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
/// A shortcut macro to insert known values into exception arguments.
///
#define dns_throw(type, args...) throw type(__FILE__, __LINE__, args)
Modified: branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.cc
==============================================================================
--- branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.cc (original)
+++ branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.cc Thu Dec 10 23:03:29 2009
@@ -14,25 +14,55 @@
// $Id$
-#include <stdexcept>
+#include <cctype>
+#include <cassert>
#include "buffer.h"
#include "name.h"
-
-using namespace std;
-using namespace ISC::DNS;
-
-// quick hack exception classes: should be moved to an appropriate place soon.
-class ISCException {};
-class ISCUnexpected : public ISCException {};
-class ISCNoSpace : public ISCException {};
-
-class DNSException {};
-class DNSEmptyLabel : public DNSException {};
-class DNSLabelTooLong : public DNSException {};
-class DNSBadEscape : public DNSException {};
-class DNSBadLabelType : public DNSException {};
-
+#include "messagerenderer.h"
+
+using namespace isc::dns;
+
+using isc::dns::NameComparisonResult;
+using isc::dns::MessageRenderer;
+
+namespace isc {
+namespace dns {
+
+namespace {
+///
+/// These are shortcut arrays for efficient character conversion.
+/// digitvalue converts a digit character to the corresponding integer.
+/// maptolower convert uppercase alphabets to their lowercase counterparts.
+/// A helper class and its only instance will initialize the arrays at startup
+/// time.
+///
+static char digitvalue[256];
+static unsigned char maptolower[256];
+
+class Initializer {
+public:
+ Initializer()
+ {
+ for (unsigned int i = 0; i < 256; i++) {
+ if (i >= '0' && i<= '9') {
+ digitvalue[i] = i - '0';
+ } else {
+ digitvalue[i] = -1;
+ }
+ }
+ for (unsigned int i = 0; i < 256; i++) {
+ if (i >= 'A' && i <= 'Z') {
+ maptolower[i] = i - ('A' - 'a');
+ } else {
+ maptolower[i] = i;
+ }
+ }
+ }
+};
+/// This object is defined only to call its constructor.
+static Initializer initialier;
+}
typedef enum {
ft_init = 0,
@@ -44,352 +74,423 @@
ft_at
} ft_state;
-typedef enum {
- fw_start = 0,
- fw_ordinary,
- fw_copy,
- fw_newcurrent
-} fw_state;
-
-static char digitvalue[256] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
-};
-
-static unsigned char maptolower[] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
- 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
- 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
- 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
- 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
- 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
- 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
- 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
- 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
- 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
- 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
- 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
- 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
- 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
- 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
- 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
- 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
- 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
- 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
- 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
- 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
- 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
-};
-
-void
-Name::from_string(const string &namestring)
+Name::Name(const std::string &namestring, bool downcase)
{
char c;
- ft_state state;
- unsigned int value, count, pos, lpos;
- unsigned int n1, n2, tlen, nrem, nused, digits, labels, tused;
- bool done;
-
- offsets_.reserve(128);
- offsets_[0] = 0;
+ std::vector<char> offsets;
+ offsets.reserve(128);
+ offsets.push_back(0);
//
// Initialize things to make the compiler happy; they're not required.
//
- n1 = 0;
- n2 = 0;
- digits = 0;
- value = 0;
- count = 0;
+ unsigned int digits = 0;
+ unsigned int value = 0;
+ unsigned int count = 0;
//
// Set up the state machine.
//
- pos = 0;
- tlen = namestring.length();
- tused = 0;
- nrem = 255;
- nused = 0;
- labels = 0;
- done = false;
- state = ft_init;
-
- while (nrem > 0 && tlen > 0 && !done) {
- c = namestring[pos++];
- tlen--;
- tused++;
+ std::string::const_iterator s = namestring.begin();
+ std::string::const_iterator send = namestring.end();
+ bool done = false;
+ bool is_root = false;
+ ft_state state = ft_init;
+
+ // 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)
+ // 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
+ // the code, rather than adding more states and cases below.
+ while (ndata_.size() < Name::MAX_WIRE && s != send && !done) {
+ c = *s++;
switch (state) {
case ft_init:
- /*
- * Is this the root name?
- */
+ //
+ // Is this the root name?
+ //
if (c == '.') {
- if (tlen != 0)
- throw DNSEmptyLabel();
- labels++;
+ if (s != send) {
+ dns_throw(EmptyLabel, "non terminating empty label");
+ }
+ is_root = true;
+ } else if (c == '@' && s == send) {
+ // handle a single '@' as the root name.
+ is_root = true;
+ }
+
+ if (is_root) {
ndata_.push_back(0);
- nrem--;
- nused++;
done = true;
break;
}
- if (c == '@' && tlen == 0) {
- state = ft_at;
- break;
- }
-
- /* FALLTHROUGH */
- case ft_start:
- ndata_.push_back(0); // dummy data
- nrem--;
- lpos = nused;
- nused++;
+
+ // FALLTHROUGH
+ case ft_start: // begin of a label
+ ndata_.push_back(0); // placeholder for the label length field
count = 0;
if (c == '\\') {
state = ft_initialescape;
break;
}
state = ft_ordinary;
- if (nrem == 0)
- throw ISCNoSpace();
- /* FALLTHROUGH */
- case ft_ordinary:
+ assert(ndata_.size() < Name::MAX_WIRE);
+ // FALLTHROUGH
+ case ft_ordinary: // parsing a normal label
if (c == '.') {
- if (count == 0)
- throw DNSEmptyLabel();
- ndata_[lpos] = count;
- labels++;
- //INSIST(labels <= 127);
- offsets_[labels] = nused;
- if (tlen == 0) {
- labels++;
+ if (count == 0) {
+ dns_throw(EmptyLabel, "duplicate period");
+ }
+ ndata_[offsets.back()] = count;
+ offsets.push_back(ndata_.size());
+ if (s == send) {
ndata_.push_back(0);
- nrem--;
- nused++;
done = true;
}
state = ft_start;
} else if (c == '\\') {
state = ft_escape;
} else {
- if (count >= 63)
- throw DNSLabelTooLong();
- count++;
- ndata_.push_back(c);
- nrem--;
- nused++;
+ if (++count > Name::MAX_LABELLEN) {
+ dns_throw(TooLongLabel, "label is too long");
+ }
+ ndata_.push_back(downcase ? maptolower[c] : c);
}
break;
- case ft_initialescape:
+ case ft_initialescape: // just found '\'
if (c == '[') {
- /*
- * This looks like a bitstring label, which
- * was deprecated. Intentionally drop it.
- */
- throw DNSBadLabelType();
+ // This looks like a bitstring label, which was deprecated.
+ // Intentionally drop it.
+ dns_throw(BadLabelType, "invalid label type");
}
state = ft_escape;
- /* FALLTHROUGH */
- case ft_escape:
+ // FALLTHROUGH
+ case ft_escape: // begin of handling a '\'-escaped sequence
if (!isdigit(c & 0xff)) {
- if (count >= 63)
- throw DNSLabelTooLong();
- count++;
- ndata_.push_back(c);
- nrem--;
- nused++;
+ if (++count > Name::MAX_LABELLEN) {
+ dns_throw(TooLongLabel, "label is too long");
+ }
+ ndata_.push_back(downcase ? maptolower[c] : c);
state = ft_ordinary;
break;
}
digits = 0;
value = 0;
state = ft_escdecimal;
- /* FALLTHROUGH */
- case ft_escdecimal:
- if (!isdigit(c & 0xff))
- throw DNSBadEscape();
+ // FALLTHROUGH
+ case ft_escdecimal: // parsing a '\DDD' octet.
+ if (!isdigit(c & 0xff)) {
+ dns_throw(BadEscape, "mixture of escaped digit and non-digit");
+ }
value *= 10;
- value += digitvalue[(int)c];
+ value += digitvalue[c];
digits++;
if (digits == 3) {
- if (value > 255)
- throw DNSBadEscape();
- if (count >= 63)
- throw DNSLabelTooLong();
- count++;
- ndata_.push_back(value);
- nrem--;
- nused++;
+ if (value > 255) {
+ dns_throw(BadEscape, "escaped decimal is too large");
+ }
+ if (++count > Name::MAX_LABELLEN) {
+ dns_throw(TooLongLabel, "label is too long");
+ }
+ ndata_.push_back(downcase ? maptolower[value] : value);
state = ft_ordinary;
}
break;
default:
- throw runtime_error("Unexpected state " + state);
+ // impossible case
+ assert(false);
+ }
+ }
+
+ if (!done) { // no trailing '.' was found.
+ if (ndata_.size() == Name::MAX_WIRE) {
+ dns_throw(TooLongName, "name is too long for termination");
+ }
+ assert(s == send);
+ if (state != ft_ordinary && state != ft_at) {
+ dns_throw(BadLabelType, "incomplete label");
+ }
+ if (state == ft_ordinary) {
+ assert(count != 0);
+ ndata_[offsets.back()] = count;
+
+ offsets.push_back(ndata_.size());
+ // add a trailing \0
+ ndata_.push_back('\0');
+ }
+ }
+
+ labels_ = offsets.size();
+ assert(labels_ <= 127);
+ length_ = ndata_.size();
+ offsets_.assign(offsets.begin(), offsets.end());
+}
+
+typedef enum {
+ fw_start = 0,
+ fw_ordinary,
+ fw_newcurrent
+} fw_state;
+
+Name::Name(InputBuffer& buffer, bool downcase)
+{
+ unsigned int new_current;
+ std::vector<char> offsets;
+ offsets.reserve(128);
+
+ /*
+ * Initialize things to make the compiler happy; they're not required.
+ */
+ unsigned int n = 0;
+
+ //
+ // Set up.
+ //
+ bool done = false;
+ unsigned int nused = 0;
+ bool seen_pointer = false;
+ fw_state state = fw_start;
+
+ unsigned int cused = 0; // Bytes of compressed name data used
+ unsigned int current = buffer.getPosition();
+ unsigned int pos_begin = current;
+ unsigned int biggest_pointer = current;
+
+ //
+ // Note: The following code is not optimized for speed, but
+ // rather for correctness. Speed will be addressed in the future.
+ //
+ while (current < buffer.getLength() && !done) {
+ unsigned int c = buffer.readUint8();
+ current++;
+ if (!seen_pointer) {
+ cused++;
+ }
+
+ switch (state) {
+ case fw_start:
+ if (c < 64) {
+ offsets.push_back(nused);
+ if (nused + c + 1 > Name::MAX_WIRE) {
+ dns_throw(TooLongName, "wire name is too long");
+ }
+ nused += c + 1;
+ ndata_.push_back(c);
+ if (c == 0) {
+ done = true;
+ }
+ n = c;
+ state = fw_ordinary;
+ } else if (c >= 192) {
+ //
+ // Ordinary 14-bit pointer.
+ //
+ new_current = c & 0x3F;
+ n = 1;
+ state = fw_newcurrent;
+ } else {
+ // this case includes local compression pointer, which hasn't
+ // been standardized.
+ dns_throw(BadLabelType, "unknown label character");
+ }
+ break;
+ case fw_ordinary:
+ if (downcase) {
+ c = maptolower[c];
+ }
+ ndata_.push_back(c);
+ if (--n == 0) {
+ state = fw_start;
+ }
+ break;
+ case fw_newcurrent:
+ new_current *= 256;
+ new_current += c;
+ if (--n != 0) {
+ break;
+ }
+ if (new_current >= biggest_pointer) {
+ dns_throw(BadPointer, "bad compression pointer: out of range");
+ }
+ biggest_pointer = new_current;
+ current = new_current;
+ buffer.setPosition(current);
+ seen_pointer = true;
+ state = fw_start;
+ break;
+ default:
+ assert(false);
}
}
if (!done) {
- if (nrem == 0)
- throw ISCNoSpace();
- //INSIST(tlen == 0);
- if (state != ft_ordinary && state != ft_at)
- throw runtime_error("Unexpected state " + state);
- if (state == ft_ordinary) {
- //INSIST(count != 0);
- ndata_[lpos] = count;
- labels++;
- //INSIST(labels <= 127);
- offsets_[labels] = nused;
-
- // added a trailing \0
- ndata_.push_back('\0');
- ++labels;
- ++nused;
- offsets_[labels] = nused;
- }
- }
-
- labels_ = labels;
+ dns_throw(IncompleteName, "incomplete wire-format name");
+ }
+
+ labels_ = offsets.size();
length_ = nused;
-}
-
-string
+ offsets_.assign(offsets.begin(), offsets.end());
+ buffer.setPosition(pos_begin + cused);
+}
+
+void
+Name::toWire(OutputBuffer& buffer) const
+{
+ buffer.writeData(ndata_.data(), ndata_.size());
+}
+
+void
+Name::toWire(MessageRenderer& renderer) const
+{
+ renderer.writeName(*this);
+}
+
+std::string
Name::toText(bool omit_final_dot) const
{
- string tdata;
- unsigned int nlen;
- unsigned char c;
+ if (length_ == 1) {
+ //
+ // Special handling for the root label. We ignore omit_final_dot.
+ //
+ assert(labels_ == 1 && ndata_[0] == '\0');
+ return (".");
+ }
+
unsigned int count;
- unsigned int labels;
- bool saw_root = false;
- string::const_iterator iter_ndata;
-
- /*
- * This function assumes the name is in proper uncompressed
- * wire format.
- */
- iter_ndata = ndata_.begin();
- nlen = length_;
- labels = labels_;
-
- if (labels == 0 && nlen == 0) {
- /*
- * Special handling for an empty name.
- */
-
- /*
- * The names of these booleans are misleading in this case.
- * This empty name is not necessarily from the root node of
- * the DNS root zone, nor is a final dot going to be included.
- * They need to be set this way, though, to keep the "@"
- * from being trounced.
- */
- saw_root = true;
- omit_final_dot = false;
- tdata.push_back('@');
-
- /*
- * Skip the while() loop.
- */
- nlen = 0;
- } else if (nlen == 1 && labels == 1 && *iter_ndata == '\0') {
- /*
- * Special handling for the root label.
- */
- saw_root = true;
- omit_final_dot = false;
- tdata.push_back('.');
-
- /*
- * Skip the while() loop.
- */
- nlen = 0;
- }
-
- while (labels > 0 && nlen > 0) {
+ std::string::const_iterator np = ndata_.begin();
+ std::string::const_iterator np_end = ndata_.end();
+ unsigned int labels = labels_; // use for integrity check
+
+ // result string: it will roughly have the same length as the wire format
+ // name data. reserve that length to minimize reallocation.
+ std::string result;
+ result.reserve(length_);
+
+ while (np != np_end) {
labels--;
- count = *iter_ndata++;
- nlen--;
+ count = *np++;
+
if (count == 0) {
- saw_root = true;
+ if (!omit_final_dot) {
+ result.push_back('.');
+ }
break;
}
- if (count < 64) {
- //INSIST(nlen >= count);
- while (count > 0) {
- c = *iter_ndata;
+
+ if (count <= Name::MAX_LABELLEN) {
+ assert(np_end - np >= count);
+
+ if (!result.empty()) {
+ // just after a non-empty label. add a separating dot.
+ result.push_back('.');
+ }
+
+ while (count-- > 0) {
+ unsigned char c = *np++;
switch (c) {
- case 0x22: /* '"' */
- case 0x28: /* '(' */
- case 0x29: /* ')' */
- case 0x2E: /* '.' */
- case 0x3B: /* ';' */
- case 0x5C: /* '\\' */
- /* Special modifiers in zone files. */
- case 0x40: /* '@' */
- case 0x24: /* '$' */
- tdata.push_back('\\');
- tdata.push_back(c);
- iter_ndata++;
- nlen--;
+ case 0x22: // '"'
+ case 0x28: // '('
+ case 0x29: // ')'
+ case 0x2E: // '.'
+ case 0x3B: // ';'
+ case 0x5C: // '\\'
+ // Special modifiers in zone files.
+ case 0x40: // '@'
+ case 0x24: // '$'
+ result.push_back('\\');
+ result.push_back(c);
break;
default:
if (c > 0x20 && c < 0x7f) {
- tdata.push_back(c);
- iter_ndata++;
- nlen--;
+ // append printable characters intact
+ result.push_back(c);
} else {
- tdata.push_back(0x5c);
- tdata.push_back(0x30 + ((c / 100) % 10));
- tdata.push_back(0x30 + ((c / 10) % 10));
- tdata.push_back(0x30 + (c % 10));
- iter_ndata++;
- nlen--;
+ // encode non-printable characters in the form of \DDD
+ result.push_back(0x5c);
+ result.push_back(0x30 + ((c / 100) % 10));
+ result.push_back(0x30 + ((c / 10) % 10));
+ result.push_back(0x30 + (c % 10));
}
}
- count--;
- }
- } else
- throw runtime_error("Unexpected label type " + count);
- // The following assumes names are absolute. If not, we
- // fix things up later. Note that this means that in some
- // cases one more byte of text buffer is required than is
- // needed in the final output.
- tdata.push_back('.');
- }
-
- if (nlen != 0)
- throw ISCNoSpace();
-
- if (!saw_root || omit_final_dot)
- tdata.erase(tdata.end() - 1);
-
- return (tdata);
+ }
+ } else {
+ dns_throw(BadLabelType, "unknown label type in name data");
+ }
+ }
+
+ assert(labels == 0);
+ assert(count == 0); // a valid name must end with a 'dot'.
+
+ return (result);
+}
+
+NameComparisonResult
+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
+ // 'this' and 'other', and also determine the hierarchical relationship
+ // of the names.
+
+ unsigned int nlabels = 0;
+ unsigned int l1 = labels_;
+ unsigned int l2 = other.labels_;
+ int ldiff = (int)l1 - (int)l2;
+ unsigned int l = (ldiff < 0) ? l1 : l2;
+
+ while (l > 0) {
+ --l;
+ --l1;
+ --l2;
+ pos1 = offsets_[l1];
+ pos2 = other.offsets_[l2];
+ count1 = ndata_[pos1++];
+ count2 = other.ndata_[pos2++];
+ label1 = ndata_[pos1];
+ label2 = other.ndata_[pos2];
+
+ // We don't support any extended label types including now-obsolete
+ // bitstring labels.
+ assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN);
+
+ cdiff = (int)count1 - (int)count2;
+ if (cdiff < 0)
+ count = count1;
+ else
+ count = count2;
+
+ while (count > 0) {
+ chdiff = (int)maptolower[label1] - (int)maptolower[label2];
+ if (chdiff != 0) {
+ return (NameComparisonResult(chdiff, nlabels,
+ NameComparisonResult::COMMONANCESTOR));
+ }
+ --count;
+ label1 = ndata_[++pos1];
+ label2 = other.ndata_[++pos2];
+ }
+ if (cdiff != 0) {
+ return (NameComparisonResult(cdiff, nlabels,
+ NameComparisonResult::COMMONANCESTOR));
+ }
+ ++nlabels;
+ }
+
+ if (ldiff < 0) {
+ return (NameComparisonResult(ldiff, nlabels,
+ NameComparisonResult::SUPERDOMAIN));
+ } else if (ldiff > 0) {
+ return (NameComparisonResult(ldiff, nlabels,
+ NameComparisonResult::SUBDOMAIN));
+ }
+
+ return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL));
}
// Are 'this' name and 'other' equal?
@@ -398,26 +499,23 @@
{
unsigned int l;
unsigned char c, count;
- string::const_iterator label1, label2;
-
- if (length_ != other.length_)
+ std::string::const_iterator label1, label2;
+
+ if (length_ != other.length_ || labels_ != other.labels_) {
return (false);
+ }
l = labels_;
-
- if (l != other.labels_)
- return (false);
-
label1 = ndata_.begin();
label2 = other.ndata_.begin();
while (l > 0) {
l--;
count = *label1++;
- if (count != *label2++)
+ if (count != *label2++) {
return (false);
-
- while (count > 0) {
- count--;
+ }
+
+ while (count-- > 0) {
c = maptolower[(unsigned char)*label1++]; // XXX should avoid cast
if (c != maptolower[(unsigned char)*label2++])
return (false);
@@ -427,9 +525,18 @@
return (true);
}
-ostream&
-operator<<(ostream& os, const Name& name)
+bool
+Name::isWildcard() const
+{
+ return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*');
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Name& name)
{
os << name.toText();
return (os);
}
+
+}
+}
Modified: branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.h
==============================================================================
--- branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.h (original)
+++ branches/jinmei-dnsmessageapi/src/lib/dns/cpp/name.h Thu Dec 10 23:03:29 2009
@@ -20,76 +20,390 @@
#include <string>
#include <vector>
-namespace ISC {
-namespace DNS {
+#include "exceptions.h"
+
+namespace isc {
+namespace dns {
class InputBuffer;
class OutputBuffer;
-class NameCompressor;
+class MessageRenderer;
class NameDecompressor;
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters an empty label in the middle of a name.
+///
+class EmptyLabel : public Exception {
+public:
+ EmptyLabel(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters too long a name.
+///
+class TooLongName : public Exception {
+public:
+ TooLongName(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters too long a label.
+///
+class TooLongLabel : public Exception {
+public:
+ TooLongLabel(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// encounters an obsolete or incomplete label type. In effect "obsolete" only
+/// applies to bitstring labels, which would begin with "\[". Incomplete cases
+/// include an incomplete escaped sequence such as "\12".
+///
+class BadLabelType : public Exception {
+public:
+ BadLabelType(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the name parser
+/// fails to decode a "\"-escaped sequence.
+///
+class BadEscape : public Exception {
+public:
+ BadEscape(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the wire-format
+/// name contains an invalid compression pointer.
+///
+class BadPointer : public Exception {
+public:
+ BadPointer(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// \brief A standard DNS module exception that is thrown if the wire-format
+/// name is not a complete domain name.
+///
+class IncompleteName : public Exception {
+public:
+ IncompleteName(const char* file, size_t line, const char* what) :
+ isc::dns::Exception(file, line, what) {}
+};
+
+///
+/// This is a supplemental class used only as a return value of Name::compare().
+/// It encapsulate a tuple of the comparison: ordering, number of common labels,
+/// and relationship as follows:
+/// - ordering: relative ordering under the DNSSEC order relation
+/// - labels: the number of common significant labels of the two names being
+/// compared
+/// - relationship: see NameComparisonResult::NameRelation
+///
class NameComparisonResult {
public:
+ /// The relation of two names under comparison.
+ /// Its semantics for the case of
+ /// <code>name1->compare(name2)</code> (where name1 and name2 are instances
+ /// of the Name class) is as follows:
+ /// - SUPERDOMAIN: name1 properly contains name2; name2 is a proper
+ /// subdomain of name1
+ /// - SUBDOMAIN: name1 is a proper subdomain of name2
+ /// - EQUAL: name1 and name2 are equal
+ /// - COMMONANCESTOR: name1 and name2 share a common ancestor
+ ///
+ /// Note that in our implementation there's always a hierarchical
+ /// relationship between any two names since all names are absolute and
+ /// they at least share the trailing empty label.
+ /// So, for example, the relationship between "com." and "net." is
+ /// "commonancestor", even though it may be counter intuitive.
enum NameRelation {
- none = 0,
- contains = 1,
- subdomain = 2,
- equal = 3,
- commonancestor = 4
+ SUPERDOMAIN = 0,
+ SUBDOMAIN = 1,
+ EQUAL = 2,
+ COMMONANCESTOR = 3
};
- explicit NameComparisonResult(int order, int nlabels,
+ ///
+ /// \name Constructors and Destructor
+ ///
+ //@{
+ /// \brief Constructor from a comparison tuple
+ ///
+ /// This constructor simply initializes the object in the straightforward
+ /// way.
+ explicit NameComparisonResult(int order, unsigned int nlabels,
NameRelation relation) :
order_(order), nlabels_(nlabels), relation_(relation) {}
- int get_order() const { return (order_); }
- int get_common_labels() const { return (nlabels_); }
- NameRelation get_relation() const { return (relation_); }
+ //@}
+
+ ///
+ /// \name Getter Methods
+ ///
+ //@{
+ /// Returns the ordering of the comparison result
+ int getOrder() const { return (order_); }
+ /// Returns the number of common labels of the comparison result
+ unsigned int getCommonLabels() const { return (nlabels_); }
+ /// Returns the NameRelation of the comparison result
+ NameRelation getRelation() const { return (relation_); }
+ //@}
private:
int order_;
- int nlabels_;
+ unsigned int nlabels_;
NameRelation relation_;
};
+///
+/// The \c Name class encapsulates DNS names. It provides interfaces
+/// to construct a name from string or wire-format data, transform a name into
+/// a string or wire-format data, compare two names, get access to attributes.
+///
+/// Note that while many other DNS APIs introduce an "absolute or relative"
+/// attribute of names as defined in RFC1035, names are always "absolute" in
+/// the initial design of this API.
+/// In fact, separating absolute and relative would confuse API users
+/// unnecessarily. For example, it's not so intuitive to consider the
+/// comparison result of an absolute name with a relative name.
+/// We've looked into how the concept of absolute names is used in BIND9,
+/// and found that in many cases names are generally absolute.
+/// The only reasonable case of separating absolute and relative is in a master
+/// file parser, where a relative name must be a complete name with an "origin"
+/// name, which must be absolute. So, in this initial design, we chose a
+/// simpler approach: the API generally handles names as absolute; when we
+/// introduce a parser of master files, we'll introduce the notion of relative
+/// names as a special case.
+///
class Name {
public:
+ ///
+ /// \name Constructors and Destructor
+ ///
+ //@{
+ /// The default constructor
Name() : length_(0), labels_(0) {}
- explicit Name(const std::string& namestr) : length_(0), labels_(0)
- { from_string(namestr); }
- explicit Name(NameDecompressor& decompressor, InputBuffer& buffer);
- // copy constructor (default cp-ctor should work fine)
- //Name(const Name& orig);
- // destructor (default dtor should work fine)
- //~Name();
-
+ /// Constructor from a string
+ ///
+ /// \param namestr A string representation of the name to be constructed.
+ /// \param downcase Whether to convert upper case alphabets to lower case.
+ explicit Name(const std::string& namestr, bool downcase = false);
+ /// Constructor from wire-format data.
+ ///
+ /// The \c buffer parameter normally stores a complete DNS message
+ /// containing the name to be constructed. The current read position of
+ /// the buffer points to the head of the name.
+ ///
+ /// The input data may or may not be compressed; if it's compressed, this
+ /// method will automatically decompress it.
+ ///
+ /// \param buffer A buffer storing the wire format data.
+ /// \param downcase Whether to convert upper case alphabets to lower case.
+ explicit Name(InputBuffer& buffer, bool downcase = false);
+ //@}
+
+ ///
+ /// \name Getter Methods
+ ///
+ //@{
+ /// \brief Gets the length of the <code>Name</code> in its wire format.
+ ///
+ /// \return the length of the <code>Name</code>
+ size_t getLength() const { return (length_); }
+
+ /// \brief Returns the number of labels contained in the <code>Name</code>.
+ ///
+ /// Note that an empty label (corresponding to a trailing '.') is counted
+ /// as a single label, so the return value of this method must be >0.
+ ///
+ /// \return the number of labels
+ unsigned int getLabels() const { return (labels_); }
+ //@}
+
+ ///
+ /// \name Converter methods
+ ///
+ //@{
+ /// \brief Convert the Name to a string.
+ ///
+ /// This method returns a <code>std::string</code> object representing the
+ /// Name as a string. Unless <code>omit_final_dot</code> is
+ /// <code>true</code>, the returned string ends with a dot '.'; the default
+ /// is <code>false</code>. The default value of this parameter is
+ /// <code>true</code>; converted names will have a trailing dot by default.
+ ///
+ /// This function assumes the name is in proper uncompressed wire format.
+ /// If it finds an unexpected label character including compression pointer,
+ /// an exception of class \c isc::dns::BadLabelType will be thrown.
+ //
+ /// \param omit_final_dot whether to omit the trailing dot in the output.
+ /// \return a string representation of the <code>Name</code>.
std::string toText(bool omit_final_dot = false) const;
- void toWire(OutputBuffer& buffer, NameCompressor& compressor) const;
- size_t getLength() const { return (length_); }
- unsigned int getLabels() const { return (labels_); }
+
+ /// \brief Render the <code>Name</code> in the wire format with compression.
+ ///
+ /// This method dumps the Name in wire format with help of \c renderer,
+ /// which encapsulates output buffer and name compression algorithm to
+ /// render the name.
+ ///
+ /// \param renderer DNS message rendering context that encapsulates the
+ /// output buffer and name compression information.
+ void toWire(MessageRenderer& renderer) const;
+
+ /// \brief Render the <code>Name</code> in the wire format without
+ /// compression.
+ ///
+ void toWire(OutputBuffer& buffer) const;
+ //@}
+
+ ///
+ /// \name Comparison methods
+ ///
+ //@{
+ /// \brief Compare two <code>Name</code>s.
+ ///
+ /// This method compares the <code>Name</code> and <code>other</code> and
+ /// returns the result in the form of a <code>NameComparisonResult</code>
+ /// object.
+ ///
+ /// Note that this is a case-insensitive comparison.
+ ///
+ /// \param other the right-hand operand to compare against.
+ /// \return a <code>NameComparisonResult</code> object representing the
+ /// comparison result.
NameComparisonResult compare(const Name& other) const;
+
+ /// \brief Return true iff two names are equal.
+ ///
+ /// The comparison is based on the result of the compare() method.
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).get_order()</code> is 0;
+ /// otherwise false.
+ bool equals(const Name& other) const;
+
+ /// Same as equals()
+ bool operator==(const Name& other) const;
+
+ /// \brief Return true iff two names are not equal.
+ ///
+ /// The comparison is based on the result of the compare() method.
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).get_order()</code> is non 0;
+ /// otherwise false.
+ bool nequals(const Name& other) const;
+
+ /// Same as nequals()
+ bool operator!=(const Name& other) const { return (!(*this == other)); }
+
+ /// \brief Less-than or equal comparison for Name against <code>other</code>
+ ///
+ /// The comparison is based on the result of the compare() method.
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).get_order() <= 0</code>;
+ /// otherwise false.
+ bool leq(const Name& other) const;
+
+ /// Same as leq()
+ bool operator<=(const Name& other) const;
+
+ /// \brief Greater-than or equal comparison for Name against
+ /// <code>other</code>
+ ///
+ /// The comparison is based on the result of the compare() method.
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).get_order() >= 0</code>;
+ /// otherwise false.
+ bool geq(const Name& other) const;
+
+ /// Same as geq()
+ bool operator>=(const Name& other) const;
+
+ /// \brief Less-than comparison for Name against <code>other</code>
+ ///
+ /// The comparison is based on the result of the compare() method.
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).get_order() < 0</code>;
+ /// otherwise false.
+ bool lthan(const Name& other) const;
+
+ /// Same as lthan()
+ bool operator<(const Name& other) const;
+
+ /// \brief Greater-than comparison for Name against <code>other</code>
+ ///
+ /// The comparison is based on the result of the compare() method.
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).get_order() > 0</code>;
+ /// otherwise false.
+ bool gthan(const Name& other) const;
+
+ /// Same as gthan()
+ bool operator>(const Name& other) const;
+ //@}
+
+ ///
+ /// \name Transformer methods
+ ///
+ //@{
+ /// \brief Extract a specified subpart of Name.
+ ///
+ /// Note: we may want to have different versions (signatures) of this
+ /// method. For example, we want to split the Name based on a given suffix
+ /// name.
+ ///
+ /// \param first the start position of the extracted name
+ /// \param n number of labels of the extracted name
+ /// \return a new Name object based on the Name containing <code>n</code>
+ /// labels including and following the <code>first</code> label.
Name split(unsigned int first, unsigned int n) const;
+
+ /// \brief Concatenate two names
+ ///
+ /// This method appends \c suffix to \c this Name.
+ ///
+ /// \param suffix a Name object to be appended to the Name.
+ /// \return a new Name object concatenating \c suffix to \c this Name.
Name concatenate(const Name& suffix) const;
+ //@}
+
+ ///
+ /// \name Testing methods
+ ///
+ //@{
+ /// \brief Test if this is a wildcard name.
+ ///
+ /// \return \c true if the least significant label of this Name is
+ /// <code>'*'</code>; otherwise \c false.
bool isWildcard() const;
- bool operator==(const Name& other) const;
- bool equals(const Name& other) const; // alias of ==
- bool operator!=(const Name& other) const { return (!(*this == other)); }
- bool nequals(const Name& other) const; // alias of !=
- bool operator<=(const Name& other) const;
- bool leq(const Name& other) const; // alias of <=
- bool operator>=(const Name& other) const;
- bool geq(const Name& other) const; // alias of >=
- bool operator<(const Name& other) const;
- bool lthan(const Name& other) const; // alias of <
- bool operator>(const Name& other) const;
- bool gthan(const Name& other) const; // alias of >
+ //@}
+
+ ///
+ /// \name Protocol constants
+ ///
+ //@{
+ /// \brief Max allowable length of domain names.
+ static const size_t MAX_WIRE = 255;
+
+ /// \brief Max allowable length of labels of a domain name.
+ static const size_t MAX_LABELLEN = 63;
+ //@}
private:
- static const unsigned int MAXWIRE = 255;
-
std::string ndata_;
std::vector<char> offsets_;
unsigned int length_;
unsigned int labels_;
- void from_string(const std::string& namestr);
+ void fromString(const std::string& namestr);
};
std::ostream& operator<<(std::ostream& os, const Name& name);
More information about the bind10-changes
mailing list