[svn] commit: r430 - in /branches/jinmei-dnsrrparams: ./ doc/ src/ src/lib/ src/lib/dns/cpp/ src/lib/dns/cpp/testdata/
BIND 10 source code commits
bind10-changes at lists.isc.org
Mon Jan 4 20:48:26 UTC 2010
Author: jinmei
Date: Mon Jan 4 20:48:26 2010
New Revision: 430
Log:
merging the name class change in the trunk into the new working branch
Added:
branches/jinmei-dnsrrparams/src/lib/dns/cpp/buffer.h
- copied unchanged from r429, trunk/src/lib/dns/cpp/buffer.h
branches/jinmei-dnsrrparams/src/lib/dns/cpp/buffer_unittest.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/buffer_unittest.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/exceptions.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/exceptions.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/exceptions.h
- copied unchanged from r429, trunk/src/lib/dns/cpp/exceptions.h
branches/jinmei-dnsrrparams/src/lib/dns/cpp/exceptions_unittest.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/exceptions_unittest.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/messagerenderer.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/messagerenderer.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/messagerenderer.h
- copied unchanged from r429, trunk/src/lib/dns/cpp/messagerenderer.h
branches/jinmei-dnsrrparams/src/lib/dns/cpp/messagerenderer_unittest.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/messagerenderer_unittest.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/name_unittest.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/name_unittest.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/testdata/
- copied from r429, trunk/src/lib/dns/cpp/testdata/
branches/jinmei-dnsrrparams/src/lib/dns/cpp/unittest_util.cc
- copied unchanged from r429, trunk/src/lib/dns/cpp/unittest_util.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/unittest_util.h
- copied unchanged from r429, trunk/src/lib/dns/cpp/unittest_util.h
Removed:
branches/jinmei-dnsrrparams/Makefile.in
branches/jinmei-dnsrrparams/aclocal.m4
branches/jinmei-dnsrrparams/config.h.in
branches/jinmei-dnsrrparams/src/Makefile.in
branches/jinmei-dnsrrparams/src/lib/Makefile.in
Modified:
branches/jinmei-dnsrrparams/ (props changed)
branches/jinmei-dnsrrparams/configure.ac
branches/jinmei-dnsrrparams/doc/Doxyfile
branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am
branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc
branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h
Modified: branches/jinmei-dnsrrparams/configure.ac
==============================================================================
--- branches/jinmei-dnsrrparams/configure.ac (original)
+++ branches/jinmei-dnsrrparams/configure.ac Mon Jan 4 20:48:26 2010
@@ -1,7 +1,7 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
-AC_PREREQ([2.64])
+AC_PREREQ([2.59])
AC_INIT(bind, 10.0.0, bind10-bugs at isc.org)
AC_CONFIG_SRCDIR(README)
AM_INIT_AUTOMAKE
@@ -10,7 +10,10 @@
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
+AM_CONDITIONAL(GCC, test "$GCC" = yes)
AC_PROG_RANLIB
+
+AC_PROG_LIBTOOL
# Checks for libraries.
@@ -20,6 +23,18 @@
AC_HEADER_STDBOOL
AC_TYPE_SIZE_T
+# default compiler warning settings
+if test "X$GCC" = "Xyes"; then
+CXXFLAGS="-g -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare"
+fi
+
+# produce PIC unless we disable share libraries. need this for python bindings.
+if test $enable_shared != "no" -a "X$GCC" = "Xyes"; then
+ CXXFLAGS="$CXXFLAGS -fPIC"
+fi
+
+AC_SUBST(CXXFLAGS)
+
#
# Check availablity of gtest, which will be used for unit tests.
#
@@ -28,9 +43,17 @@
gtest_path="$withval", gtest_path="no")
if test "$gtest_path" != "no"
then
- GTEST_INCLUDES="-I${gtest_path}/include"
- GTEST_LDFLAGS="-L${gtest_path}/lib"
- GTEST_LDADD="-lgtest"
+ if test "$gtest_path" != "yes"
+ then
+ GTEST_INCLUDES="-I${gtest_path}/include"
+ GTEST_LDFLAGS="-L${gtest_path}/lib"
+ GTEST_LDADD="-lgtest"
+ else
+ # todo: check for default paths (/usr and /usr/local?)
+ GTEST_INCLUDE="-I/usr/include"
+ GTEST_LDFLAGS="-L/usr/lib"
+ GTEST_LDADD="-lgtest"
+ fi
else
GTEST_INCLUDES=
GTEST_LDFLAGS=
Modified: branches/jinmei-dnsrrparams/doc/Doxyfile
==============================================================================
--- branches/jinmei-dnsrrparams/doc/Doxyfile (original)
+++ branches/jinmei-dnsrrparams/doc/Doxyfile Mon Jan 4 20:48:26 2010
@@ -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-dnsrrparams/src/lib/dns/cpp/Makefile.am
==============================================================================
--- branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am (original)
+++ branches/jinmei-dnsrrparams/src/lib/dns/cpp/Makefile.am Mon Jan 4 20:48:26 2010
@@ -1,13 +1,17 @@
-lib_LIBRARIES = libdns.a
-libdns_a_SOURCES = name.cc name.h
+lib_LTLIBRARIES = libdns.la
+libdns_la_SOURCES = buffer.h name.cc name.h messagerenderer.h messagerenderer.cc
+libdns_la_SOURCES += exceptions.h exceptions.cc
TESTS =
if HAVE_GTEST
TESTS += run_unittests
-run_unittests_SOURCES = run_unittests.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)
-run_unittests_LDADD = ./libdns.a $(GTEST_LDADD)
+run_unittests_LDADD = .libs/libdns.a $(GTEST_LDADD)
endif
noinst_PROGRAMS = $(TESTS)
Modified: branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc
==============================================================================
--- branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc (original)
+++ branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.cc Mon Jan 4 20:48:26 2010
@@ -14,421 +14,681 @@
// $Id$
-#include <stdexcept>
-
+#include <cctype>
+#include <cassert>
+#include <iterator>
+#include <functional>
+
+#include <algorithm>
+
+#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 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;
+}
+
+namespace {
+///
+/// Textual name parser states.
+///
typedef enum {
- ft_init = 0,
- ft_start,
- ft_ordinary,
- ft_initialescape,
- ft_escape,
- ft_escdecimal,
- ft_at
+ ft_init = 0, // begin of the name
+ ft_start, // begin of a label
+ ft_ordinary, // parsing an ordinary label
+ ft_initialescape, // just found '\'
+ ft_escape, // begin of handling a '\'-escaped sequence
+ ft_escdecimal, // parsing a '\DDD' octet.
+
+ // Unused at this moment. We'll revisit this when we support master file
+ // parser where @ is used to mean an origin name.
+ 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)
-{
- 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;
-
+}
+
+Name::Name(const std::string &namestring, bool downcase)
+{
//
// 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;
+
+ std::vector<unsigned char> offsets;
+ offsets.reserve(Name::MAX_LABELS);
+ offsets.push_back(0);
+
+ std::string ndata;
+ ndata.reserve(Name::MAX_WIRE);
+
+ // 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) {
+ unsigned char 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++;
- ndata_.push_back(0);
- nrem--;
- nused++;
+ 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);
done = true;
break;
}
- if (c == '@' && tlen == 0) {
- state = ft_at;
- break;
- }
-
- /* FALLTHROUGH */
+
+ // FALLTHROUGH
case ft_start:
- ndata_.push_back(0); // dummy data
- nrem--;
- lpos = nused;
- nused++;
+ 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 */
+ assert(ndata.size() < Name::MAX_WIRE);
+ // FALLTHROUGH
case ft_ordinary:
if (c == '.') {
- if (count == 0)
- throw DNSEmptyLabel();
- ndata_[lpos] = count;
- labels++;
- //INSIST(labels <= 127);
- offsets_[labels] = nused;
- if (tlen == 0) {
- labels++;
- ndata_.push_back(0);
- nrem--;
- nused++;
+ if (count == 0) {
+ dns_throw(EmptyLabel, "duplicate period");
+ }
+ ndata.at(offsets.back()) = count;
+ offsets.push_back(ndata.size());
+ if (s == send) {
+ ndata.push_back(0);
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 > MAX_LABELLEN) {
+ dns_throw(TooLongLabel, "label is too long");
+ }
+ ndata.push_back(downcase ? maptolower[c] : c);
}
break;
case ft_initialescape:
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 */
+ // FALLTHROUGH
case ft_escape:
if (!isdigit(c & 0xff)) {
- if (count >= 63)
- throw DNSLabelTooLong();
- count++;
- ndata_.push_back(c);
- nrem--;
- nused++;
+ if (++count > 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 */
+ // FALLTHROUGH
case ft_escdecimal:
- if (!isdigit(c & 0xff))
- throw DNSBadEscape();
+ 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 > 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(IncompleteName, "incomplete textual name");
+ }
+ if (state == ft_ordinary) {
+ assert(count != 0);
+ ndata.at(offsets.back()) = count;
+
+ offsets.push_back(ndata.size());
+ // add a trailing \0
+ ndata.push_back('\0');
+ }
+ }
+
+ labelcount_ = offsets.size();
+ assert(labelcount_ > 0 && labelcount_ <= Name::MAX_LABELS);
+ ndata_.assign(ndata.data(), ndata.size());
+ length_ = ndata_.size();
+ offsets_.assign(offsets.begin(), offsets.end());
+}
+
+namespace {
+///
+/// Wire-format name parser states.
+///
+typedef enum {
+ fw_start = 0, // beginning of a label
+ fw_ordinary, // inside an ordinary (non compressed) label
+ fw_newcurrent // beginning of a compression pointer
+} fw_state;
+}
+
+Name::Name(InputBuffer& buffer, bool downcase)
+{
+ std::vector<unsigned char> offsets;
+ offsets.reserve(Name::MAX_LABELS);
+
+ /*
+ * 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;
+
+ // Make the compiler happy; this is not required.
+ // XXX: bad style in that we initialize it with a dummy value and define
+ // it far from where it's used. But alternatives seemed even worse.
+ unsigned int new_current = 0;
+
+ //
+ // 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 <= MAX_LABELLEN) {
+ 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 & COMPRESS_POINTER_MARK8) == COMPRESS_POINTER_MARK8) {
+ //
+ // Ordinary 14-bit pointer.
+ //
+ new_current = c & ~COMPRESS_POINTER_MARK8;
+ 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");
+ }
+
+ labelcount_ = offsets.size();
length_ = nused;
-}
-
-string
-Name::to_text(bool omit_final_dot) const
-{
- string tdata;
- unsigned int nlen;
- unsigned char c;
- 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) {
+ 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
+{
+ if (length_ == 1) {
+ //
+ // Special handling for the root label. We ignore omit_final_dot.
+ //
+ assert(labelcount_ == 1 && ndata_[0] == '\0');
+ return (".");
+ }
+
+ std::string::const_iterator np = ndata_.begin();
+ std::string::const_iterator np_end = ndata_.end();
+ unsigned int labels = labelcount_; // use for integrity check
+ // init with an impossible value to catch error cases in the end:
+ unsigned int count = MAX_LABELLEN + 1;
+
+ // 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 <= 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);
-}
-
-// Are 'this' name and 'other' equal?
+ }
+ } 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
+{
+ // 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 = labelcount_;
+ unsigned int l2 = other.labelcount_;
+ int ldiff = (int)l1 - (int)l2;
+ unsigned int l = (ldiff < 0) ? l1 : l2;
+
+ while (l > 0) {
+ --l;
+ --l1;
+ --l2;
+ size_t pos1 = offsets_[l1];
+ size_t pos2 = other.offsets_[l2];
+ unsigned int count1 = ndata_[pos1++];
+ unsigned int count2 = other.ndata_[pos2++];
+
+ // We don't support any extended label types including now-obsolete
+ // bitstring labels.
+ assert(count1 <= MAX_LABELLEN && count2 <= MAX_LABELLEN);
+
+ int cdiff = (int)count1 - (int)count2;
+ unsigned int count = (cdiff < 0) ? count1 : count2;
+
+ while (count > 0) {
+ unsigned char label1 = ndata_[pos1];
+ unsigned char label2 = other.ndata_[pos2];
+
+ int chdiff = (int)maptolower[label1] - (int)maptolower[label2];
+ if (chdiff != 0) {
+ return (NameComparisonResult(chdiff, nlabels,
+ NameComparisonResult::COMMONANCESTOR));
+ }
+ --count;
+ ++pos1;
+ ++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));
+}
+
bool
-Name::operator==(const Name& other) const
-{
- unsigned int l;
- unsigned char c, count;
- string::const_iterator label1, label2;
-
- if (length_ != other.length_)
+Name::equals(const Name& other) const
+{
+ if (length_ != other.length_ || labelcount_ != other.labelcount_) {
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++)
+ }
+
+ for (unsigned int l = labelcount_, pos = 0; l > 0; --l) {
+ unsigned char count = ndata_[pos];
+ if (count != other.ndata_[pos]) {
return (false);
+ }
+ ++pos;
+
+ while (count-- > 0) {
+ unsigned char label1 = ndata_[pos];
+ unsigned char label2 = other.ndata_[pos];
+
+ if (maptolower[label1] != maptolower[label2]) {
+ return (false);
+ }
+ ++pos;
+ }
+ }
+
+ return (true);
+}
+
+bool
+Name::leq(const Name& other) const
+{
+ return (compare(other).getOrder() <= 0);
+}
+
+bool
+Name::geq(const Name& other) const
+{
+ return (compare(other).getOrder() >= 0);
+}
+
+bool
+Name::lthan(const Name& other) const
+{
+ return (compare(other).getOrder() < 0);
+}
+
+bool
+Name::gthan(const Name& other) const
+{
+ return (compare(other).getOrder() > 0);
+}
+
+bool
+Name::isWildcard() const
+{
+ return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*');
+}
+
+namespace { // hide the local class
+///
+/// A helper functor class to add an additional offset to an offset vector.
+///
+struct OffsetAdjuster : public std::binary_function<unsigned char,
+ int, unsigned char> {
+ unsigned char operator()(unsigned char ch, int offset) const
+ {
+ return (ch + offset);
+ }
+};
+}
+
+Name
+Name::concatenate(const Name& suffix) const
+{
+ assert(this->length_ > 0 && suffix.length_ > 0);
+ assert(this->labelcount_ > 0 && suffix.labelcount_ > 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;
+
+ //
+ // Setup the offsets vector. Copy the offsets of this (prefix) name,
+ // excluding that for the trailing dot, and append the offsets of the
+ // suffix name with the additional offset of the length of the prefix.
+ //
+ unsigned int labels = this->labelcount_ + suffix.labelcount_ - 1;
+ assert(labels <= Name::MAX_LABELS);
+ retname.offsets_.reserve(labels);
+ retname.offsets_.assign(&this->offsets_[0],
+ &this->offsets_[0] + this->labelcount_ - 1);
+ transform(suffix.offsets_.begin(), suffix.offsets_.end(),
+ back_inserter(retname.offsets_),
+ bind2nd(OffsetAdjuster(), this->length_ - 1));
+ assert(retname.offsets_.size() == labels);
+ retname.labelcount_ = labels;
+
+ return (retname);
+}
+
+Name
+Name::split(unsigned int first, unsigned int n) const
+{
+ if (n == 0 || first + n > labelcount_) {
+ dns_throw(OutOfRange, "Name::split: invalid split range");
+ }
+
+ Name retname;
+ // If the specified range doesn't include the trailing dot, we need one
+ // more label for that.
+ unsigned int newlabels = (first + n == labelcount_) ? n : n + 1;
+
+ //
+ // Set up offsets: copy the corresponding range of the original offsets
+ // with subtracting an offset of the prefix length.
+ //
+ retname.offsets_.reserve(newlabels);
+ transform(offsets_.begin() + first, offsets_.begin() + first + newlabels,
+ back_inserter(retname.offsets_),
+ bind2nd(OffsetAdjuster(), -offsets_[first]));
+
+ //
+ // Set up the new name. At this point the tail of the new offsets specifies
+ // the position of the trailing dot, which should be equal to the length of
+ // the extracted portion excluding the dot. First copy that part from the
+ // original name, and append the trailing dot explicitly.
+ //
+ retname.ndata_.reserve(retname.offsets_.back() + 1);
+ retname.ndata_.assign(ndata_, offsets_[first], retname.offsets_.back());
+ retname.ndata_.push_back(0);
+
+ retname.length_ = retname.ndata_.size();
+ retname.labelcount_ = retname.offsets_.size();
+ assert(retname.labelcount_ == newlabels);
+
+ return (retname);
+}
+
+Name&
+Name::downcase()
+{
+ unsigned int nlen = length_;
+ unsigned int labels = labelcount_;
+ unsigned int pos = 0;
+
+ while (labels > 0 && nlen > 0) {
+ --labels;
+ --nlen;
+
+ // we assume a valid name, and do abort() if the assumption fails
+ // rather than throwing an exception.
+ unsigned int count = ndata_.at(pos++);
+ assert(count <= MAX_LABELLEN);
+ assert(nlen >= count);
while (count > 0) {
- count--;
- c = maptolower[(unsigned char)*label1++]; // XXX should avoid cast
- if (c != maptolower[(unsigned char)*label2++])
- return (false);
- }
- }
-
- return (true);
-}
-
-ostream&
-operator<<(ostream& os, const Name& name)
-{
- os << name.to_text();
+ ndata_.at(pos) =
+ maptolower[static_cast<unsigned char>(ndata_.at(pos))];
+ ++pos;
+ --nlen;
+ --count;
+ }
+ }
+
+ return (*this);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const Name& name)
+{
+ os << name.toText();
return (os);
}
+}
+}
Modified: branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h
==============================================================================
--- branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h (original)
+++ branches/jinmei-dnsrrparams/src/lib/dns/cpp/name.h Mon Jan 4 20:48:26 2010
@@ -17,81 +17,561 @@
#ifndef __NAME_H
#define __NAME_H 1
+#include <stdint.h>
+
#include <string>
#include <vector>
-namespace ISC {
-namespace DNS {
-class Buffer;
-class NameCompressor;
-class NameDecompressor;
-
+#include "exceptions.h"
+
+namespace isc {
+namespace dns {
+class InputBuffer;
+class OutputBuffer;
+class MessageRenderer;
+
+///
+/// \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 name parser
+/// finds the input (string or wire-format data) is incomplete.
+///
+/// An attempt of constructing a name from an empty string will trigger this
+/// exception.
+///
+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". This may be counter intuitive and inconvenient, but
+ /// we'll keep this design at the moment until we decide whether and how to
+ /// handle "non absolute" names (see the description of the \c Name class).
+ /// If we want to (re)introduce the notion of non absolute names, we'll
+ /// want to distinguish "com" and "com.", and the current definition would
+ /// be more compatible for that purpose.
+ /// If, on the other hand, we finally decide we really don't need that
+ /// notion, we'll probably reconsider the design here, too.
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 various properties of a name, etc.
+///
+/// Notes to developers: Internally, a name object maintains the name data
+/// in wire format as an instance of \c std::string. Since many string
+/// implementations adopt copy-on-write data sharing, we expect this approach
+/// will make copying a name less expensive in typical cases. If this is
+/// found to be a significant performance bottleneck later, we may reconsider
+/// the internal representation or perhaps the API.
+///
+/// A name object also maintains a vector of offsets (\c offsets_ member),
+/// each of which is the offset to a label of the name: The n-th element of
+/// the vector specifies the offset to the n-th label. For example, if the
+/// object represents "www.example.com", the elements of the offsets vector
+/// are 0, 4, 12, and 16. Note that the offset to the trailing dot (16) is
+/// included. In the BIND9 DNS library from which this implementation is
+/// derived, the offsets are optional, probably due to performance
+/// considerations (in fact, offsets can always be calculated from the name
+/// data, and in that sense are redundant). In our implementation, however,
+/// we always build and maintain the offsets. We believe we need more low
+/// level, specialized data structure and interface where we really need to
+/// pursue performance, and would rather keep this generic API and
+/// implementation simpler.
+///
+/// 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() : length_(0), labels_(0) {}
- explicit Name(const std::string& namestr) : length_(0), labels_(0)
- { from_string(namestr); }
- explicit Name(NameDecompressor& decompressor, Buffer& buffer);
- // copy constructor (default cp-ctor should work fine)
- //Name(const Name& orig);
- // destructor (default dtor should work fine)
- //~Name();
-
- std::string to_text(bool omit_final_dot = false) const;
- void to_wire(Buffer& buffer, NameCompressor& compressor) const;
- size_t get_length() const { return (length_); }
- unsigned int get_labels() const { return (labels_); }
+ ///
+ /// \name Constructors and Destructor
+ ///
+ //@{
+private:
+ /// The default constructor
+ ///
+ /// This is used internally in the class implementation, but at least at
+ /// the moment defined as private because it will construct an incomplete
+ /// object in that it doesn't have any labels. We may reconsider this
+ /// design choice as we see more applications of the class.
+ Name() : length_(0), labelcount_(0) {}
+public:
+ /// Constructor from a string
+ ///
+ /// If the given string does not represent a valid DNS name, an exception
+ /// of class \c EmptyLabel, \c TooLongLabel, \c BadLabelType, \c BadEscape,
+ /// \c TooLongName, or \c IncompleteName will be thrown.
+ /// In addition, if resource allocation for the new name fails, a
+ /// corresponding standard exception will be thrown.
+ ///
+ /// \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.
+ ///
+ /// If the given data does not represent a valid DNS name, an exception
+ /// of class \c TooLongName, \c BadLabelType, \c BadPointer, or
+ /// \c IncompleteName will be thrown.
+ /// In addition, if resource allocation for the new name fails, a
+ /// corresponding standard exception will be thrown.
+ ///
+ /// \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.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \return the length (the number of octets in wire format) 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.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \return the number of labels
+ unsigned int getLabelCount() const { return (labelcount_); }
+ //@}
+
+ ///
+ /// \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 BadLabelType will be thrown.
+ /// In addition, if resource allocation for the result string fails, a
+ /// corresponding standard exception 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;
+
+ /// \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.
+ ///
+ /// If resource allocation in rendering process fails, a corresponding
+ /// standard exception will be thrown.
+ ///
+ /// \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.
+ ///
+ /// If resource allocation in rendering process fails, a corresponding
+ /// standard exception will be thrown. This can be avoided by preallocating
+ /// a sufficient size of \c buffer. Specifically, if
+ /// <code>buffer.getCapacity() - buffer.getLength() >= Name::MAX_WIRE</code>
+ /// then this method should not throw an exception.
+ ///
+ 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 case-insensitive comparison.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \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.
+ ///
+ /// 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.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if the two names are equal; otherwise false.
+ bool equals(const Name& other) const;
+
+ /// Same as equals()
+ bool operator==(const Name& other) const { return (this->equals(other)); }
+
+ /// \brief Return true iff two names are not equal.
+ ///
+ /// This method simply negates the result of \c equal() method, and in that
+ /// sense it's redundant. The separate method is provided just for
+ /// convenience.
+ bool nequals(const Name& other) const { return !(this->equals(other)); }
+
+ /// Same as nequals()
+ bool operator!=(const Name& other) const { return (this->nequals(other)); }
+
+ /// \brief Less-than or equal comparison for Name against <code>other</code>
+ ///
+ /// The comparison is based on the result of the \c compare() method.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).getOrder() <= 0</code>;
+ /// otherwise false.
+ bool leq(const Name& other) const;
+
+ /// Same as leq()
+ bool operator<=(const Name& other) const { return (leq(other)); }
+
+ /// \brief Greater-than or equal comparison for Name against
+ /// <code>other</code>
+ ///
+ /// The comparison is based on the result of the \c compare() method.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).getOrder() >= 0</code>;
+ /// otherwise false.
+ bool geq(const Name& other) const;
+
+ /// Same as geq()
+ bool operator>=(const Name& other) const { return (geq(other)); }
+
+ /// \brief Less-than comparison for Name against <code>other</code>
+ ///
+ /// The comparison is based on the result of the \c compare() method.
+ ///
+ /// This method never throws an exception.
+ ///
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).getOrder() < 0</code>;
+ /// otherwise false.
+ bool lthan(const Name& other) const;
+
+ /// Same as lthan()
+ bool operator<(const Name& other) const { return (lthan(other)); }
+
+ /// \brief Greater-than comparison for Name against <code>other</code>
+ ///
+ /// The comparison is based on the result of the \c compare() method.
+ ////
+ /// This method never throws an exception.
+ ///
+ /// \param other the <code>Name</code> object to compare against.
+ /// \return true if <code>compare(other).getOrder() > 0</code>;
+ /// otherwise false.
+ bool gthan(const Name& other) const;
+
+ /// Same as gthan()
+ bool operator>(const Name& other) const { return (gthan(other)); }
+ //@}
+
+ ///
+ /// \name Transformer methods
+ ///
+ //@{
+ /// \brief Extract a specified subpart of Name.
+ ///
+ /// <code>name.split(first, n)</code> constructs a new name starting from
+ /// the <code>first</code>-th label of the \c name, and subsequent \c n
+ /// labels including the \c first one. Since names in this current
+ /// implementation are always "absolute", if the specified range doesn't
+ /// contain the trailing dot of the original \c name, then a dot will be
+ /// appended to the resulting name. As a result, the number of labels
+ /// will be <code>n + 1</code>, rather than \c n. For example,
+ /// when \c n is <code>Name("www.example.com")</code>,
+ /// both <code>n.split(1, 2)</code> and <code>n.split(1, 3)</code>
+ /// will produce a name corresponding to "example.com.", which has 3 labels.
+ /// Note also that labels are counted from 0, and so <code>first = 1</code>
+ /// in this example specified the label "example", not "www".
+ ///
+ /// Parameter \c n must be larger than 0, and the range specified by
+ /// \c first and \c n must not exceed the valid range of the original name;
+ /// otherwise, an exception of class \c OutOfRange will be thrown.
+ ///
+ /// Note to developers: 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 (in labels) 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. The trailing dot of
+ /// \c this Name will be removed. For example, if \c this is "www."
+ /// and \c suffix is "example.com.", a successful return of this method
+ /// will be a name of "www.example.com."
+ ///
+ ///The resulting length of the concatenated name must not exceed
+ /// \c Name::MAX_WIRE; otherwise an exception of class
+ /// \c TooLongName will be thrown.
+ ///
+ /// \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;
- bool is_wildcard() 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 >
+
+ /// \brief Downcase all upper case alphabet characters in the name.
+ ///
+ /// This method modifies the calling object so that it can perform the
+ /// conversion as fast as possible and can be exception free.
+ ///
+ /// The return value of this version of \c downcase() is a reference to
+ /// the calling object (i.e., \c *this) so that the caller can use the
+ /// result of downcasing in a single line. For example, if variable
+ /// \c n is a \c Name class object possibly containing upper case
+ /// characters, and \c b is an \c OutputBuffer class object, then the
+ /// following code will dump the name in wire format to \c b with
+ /// downcasing upper case characters:
+ ///
+ /// \code n.downcase().toWire(b); \endcode
+ ///
+ /// Since this method modifies the calling object, a \c const name object
+ /// cannot call it. If \c n is a \c const Name class object, it must first
+ /// be copied to a different object and the latter must be used for the
+ /// downcase modification.
+ ///
+ /// \return A reference to the calling object with being downcased.
+ Name& downcase();
+ //@}
+
+ ///
+ /// \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;
+ //@}
+
+ ///
+ /// \name Protocol constants
+ ///
+ //@{
+ /// \brief Max allowable length of domain names.
+ 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.
+ static const size_t MAX_LABELLEN = 63;
+
+ /// \brief Max possible pointer value for name compression.
+ ///
+ /// This is the highest number of 14-bit unsigned integer. Name compression
+ /// pointers are identified as a 2-byte value starting with the upper two
+ /// bit being 11.
+ static const uint16_t MAX_COMPRESS_POINTER = 0x3fff;
+ /// \brief A 8-bit masked value indicating a start of compression pointer.
+ static const uint16_t COMPRESS_POINTER_MARK8 = 0xc0;
+ /// \brief A 16-bit masked value indicating a start of compression pointer.
+ static const uint16_t COMPRESS_POINTER_MARK16 = 0xc000;
+ //@}
private:
- static const unsigned int MAXWIRE = 255;
-
std::string ndata_;
- std::vector<char> offsets_;
+ std::vector<unsigned char> offsets_;
unsigned int length_;
- unsigned int labels_;
-
- void from_string(const std::string& namestr);
-};
-
-std::ostream& operator<<(std::ostream& os, const Name& name);
+ unsigned int labelcount_;
+};
+
+///
+/// \brief Insert the name as a string into stream.
+///
+/// This method convert the \c name into a string and inserts it into the
+/// output stream \c os.
+///
+/// This function overloads the global operator<< to behave as described in
+/// ostream::operator<< but applied to \c Name objects.
+///
+/// \param os A \c std::ostream object on which the insertion operation is
+/// performed.
+/// \param name The \c Name object output by the operation.
+/// \return A reference to the same \c std::ostream object referenced by
+/// parameter \c os after the insertion operation.
+std::ostream&
+operator<<(std::ostream& os, const Name& name);
}
}
#endif // __NAME_H
More information about the bind10-changes
mailing list