From 248011badf0d73bc8dd429da7fb94cc85466493c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 23 Nov 2009 22:00:24 +0000 Subject: [PATCH 01/54] created a working branch for the production version of DNS message API git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@325 e5f2f494-b856-4b98-b285-d166d9295462 From da77b4881fc9bf43018734cc4b9c7c84fd7551e3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 24 Nov 2009 22:32:02 +0000 Subject: [PATCH 02/54] added a base exception class for the DNS library with a simple unit test case. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@326 e5f2f494-b856-4b98-b285-d166d9295462 --- Makefile.in | 4 +- src/lib/dns/cpp/Makefile.am | 4 +- src/lib/dns/cpp/exceptions.h | 92 ++++++++++++++++++++++++++ src/lib/dns/cpp/exceptions_unittest.cc | 34 ++++++++++ 4 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/lib/dns/cpp/exceptions.h create mode 100644 src/lib/dns/cpp/exceptions_unittest.cc diff --git a/Makefile.in b/Makefile.in index f101354c3d..19b104be19 100644 --- a/Makefile.in +++ b/Makefile.in @@ -34,8 +34,8 @@ POST_UNINSTALL = : subdir = . DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/config.h.in \ - $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \ - depcomp install-sh missing + $(top_srcdir)/configure AUTHORS COPYING ChangeLog NEWS depcomp \ + install-sh missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ diff --git a/src/lib/dns/cpp/Makefile.am b/src/lib/dns/cpp/Makefile.am index bb777d28e6..91ba2d3a98 100644 --- a/src/lib/dns/cpp/Makefile.am +++ b/src/lib/dns/cpp/Makefile.am @@ -1,10 +1,10 @@ lib_LIBRARIES = libdns.a -libdns_a_SOURCES = name.cc name.h +libdns_a_SOURCES = name.cc name.h exceptions.h TESTS = if HAVE_GTEST TESTS += run_unittests -run_unittests_SOURCES = run_unittests.cc +run_unittests_SOURCES = exceptions_unittest.cc run_unittests.cc run_unittests_CPPFLAGS = $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(GTEST_LDFLAGS) run_unittests_LDADD = ./libdns.a $(GTEST_LDADD) diff --git a/src/lib/dns/cpp/exceptions.h b/src/lib/dns/cpp/exceptions.h new file mode 100644 index 0000000000..a487a144da --- /dev/null +++ b/src/lib/dns/cpp/exceptions.h @@ -0,0 +1,92 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#ifndef __EXCEPTIONS_H +#define __EXCEPTIONS_H 1 + +#include + +namespace isc { +namespace dns { + +/// +/// This is a base class for exceptions thrown from the DNS library module. +/// Normally, the exceptions are thrown via a convenient shortcut macro, +/// @ref dns_throw, which automatically gives trivial parameters for the +/// exception such as the file name and line number where the exception is +/// triggered. +/// +class Exception { +public: + /// + /// \name Constructors and Destructor + /// + //@{ + /// \brief Constructor for a given type for exceptions with file name and + /// file line number. + /// + /// @param file the file name where the exception was thrown. + /// @param line the line in @ref file where the exception was thrown. + /// @param what a description (type) of the exception. + Exception(const char* const file, size_t line, const char* what) : + file_(file), line_(line), what_(what) {} + /// The destructor + virtual ~Exception() {} + //@} +private: + /// + /// The assignment operator is intentionally disabled. + /// + void operator=(const Exception& src); + +public: + /// + /// \name Getter Methods + /// + //@{ + /// \brief Gets a string describing the cause of the exception. + /// + /// @return the cause string. + const std::string& getMessage() const { return (what_); } + + /// \brief Gets the file name where the exception was thrown. + /// + /// @return a C-style string of the file name. + const char* getFile() const { return (file_); } + + /// \brief Gets the line number of the file where the exception was thrown. + /// + /// @return an integer specifying the line number. + size_t getLine() const { return (line_); } + +private: + const char* const file_; + size_t line_; + const std::string what_; +}; + +/// +/// A shortcut macro to insert known values into exception arguments. +/// +#define dns_throw(type, args...) throw type(__FILE__, __LINE__, args) + +} +} +#endif // __EXCEPTIONS_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/cpp/exceptions_unittest.cc b/src/lib/dns/cpp/exceptions_unittest.cc new file mode 100644 index 0000000000..37a0acf220 --- /dev/null +++ b/src/lib/dns/cpp/exceptions_unittest.cc @@ -0,0 +1,34 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#include "exceptions.h" + +#include + +namespace { + +using isc::dns::Exception; + +TEST(ExceptionTest, ExceptionTest) { + try { + dns_throw(Exception, "test"); + } catch (Exception& ex) { + EXPECT_EQ(ex.getMessage(), "test"); + EXPECT_EQ(ex.getFile(), std::string(__FILE__)); + EXPECT_EQ(ex.getLine(), __LINE__ - 4); + } +} +} From 33cd4b55dc7fea4afa2b11a95324d10f8c3ed611 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 2 Dec 2009 09:08:09 +0000 Subject: [PATCH 03/54] Input/Output buffer classes with design/API documentation and unittest cases. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@328 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/Makefile.am | 5 +- src/lib/dns/cpp/buffer.h | 366 +++++++++++++++++++++++++++++ src/lib/dns/cpp/buffer_unittest.cc | 151 ++++++++++++ src/lib/dns/cpp/exceptions.h | 2 +- src/lib/dns/cpp/name.cc | 5 +- src/lib/dns/cpp/name.h | 15 +- 6 files changed, 532 insertions(+), 12 deletions(-) create mode 100644 src/lib/dns/cpp/buffer.h create mode 100644 src/lib/dns/cpp/buffer_unittest.cc diff --git a/src/lib/dns/cpp/Makefile.am b/src/lib/dns/cpp/Makefile.am index 91ba2d3a98..c43f626373 100644 --- a/src/lib/dns/cpp/Makefile.am +++ b/src/lib/dns/cpp/Makefile.am @@ -1,10 +1,11 @@ lib_LIBRARIES = libdns.a -libdns_a_SOURCES = name.cc name.h exceptions.h +libdns_a_SOURCES = buffer.h name.cc name.h exceptions.h TESTS = if HAVE_GTEST TESTS += run_unittests -run_unittests_SOURCES = exceptions_unittest.cc run_unittests.cc +run_unittests_SOURCES = buffer_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) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h new file mode 100644 index 0000000000..600f83faa8 --- /dev/null +++ b/src/lib/dns/cpp/buffer.h @@ -0,0 +1,366 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#ifndef __BUFFER_H +#define __BUFFER_H 1 + +#include + +#include "exceptions.h" + +namespace isc { +namespace dns { + +/// +/// \brief A standard DNS module exception that is thrown if an out-of-range +/// buffer operation is being performed. +/// +class InvalidBufferPosition : public Exception { +public: + InvalidBufferPosition(const char* file, size_t line, const char* what) : + isc::dns::Exception(file, line, what) {} +}; + +///\brief The \c InputBuffer class is a buffer abstraction for manipulating +/// read-only data. +/// +/// The main purpose of this class is to provide a safe placeholder for +/// examining wire-format data received from a network. +/// +/// Applications normally use this class only in a limited situation: as an +/// interface between legacy I/O operation (such as receiving data from a BSD +/// socket) and the rest of the BIND10 DNS library. One common usage of this +/// class for an application would therefore be something like this: +/// +/// \code unsigned char buf[1024]; +/// struct sockaddr addr; +/// socklen_t addrlen = sizeof(addr); +/// int cc = recvfrom(s, buf, sizeof(buf), 0, &addr, &addrlen); +/// InputBuffer buffer(buf, cc); +/// // pass the buffer to a DNS message object to parse the message \endcode +/// +/// Other BIND10 DNS classes will then use methods of this class to get access +/// to the data, but the application normally doesn't have to care about the +/// details. +/// +/// An \c InputBuffer object internally holds a reference to the given data, +/// rather than make a local copy of the data. Also, it does not have an +/// ownership of the given data. It is application's responsibility to ensure +/// the data remains valid throughout the lifetime of the \c InputBuffer +/// object. Likewise, this object generally assumes the data isn't modified +/// throughout its lifetime; if the application modifies the data while this +/// object retains a reference to it, the result is undefined. The application +/// will also be responsible for releasing the data when it's not needed if it +/// was dynamically acquired. +/// +/// This is a deliberate design choice: although it's safer to make a local +/// copy of the given data on construction, it would cause unacceptable +/// performance overhead, especially considering that a DNS message can be +/// as large as a few KB. Alternatively, we could allow the object to allocate +/// memory internally and expose it to the application to store network data +/// in it. This is also a bad design, however, in that we would effectively +/// break the abstraction employed in the class, and do so by publishing +/// "read-only" stuff as a writable memory region. Since there doesn't seem to +/// be a perfect solution, we have adopted what we though a "least bad" one. +/// +/// Methods for reading data from the buffer generally work like an input +/// stream: it begins with the head of the data, and once some length of data +/// is read from the buffer, the next read operation will take place from the +/// head of the unread data. An object of this class internally holds (a +/// notion of) where the next read operation should start. We call it the +/// read position in this document. +class InputBuffer { +public: + /// + /// \name Constructors and Destructor + //@{ + /// \brief Constructor from variable length of data. + /// + /// It is caller's responsibility to ensure that the data is valid as long + /// as the buffer exists. + /// \param data A pointer to the data stored in the buffer. + /// \param len The length of the data in bytes. + InputBuffer(const void* data, size_t len) : + position_(0), len_(len), data_(static_cast(data)) {} + //@} + + /// + /// \name Getter Methods + //@{ + /// \brief Return the length of the data stored in the buffer. + size_t getLength() const { return (len_); } + /// \brief Return the current read position. + size_t getPosition() const { return (position_); } + //@} + + /// + /// \name Setter Methods + /// + //@{ + /// \brief Set the read position of the buffer to the given value. + /// + /// The new position must be in the valid range of the buffer; otherwise + /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown. + /// \param Position The new position (offset from the beginning of the + /// buffer). + void setPosition(size_t position) + { + if (position > len_) + dns_throw(InvalidBufferPosition, "position is too large"); + position_ = position; + } + //@} + + /// + /// \name Methods for reading data from the buffer. + //@{ + /// \brief Read an unsigned 8-bit integer from the buffer and return it. + /// + /// If the remaining length of the buffer is smaller than 8-bit, an + /// exception of class \c isc::dns::InvalidBufferPosition will be thrown. + uint8_t readUint8() + { + if (position_ + sizeof(uint8_t) > len_) { + dns_throw(InvalidBufferPosition, "read beyond end of buffer"); + } + + return (data_[position_++]); + } + /// \brief Read an unsigned 16-bit integer in network byte order from the + /// buffer, convert it to host byte order, and return it. + /// + /// If the remaining length of the buffer is smaller than 16-bit, an + /// exception of class \c isc::dns::InvalidBufferPosition will be thrown. + uint16_t readUint16() + { + uint16_t data; + const uint8_t* cp; + + if (position_ + sizeof(data) > len_) { + dns_throw(InvalidBufferPosition, "read beyond end of buffer"); + } + + cp = &data_[position_]; + data = ((unsigned int)(cp[0])) << 8; + data |= ((unsigned int)(cp[1])); + position_ += sizeof(data); + + return (data); + } + /// \brief Read an unsigned 32-bit integer in network byte order from the + /// buffer, convert it to host byte order, and return it. + /// + /// If the remaining length of the buffer is smaller than 32-bit, an + /// exception of class \c isc::dns::InvalidBufferPosition will be thrown. + uint32_t readUint32() + { + uint32_t data; + const uint8_t* cp; + + if (position_ + sizeof(data) > len_) { + dns_throw(InvalidBufferPosition, "read beyond end of buffer"); + } + + cp = &data_[position_]; + data = ((unsigned int)(cp[0])) << 24; + data |= ((unsigned int)(cp[1])) << 16; + data |= ((unsigned int)(cp[2])) << 8; + data |= ((unsigned int)(cp[3])); + position_ += sizeof(data); + + return (data); + } + /// \brief Read data of the specified length from the buffer and copy it to + /// the caller supplied buffer. + /// + /// The data is copied as stored in the buffer; no conversion is performed. + /// If the remaining length of the buffer is smaller than the specified + /// length, an exception of class \c isc::dns::InvalidBufferPosition will + /// be thrown. + void readData(void* data, size_t len) + { + if (position_ + len > len_) { + dns_throw(InvalidBufferPosition, "read beyond end of buffer"); + } + + memcpy(data, &data_[position_], len); + position_ += len; + } + //@} + +private: + size_t position_; + const uint8_t* data_; + size_t len_; +}; + +/// +///\brief The \c OutputBuffer class is a buffer abstraction for manipulating +/// mutable data. +/// +/// The main purpose of this class is to provide a safe workplace for +/// constructing wire-format data to be sent out to a network. Here, +/// safe means that it automatically allocates necessary memory and +/// avoid buffer overrun. +/// +/// Like for the \c InputBuffer class, applications normally use this class only +/// in a limited situation. One common usage of this class for an application +/// would be something like this: +/// +/// \code OutputBuffer buffer(4096); // give a sufficiently large initial size +/// // pass the buffer to a DNS message object to construct a wire-format +/// // DNS message. +/// struct sockaddr to; +/// sendto(s, buffer.getData(), buffer.getLength(), 0, &to, sizeof(to)); +/// \endcode +/// +/// where the \c getData() method gives a reference to the internal memory +/// region stored in the \c buffer object. This is a suboptimal design in that +/// it exposes an encapsulated "handle" of an object to its user. +/// Unfortunately, there is no easy way to avoid this without involving +/// expensive data copy if we want to use this object with a legacy API such as +/// a BSD socket interface. And, indeed, this is one major purpose for this +/// object. Applications should use this method only under such a special +/// circumstance. +/// +/// An \c OutputBuffer class object automatically extends its memory region when +/// data is written beyond the end of the current buffer. However, it will +/// involve performance overhead such as reallocating more memory and copying +/// data. It is therefore recommended to construct the buffer object with a +/// sufficiently large initial size. +/// The \c getCapacity() method provides the current maximum size of data +/// (including the portion already written) that can be written into the buffer +/// without causing memory reallocation. +/// +/// Methods for writing data into the buffer generally work like an output +/// stream: it begins with the head of the buffer, and once some length of data +/// is written into the buffer, the next write operation will take place from +/// the end of the buffer. Other methods to emulate "random access" are also +/// provided (e.g., \c writeUint16At()). The normal write operations are +/// normally exception-free as this class automatically extends the buffer +/// when necessary. However, in extreme cases such as an attempt of writing +/// multi-GB data, a separate exception (e.g., \c std::bad_alloc) may be thrown +/// by the system. This also applies to the constructor with a very large +/// initial size. +class OutputBuffer { +public: + /// + /// \name Constructors and Destructor + /// + //@{ + /// \brief Constructor from the initial size of the buffer. + /// + /// \param len The initial length of the buffer in bytes. + OutputBuffer(size_t len) { data_.reserve(len); } + //@} + + /// + /// \name Getter Methods + /// + //@{ + /// \brief Return the current capacity of the buffer. + size_t getCapacity() const { return (data_.capacity()); } + /// \brief Return a pointer to the head of the data stored in the buffer. + /// + /// The caller can assume that the subsequent \c getLength() bytes are + /// identical to the stored data of the buffer. + /// + /// Note: The pointer returned by this method may be invalidated after a + /// subsequent write operation. + const void* getData() const { return (&data_[0]); } + /// \brief Return the length of data written in the buffer. + size_t getLength() const { return (data_.size()); } + //@} + + /// + /// \name Methods for writing data into the buffer. + /// + //@{ + /// \brief Insert a specified length of gap at the end of the buffer. + /// + /// The caller should not assume any particular value to be inserted. + /// This method is provided as a shortcut to make a hole in the buffer + /// 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 Write an unsigned 8-bit integer into the buffer. + /// + /// \param data The 8-bit integer to be written into the buffer. + void writeUint8(uint8_t data) { data_.push_back(data); } + + /// \brief Write an unsigned 16-bit integer in host byte order into the + /// buffer in network byte order. + /// + /// \param data The 16-bit integer to be written into the buffer. + void writeUint16(uint16_t data) + { + data_.push_back(static_cast((data & 0xff00U) >> 8)); + data_.push_back(static_cast(data & 0x00ffU)); + } + /// \brief Write an unsigned 16-bit integer in host byte order at the + /// specified position of the buffer in network byte order. + /// + /// The buffer must have a sufficient room to store the given data at the + /// given position, that is, pos + 2 < getLength(); + /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will + /// be thrown. + /// Note also that this method never extend the buffer. + /// + /// \param data The 16-bit integer to be written into the buffer. + /// \param pos The beginning position in the buffer to write the data. + void writeUint16At(uint16_t data, size_t pos) + { + if (pos + sizeof(data) >= data_.size()) { + dns_throw(InvalidBufferPosition, "write at invalid position"); + } + + data_[pos] = static_cast((data & 0xff00U) >> 8); + data_[pos + 1] = static_cast(data & 0x00ffU); + } + /// \brief Write an unsigned 32-bit integer in host byte order + /// into the buffer in network byte order. + /// + /// \param data The 32-bit integer to be written into the buffer. + void writeUint32(uint32_t data) + { + data_.push_back(static_cast((data & 0xff000000) >> 24)); + data_.push_back(static_cast((data & 0x00ff0000) >> 16)); + data_.push_back(static_cast((data & 0x0000ff00) >> 8)); + data_.push_back(static_cast(data & 0x000000ff)); + } + /// \brief Copy an arbitrary length of data into the buffer. + /// + /// No conversion on the copied data is performed. + /// + /// \param data A pointer to the data to be copied into the buffer. + /// \param len The length of the data in bytes. + void writeData(const void *data, size_t len) + { + const uint8_t* cp = static_cast(data); + data_.insert(data_.end(), cp, cp + len); + } + //@} + +private: + std::vector data_; +}; +} +} +#endif // __BUFFER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/cpp/buffer_unittest.cc b/src/lib/dns/cpp/buffer_unittest.cc new file mode 100644 index 0000000000..07f7d9c3a1 --- /dev/null +++ b/src/lib/dns/cpp/buffer_unittest.cc @@ -0,0 +1,151 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#include "buffer.h" + +#include + +namespace { + +using isc::dns::InputBuffer; +using isc::dns::OutputBuffer; + +class BufferTest : public ::testing::Test { +protected: + BufferTest() : ibuffer(testdata, sizeof(testdata)), obuffer(0), + expected_size(0) + { + data16 = (2 << 8) | 3; + data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7; + } + + InputBuffer ibuffer; + OutputBuffer obuffer; + static const uint8_t testdata[5]; + uint8_t vdata[sizeof(testdata)]; + size_t expected_size; + uint16_t data16; + uint32_t data32; +}; + +const uint8_t BufferTest::testdata[5] = {1, 2, 3, 4, 5}; + +TEST_F(BufferTest, input_buffer_read) +{ + EXPECT_EQ(5, ibuffer.getLength()); + EXPECT_EQ(1, ibuffer.readUint8()); + EXPECT_EQ(1, ibuffer.getPosition()); + data16 = ibuffer.readUint16(); + EXPECT_EQ((2 << 8) | 3, data16); + EXPECT_EQ(3, ibuffer.getPosition()); + ibuffer.setPosition(1); + EXPECT_EQ(1, ibuffer.getPosition()); + data32 = ibuffer.readUint32(); + EXPECT_EQ((2 << 24) | (3 << 16) | (4 << 8) | 5, data32); + ibuffer.setPosition(0); + memset(vdata, 0, sizeof(vdata)); + ibuffer.readData(vdata, sizeof(vdata)); + EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata))); +} + +TEST_F(BufferTest, input_buffer_exception) +{ + EXPECT_THROW(ibuffer.setPosition(6), isc::dns::InvalidBufferPosition); + + ibuffer.setPosition(sizeof(testdata)); + EXPECT_THROW(ibuffer.readUint8(), isc::dns::InvalidBufferPosition); + + ibuffer.setPosition(sizeof(testdata) - 1); + EXPECT_THROW(ibuffer.readUint16(), isc::dns::InvalidBufferPosition); + + ibuffer.setPosition(sizeof(testdata) - 3); + EXPECT_THROW(ibuffer.readUint32(), isc::dns::InvalidBufferPosition); + + ibuffer.setPosition(sizeof(testdata) - 4); + EXPECT_THROW(ibuffer.readData(vdata, sizeof(vdata)), + isc::dns::InvalidBufferPosition); +} + +TEST_F(BufferTest, output_buffer_extend) +{ + EXPECT_EQ(0, obuffer.getCapacity()); + EXPECT_EQ(0, obuffer.getLength()); + obuffer.writeUint8(10); + EXPECT_LT(0, obuffer.getCapacity()); + EXPECT_EQ(1, obuffer.getLength()); +} + +TEST_F(BufferTest, output_buffer_write) +{ + const uint8_t* cp; + + obuffer.writeUint8(1); + expected_size += sizeof(uint8_t); + EXPECT_EQ(expected_size, obuffer.getLength()); + cp = static_cast(obuffer.getData()); + EXPECT_EQ(1, *cp); + + obuffer.writeUint16(data16); + expected_size += sizeof(data16); + cp = static_cast(obuffer.getData()); + EXPECT_EQ(expected_size, obuffer.getLength()); + EXPECT_EQ(2, *(cp + 1)); + EXPECT_EQ(3, *(cp + 2)); + + obuffer.writeUint32(data32); + expected_size += sizeof(data32); + cp = static_cast(obuffer.getData()); + EXPECT_EQ(expected_size, obuffer.getLength()); + EXPECT_EQ(4, *(cp + 3)); + EXPECT_EQ(5, *(cp + 4)); + EXPECT_EQ(6, *(cp + 5)); + EXPECT_EQ(7, *(cp + 6)); + + obuffer.writeData(testdata, sizeof(testdata)); + expected_size += sizeof(testdata); + EXPECT_EQ(expected_size, obuffer.getLength()); + cp = static_cast(obuffer.getData()); + EXPECT_EQ(0, memcmp(cp + 7, testdata, sizeof(testdata))); +} + +TEST_F(BufferTest, output_buffer_writeat) +{ + obuffer.writeUint32(data32); + expected_size += sizeof(data32); + obuffer.writeUint16At(data16, 1); + EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change + + const uint8_t* cp = static_cast(obuffer.getData()); + EXPECT_EQ(2, *(cp + 1)); + EXPECT_EQ(3, *(cp + 2)); + + EXPECT_THROW(obuffer.writeUint16At(data16, 3), + isc::dns::InvalidBufferPosition); + EXPECT_THROW(obuffer.writeUint16At(data16, 4), + isc::dns::InvalidBufferPosition); + EXPECT_THROW(obuffer.writeUint16At(data16, 5), + isc::dns::InvalidBufferPosition); +} + +TEST_F(BufferTest, output_buffer_skip) +{ + obuffer.skip(4); + EXPECT_EQ(4, obuffer.getLength()); + + obuffer.skip(2); + EXPECT_EQ(6, obuffer.getLength()); +} +} diff --git a/src/lib/dns/cpp/exceptions.h b/src/lib/dns/cpp/exceptions.h index a487a144da..897dca8f43 100644 --- a/src/lib/dns/cpp/exceptions.h +++ b/src/lib/dns/cpp/exceptions.h @@ -41,7 +41,7 @@ public: /// @param file the file name where the exception was thrown. /// @param line the line in @ref file where the exception was thrown. /// @param what a description (type) of the exception. - Exception(const char* const file, size_t line, const char* what) : + Exception(const char* file, size_t line, const char* what) : file_(file), line_(line), what_(what) {} /// The destructor virtual ~Exception() {} diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index ff0007ae50..add66a4c1b 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -16,6 +16,7 @@ #include +#include "buffer.h" #include "name.h" using namespace std; @@ -278,7 +279,7 @@ Name::from_string(const string &namestring) } string -Name::to_text(bool omit_final_dot) const +Name::toText(bool omit_final_dot) const { string tdata; unsigned int nlen; @@ -429,6 +430,6 @@ Name::operator==(const Name& other) const ostream& operator<<(ostream& os, const Name& name) { - os << name.to_text(); + os << name.toText(); return (os); } diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 349c550181..cc7d476014 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -22,7 +22,8 @@ namespace ISC { namespace DNS { -class Buffer; +class InputBuffer; +class OutputBuffer; class NameCompressor; class NameDecompressor; @@ -53,20 +54,20 @@ 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); + 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(); - 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_); } + 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_); } NameComparisonResult compare(const Name& other) const; Name split(unsigned int first, unsigned int n) const; Name concatenate(const Name& suffix) const; - bool is_wildcard() const; + 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)); } From 3bf63c39e567d185b0f53659a0eecb3fd7dd8e90 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 2 Dec 2009 09:10:18 +0000 Subject: [PATCH 04/54] wording fix to the document git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@329 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h index 600f83faa8..526014696a 100644 --- a/src/lib/dns/cpp/buffer.h +++ b/src/lib/dns/cpp/buffer.h @@ -317,7 +317,7 @@ public: /// given position, that is, pos + 2 < getLength(); /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will /// be thrown. - /// Note also that this method never extend the buffer. + /// Note also that this method never extends the buffer. /// /// \param data The 16-bit integer to be written into the buffer. /// \param pos The beginning position in the buffer to write the data. From c0f55021fed6e9f3c62a9cfc7fb0ecf2a208781e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 2 Dec 2009 09:11:47 +0000 Subject: [PATCH 05/54] another wording fix git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@330 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h index 526014696a..c371736038 100644 --- a/src/lib/dns/cpp/buffer.h +++ b/src/lib/dns/cpp/buffer.h @@ -74,7 +74,7 @@ public: /// in it. This is also a bad design, however, in that we would effectively /// break the abstraction employed in the class, and do so by publishing /// "read-only" stuff as a writable memory region. Since there doesn't seem to -/// be a perfect solution, we have adopted what we though a "least bad" one. +/// be a perfect solution, we have adopted what we thought a "least bad" one. /// /// Methods for reading data from the buffer generally work like an input /// stream: it begins with the head of the data, and once some length of data From a80c0f870ac571cc2338178cea26646fea206542 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 10 Dec 2009 23:03:29 +0000 Subject: [PATCH 06/54] mostly complete name class implementation. still need some more work to be review-able, but committed for share. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@354 e5f2f494-b856-4b98-b285-d166d9295462 --- doc/Doxyfile | 2 +- src/lib/dns/cpp/Makefile.am | 7 +- src/lib/dns/cpp/buffer.h | 18 + src/lib/dns/cpp/buffer_unittest.cc | 16 + src/lib/dns/cpp/exceptions.h | 10 + src/lib/dns/cpp/messagerenderer.cc | 160 +++++ src/lib/dns/cpp/messagerenderer.h | 41 ++ src/lib/dns/cpp/messagerenderer_unittest.cc | 74 +++ src/lib/dns/cpp/name.cc | 681 +++++++++++--------- src/lib/dns/cpp/name.h | 382 ++++++++++- src/lib/dns/cpp/name_unittest.cc | 259 ++++++++ src/lib/dns/cpp/testdata/name_fromWire1 | 14 + src/lib/dns/cpp/testdata/name_fromWire2 | 15 + src/lib/dns/cpp/testdata/name_fromWire3_1 | 11 + src/lib/dns/cpp/testdata/name_fromWire3_2 | 13 + src/lib/dns/cpp/testdata/name_fromWire4 | 45 ++ src/lib/dns/cpp/testdata/name_fromWire6 | 14 + src/lib/dns/cpp/testdata/name_fromWire7 | 6 + src/lib/dns/cpp/testdata/name_fromWire8 | 27 + src/lib/dns/cpp/testdata/name_toWire1 | 12 + src/lib/dns/cpp/testdata/name_toWire2 | 14 + src/lib/dns/cpp/testdata/name_toWire3 | 14 + src/lib/dns/cpp/unittest_util.cc | 74 +++ src/lib/dns/cpp/unittest_util.h | 46 ++ 24 files changed, 1631 insertions(+), 324 deletions(-) create mode 100644 src/lib/dns/cpp/messagerenderer.cc create mode 100644 src/lib/dns/cpp/messagerenderer.h create mode 100644 src/lib/dns/cpp/messagerenderer_unittest.cc create mode 100644 src/lib/dns/cpp/name_unittest.cc create mode 100644 src/lib/dns/cpp/testdata/name_fromWire1 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire2 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire3_1 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire3_2 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire4 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire6 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire7 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire8 create mode 100644 src/lib/dns/cpp/testdata/name_toWire1 create mode 100644 src/lib/dns/cpp/testdata/name_toWire2 create mode 100644 src/lib/dns/cpp/testdata/name_toWire3 create mode 100644 src/lib/dns/cpp/unittest_util.cc create mode 100644 src/lib/dns/cpp/unittest_util.h diff --git a/doc/Doxyfile b/doc/Doxyfile index 34d3e2c973..422e2e367a 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -611,7 +611,7 @@ EXCLUDE_SYMLINKS = NO # 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 diff --git a/src/lib/dns/cpp/Makefile.am b/src/lib/dns/cpp/Makefile.am index c43f626373..863e67bf63 100644 --- a/src/lib/dns/cpp/Makefile.am +++ b/src/lib/dns/cpp/Makefile.am @@ -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) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h index c371736038..c259933bfd 100644 --- a/src/lib/dns/cpp/buffer.h +++ b/src/lib/dns/cpp/buffer.h @@ -283,6 +283,19 @@ public: 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 @@ public: /// 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. diff --git a/src/lib/dns/cpp/buffer_unittest.cc b/src/lib/dns/cpp/buffer_unittest.cc index 07f7d9c3a1..91babaa5d3 100644 --- a/src/lib/dns/cpp/buffer_unittest.cc +++ b/src/lib/dns/cpp/buffer_unittest.cc @@ -148,4 +148,20 @@ TEST_F(BufferTest, output_buffer_skip) 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()); +} } diff --git a/src/lib/dns/cpp/exceptions.h b/src/lib/dns/cpp/exceptions.h index 897dca8f43..914ddb6d92 100644 --- a/src/lib/dns/cpp/exceptions.h +++ b/src/lib/dns/cpp/exceptions.h @@ -78,6 +78,16 @@ private: const std::string what_; }; +/// +/// \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. /// diff --git a/src/lib/dns/cpp/messagerenderer.cc b/src/lib/dns/cpp/messagerenderer.cc new file mode 100644 index 0000000000..cc08f2faaf --- /dev/null +++ b/src/lib/dns/cpp/messagerenderer.cc @@ -0,0 +1,160 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#include +#include +#include + +#include + +#include "buffer.h" +#include "name.h" +#include "messagerenderer.h" + +namespace isc { +namespace dns { + +struct NameCompressNode { + NameCompressNode(const OutputBuffer& buffer, size_t pos, size_t len) : + buffer_(buffer), pos_(pos), len_(len) {} + const OutputBuffer& buffer_; + uint16_t pos_; + uint16_t len_; +}; + +/// +/// Helper class to give ordering for MessageRendererImpl::nodeset_. +/// +struct NameCompare : public std::binary_function { + /// + /// Returns true if n1 < n2 as a result of case-insensitive comparison; + /// otherwise return false. + /// + bool operator()(const NameCompressNode& n1, + const NameCompressNode& n2) const + { + if (n1.len_ < n2.len_) { + return (true); + } else if (n1.len_ > n2.len_) { + return (false); + } + + uint16_t pos1 = n1.pos_; + uint16_t pos2 = n2.pos_; + uint16_t l1 = 0; + uint16_t l2 = 0; + for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) { + pos1 = nextPosition(n1.buffer_, pos1, l1); + pos2 = nextPosition(n2.buffer_, pos2, l2); + if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) { + return (true); + } else if (tolower(n1.buffer_[pos1]) > tolower(n2.buffer_[pos2])) { + return (false); + } + } + + return (false); + } + + uint16_t nextPosition(const OutputBuffer& buffer, + uint16_t pos, uint16_t& llen) const + { + if (llen == 0) { + int i = 0; + while ((buffer[pos] & 0xc0) == 0xc0) { + pos = (buffer[pos] & 0x3f) * 256 + buffer[pos + 1]; + + // This loop should stop as long as the buffer has been + // constructed validly and the search/insert argument is based + // on a valid name, which is an assumption for this class. + // But we'll abort if a bug could cause an infinite loop. + i += 2; + assert(i < Name::MAX_WIRE); + } + llen = buffer[pos]; + } else + --llen; + return (pos); + } +}; + +struct MessageRendererImpl { + MessageRendererImpl(OutputBuffer& buffer) : + buffer_(buffer), nbuffer_(Name::MAX_WIRE) {} + OutputBuffer& buffer_; + OutputBuffer nbuffer_; + std::set nodeset_; +}; + +MessageRenderer::MessageRenderer(OutputBuffer& buffer) +{ + impl_ = new MessageRendererImpl(buffer); +} + +MessageRenderer::~MessageRenderer() +{ + delete impl_; +} + +void +MessageRenderer::writeName(const Name& name, bool compress) +{ + impl_->nbuffer_.clear(); + name.toWire(impl_->nbuffer_); + + unsigned int i; + std::set::const_iterator n; + + for (i = 0; i < impl_->nbuffer_.getLength(); i += impl_->nbuffer_[i] + 1) { + // skip the trailing null label + if (impl_->nbuffer_[i] == 0) { + continue; + } + n = impl_->nodeset_.find(NameCompressNode(impl_->nbuffer_, i, + impl_->nbuffer_.getLength() - + i)); + if (n != impl_->nodeset_.end()) { + break; + } + } + + size_t offset = impl_->buffer_.getLength(); + // write uncompress part... + impl_->buffer_.writeData(impl_->nbuffer_.getData(), + compress ? i : impl_->nbuffer_.getLength()); + if (compress && n != impl_->nodeset_.end()) { + // ...and compression pointer if available. + uint16_t pointer = (*n).pos_; + pointer |= 0xc000; + impl_->buffer_.writeUint16(pointer); + } + + for (unsigned int j = 0; j < i; j += impl_->nbuffer_[j] + 1) { + if (impl_->nbuffer_[j] == 0) { + continue; + } + if (offset + j >= 0x4000) { + break; + } + impl_->nodeset_.insert(NameCompressNode(impl_->buffer_, offset + j, + impl_->nbuffer_.getLength() - + j)); + } +} +} +} diff --git a/src/lib/dns/cpp/messagerenderer.h b/src/lib/dns/cpp/messagerenderer.h new file mode 100644 index 0000000000..b7a8ed348d --- /dev/null +++ b/src/lib/dns/cpp/messagerenderer.h @@ -0,0 +1,41 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#ifndef __MESSAGERENDERER_H +#define __MESSAGERENDERER_H 1 + +namespace isc { +namespace dns { +// forward declarations +class OutputBuffer; +class Name; +class MessageRendererImpl; + +class MessageRenderer { +public: + MessageRenderer(OutputBuffer& buffer); + ~MessageRenderer(); + void writeName(const Name& name, bool compress = true); +private: + MessageRendererImpl* impl_; +}; +} +} +#endif // __MESSAGERENDERER_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/cpp/messagerenderer_unittest.cc b/src/lib/dns/cpp/messagerenderer_unittest.cc new file mode 100644 index 0000000000..d4c01ef233 --- /dev/null +++ b/src/lib/dns/cpp/messagerenderer_unittest.cc @@ -0,0 +1,74 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#include + +#include "buffer.h" +#include "name.h" +#include "messagerenderer.h" + +#include "unittest_util.h" + +#include + +using isc::UnitTestUtil; +using isc::dns::OutputBuffer; +using isc::dns::Name; +using isc::dns::MessageRenderer; + +namespace { +class MessageRendererTest : public ::testing::Test { +protected: + MessageRendererTest() : buffer(0), renderer(buffer) {} + OutputBuffer buffer; + MessageRenderer renderer; + std::vector data; +}; + +TEST_F(MessageRendererTest, toWire) +{ + UnitTestUtil::readWireData("testdata/name_toWire1", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + renderer.writeName(Name("a.example.org.")); + EXPECT_EQ(true, buffer.getLength() == data.size() && + memcmp(buffer.getData(), &data[0], data.size()) == 0); +} + +TEST_F(MessageRendererTest, toWireInLargeBuffer) +{ + size_t offset = 0x3fff; + buffer.skip(offset); + + UnitTestUtil::readWireData("testdata/name_toWire2", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + EXPECT_EQ(true, buffer.getLength() == data.size() + offset && + memcmp(static_cast(buffer.getData()) + offset, + &data[0], data.size()) == 0); +} + +TEST_F(MessageRendererTest, toWireTBD) +{ + UnitTestUtil::readWireData("testdata/name_toWire3", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com."), false); + renderer.writeName(Name("b.example.com.")); + EXPECT_EQ(true, buffer.getLength() == data.size() && + memcmp(buffer.getData(), &data[0], data.size()) == 0); +} +} diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index add66a4c1b..9c8b934d7d 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -14,25 +14,55 @@ // $Id$ -#include +#include +#include #include "buffer.h" #include "name.h" +#include "messagerenderer.h" -using namespace std; -using namespace ISC::DNS; +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 {}; +using isc::dns::NameComparisonResult; +using isc::dns::MessageRenderer; -class DNSException {}; -class DNSEmptyLabel : public DNSException {}; -class DNSLabelTooLong : public DNSException {}; -class DNSBadEscape : public DNSException {}; -class DNSBadLabelType : public DNSException {}; +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 @@ typedef enum { 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 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; + 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; - while (nrem > 0 && tlen > 0 && !done) { - c = namestring[pos++]; - tlen--; - tused++; + // 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 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; - } + dns_throw(IncompleteName, "incomplete wire-format name"); } - labels_ = labels; + labels_ = offsets.size(); length_ = nused; + offsets_.assign(offsets.begin(), offsets.end()); + buffer.setPosition(pos_begin + cused); } -string +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; - 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; + if (length_ == 1) { + // + // Special handling for the root label. We ignore omit_final_dot. + // + assert(labels_ == 1 && ndata_[0] == '\0'); + return ("."); } - while (labels > 0 && nlen > 0) { + unsigned int count; + 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('.'); + } else { + dns_throw(BadLabelType, "unknown label type in name data"); + } } - if (nlen != 0) - throw ISCNoSpace(); + assert(labels == 0); + assert(count == 0); // a valid name must end with a 'dot'. - if (!saw_root || omit_final_dot) - tdata.erase(tdata.end() - 1); + return (result); +} - return (tdata); +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 @@ Name::operator==(const Name& other) const { unsigned int l; unsigned char c, count; - string::const_iterator label1, label2; + std::string::const_iterator label1, label2; - if (length_ != other.length_) + 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 @@ Name::operator==(const Name& other) const 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); } + +} +} diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index cc7d476014..af6e8f745a 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -20,76 +20,390 @@ #include #include -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 + /// name1->compare(name2) (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); + //@} - std::string toText(bool omit_final_dot = false) const; - void toWire(OutputBuffer& buffer, NameCompressor& compressor) const; + /// + /// \name Getter Methods + /// + //@{ + /// \brief Gets the length of the Name in its wire format. + /// + /// \return the length of the Name size_t getLength() const { return (length_); } + + /// \brief Returns the number of labels contained in the Name. + /// + /// 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 std::string object representing the + /// Name as a string. Unless omit_final_dot is + /// true, the returned string ends with a dot '.'; the default + /// is false. The default value of this parameter is + /// true; 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 Name. + std::string toText(bool omit_final_dot = false) const; + + /// \brief Render the Name 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 Name in the wire format without + /// compression. + /// + void toWire(OutputBuffer& buffer) const; + //@} + + /// + /// \name Comparison methods + /// + //@{ + /// \brief Compare two Names. + /// + /// This method compares the Name and other and + /// returns the result in the form of a NameComparisonResult + /// object. + /// + /// Note that this is a case-insensitive comparison. + /// + /// \param other the right-hand operand to compare against. + /// \return a NameComparisonResult object representing the + /// comparison result. NameComparisonResult compare(const Name& other) const; - Name split(unsigned int first, unsigned int n) const; - Name concatenate(const Name& suffix) const; - bool isWildcard() const; + + /// \brief Return true iff two names are equal. + /// + /// The comparison is based on the result of the compare() method. + /// \param other the Name object to compare against. + /// \return true if compare(other).get_order() is 0; + /// otherwise false. + bool equals(const Name& other) const; + + /// Same as equals() bool operator==(const Name& other) const; - bool equals(const Name& other) const; // alias of == + + /// \brief Return true iff two names are not equal. + /// + /// The comparison is based on the result of the compare() method. + /// \param other the Name object to compare against. + /// \return true if compare(other).get_order() is non 0; + /// otherwise false. + bool nequals(const Name& other) const; + + /// Same as nequals() bool operator!=(const Name& other) const { return (!(*this == other)); } - bool nequals(const Name& other) const; // alias of != + + /// \brief Less-than or equal comparison for Name against other + /// + /// The comparison is based on the result of the compare() method. + /// \param other the Name object to compare against. + /// \return true if compare(other).get_order() <= 0; + /// otherwise false. + bool leq(const Name& other) const; + + /// Same as leq() bool operator<=(const Name& other) const; - bool leq(const Name& other) const; // alias of <= + + /// \brief Greater-than or equal comparison for Name against + /// other + /// + /// The comparison is based on the result of the compare() method. + /// \param other the Name object to compare against. + /// \return true if compare(other).get_order() >= 0; + /// otherwise false. + bool geq(const Name& other) const; + + /// Same as geq() bool operator>=(const Name& other) const; - bool geq(const Name& other) const; // alias of >= + + /// \brief Less-than comparison for Name against other + /// + /// The comparison is based on the result of the compare() method. + /// \param other the Name object to compare against. + /// \return true if compare(other).get_order() < 0; + /// otherwise false. + bool lthan(const Name& other) const; + + /// Same as lthan() bool operator<(const Name& other) const; - bool lthan(const Name& other) const; // alias of < + + /// \brief Greater-than comparison for Name against other + /// + /// The comparison is based on the result of the compare() method. + /// \param other the Name object to compare against. + /// \return true if compare(other).get_order() > 0; + /// otherwise false. + bool gthan(const Name& other) const; + + /// Same as gthan() bool operator>(const Name& other) const; - bool gthan(const Name& other) const; // alias of > + //@} + + /// + /// \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 n + /// labels including and following the first 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 + /// '*'; 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 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 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); diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc new file mode 100644 index 0000000000..cb6395674b --- /dev/null +++ b/src/lib/dns/cpp/name_unittest.cc @@ -0,0 +1,259 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#include +#include +#include + +#include "buffer.h" +#include "name.h" +#include "messagerenderer.h" + +#include "unittest_util.h" + +#include + +using isc::UnitTestUtil; +using isc::dns::InputBuffer; +using isc::dns::OutputBuffer; +using isc::dns::Name; +using isc::dns::NameComparisonResult; + +namespace { +class NameTest : public ::testing::Test { +protected: + NameTest() : example_name("www.example.com") {} + Name example_name; + + // + // helper methods + // + Name nameFactoryFromWire(const char* datafile, size_t position, + bool downcase = false); +}; + +Name +NameTest::nameFactoryFromWire(const char* datafile, size_t position, + bool downcase) +{ + std::vector data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + return (Name(buffer, downcase)); +} + +TEST_F(NameTest, fromText) +{ + std::vector strnames; + strnames.push_back("www.example.com"); + strnames.push_back("www.example.com."); // with a trailing dot + strnames.push_back("wWw.exAmpLe.com"); // mixed cases + strnames.push_back("\\wWw.exAmpLe.com"); // escape with a backslash + // decimal representation for "WWW" + strnames.push_back("\\087\\087\\087.example.com"); + + std::vector::const_iterator it; + for (it = strnames.begin(); it != strnames.end(); ++it) { + EXPECT_EQ(true, example_name == Name(*it)); + } + + // root names + EXPECT_EQ(true, Name("@") == Name(".")); + + // downcase + EXPECT_EQ(Name("Www.eXample.coM", true).toText(), example_name.toText()); + + // + // Tests for bogus names. These should trigger an exception. + // + // empty label cannot be followed by another label + EXPECT_THROW(Name(".a"), isc::dns::EmptyLabel); + // duplicate period + EXPECT_THROW(Name("a.."), isc::dns::EmptyLabel); + // label length must be < 64 + EXPECT_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "0123"), isc::dns::TooLongLabel); + // now-unsupported bitstring labels + EXPECT_THROW(Name("\\[b11010000011101]"), isc::dns::BadLabelType); + // label length must be < 64 + EXPECT_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "012\\x"), isc::dns::TooLongLabel); + // but okay as long as resulting len < 64 even if the original string is + // "too long" + EXPECT_NO_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "01\\x")); + // incomplete \DDD pattern (exactly 3 D's must appear) + EXPECT_THROW(Name("\\12abc"), isc::dns::BadEscape); + // \DDD must not exceed 255 + EXPECT_THROW(Name("\\256"), isc::dns::BadEscape); + // Same tests for \111 as for \\x above + EXPECT_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "012\\111"), isc::dns::TooLongLabel); + EXPECT_NO_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "01\\111")); + // A domain name must be 255 octets or less + EXPECT_THROW(Name("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "1234"), isc::dns::TooLongName); + // This is a possible longest name and should be accepted + EXPECT_NO_THROW(Name("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123")); + // \DDD must consist of 3 digits. + EXPECT_THROW(Name("\\12"), isc::dns::BadLabelType); +} + +TEST_F(NameTest, fromWire) +{ + // + // test cases derived from BIND9 tests. + // + // normal case with a compression pointer + EXPECT_EQ(true, nameFactoryFromWire("testdata/name_fromWire1", 25) == + Name("vix.com")); + // bogus label character (looks like a local compression pointer) + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire2", 25), + isc::dns::BadLabelType); + // a bad compression pointer (too big) + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire3_1", 25), + isc::dns::BadPointer); + // forward reference + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire3_2", 25), + isc::dns::BadPointer); + // invalid name length + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire4", 550), + isc::dns::TooLongName); + + // skip test for from Wire5. It's for disabling decompression, but our + // implementation always allows it. + + // bad pointer (too big) + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire6", 25), + isc::dns::BadPointer); + // input ends unexpectedly + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire7", 25), + isc::dns::IncompleteName); + // many hops of compression but valid. should succeed. + EXPECT_EQ(true, nameFactoryFromWire("testdata/name_fromWire8", 383) == + Name("vix.com")); + + // converting upper-case letters to down-case + EXPECT_EQ("vix.com.", nameFactoryFromWire("testdata/name_fromWire1", + 25, true).toText()); + EXPECT_EQ(3, + nameFactoryFromWire("testdata/name_fromWire1", 25).getLabels()); +} + +TEST_F(NameTest, toText) +{ + // tests derived from BIND9 + EXPECT_EQ("a.b.c.d", Name("a.b.c.d").toText(true)); + EXPECT_EQ("a.\\\\[[.c.d", Name("a.\\\\[\\[.c.d").toText(true)); + EXPECT_EQ("a.b.C.d.", Name("a.b.C.d").toText(false)); + EXPECT_EQ("a.b.", Name("a.b.").toText(false)); + + // test omit_final_dot. It's false by default. + EXPECT_EQ("a.b.c.d", Name("a.b.c.d.").toText(true)); + EXPECT_EQ(Name("a.b.").toText(false), Name("a.b.").toText()); + + // the root name is a special case: omit_final_dot will be ignored. + EXPECT_EQ(".", Name(".").toText(true)); +} + +TEST_F(NameTest, toWireBuffer) +{ + std::vector data; + OutputBuffer buffer(0); + + UnitTestUtil::readWireData(std::string("01610376697803636f6d00"), data); + Name("a.vix.com.").toWire(buffer); + EXPECT_EQ(true, buffer.getLength() == data.size() && + memcmp(buffer.getData(), &data[0], data.size()) == 0); +} + +// +// Helper class to hold comparison test parameters. +// +struct CompareParameters { + CompareParameters(const Name& n1, const Name& n2, + NameComparisonResult::NameRelation r, int o, + unsigned int l) : + name1(n1), name2(n2), reln(r), order(o), labels(l) {} + static int normalizeOrder(int o) + { + if (o > 0) { + return (1); + } else if (o < 0) { + return (-1); + } + return (0); + } + Name name1; + Name name2; + NameComparisonResult::NameRelation reln; + int order; + unsigned int labels; +}; + +TEST_F(NameTest, compare) +{ + std::vector params; + params.push_back(CompareParameters(Name("c.d"), Name("a.b.c.d"), + NameComparisonResult::SUPERDOMAIN, + -1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("c.d"), + NameComparisonResult::SUBDOMAIN, 1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("c.d.e.f"), + NameComparisonResult::COMMONANCESTOR, + -1, 1)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("f.g.c.d"), + NameComparisonResult::COMMONANCESTOR, + -1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("A.b.C.d."), + NameComparisonResult::EQUAL, + 0, 5)); + + std::vector::const_iterator it; + for (it = params.begin(); it != params.end(); ++it) { + NameComparisonResult result = (*it).name1.compare((*it).name2); + EXPECT_EQ((*it).reln, result.getRelation()); + EXPECT_EQ((*it).order, + CompareParameters::normalizeOrder(result.getOrder())); + EXPECT_EQ((*it).labels, result.getCommonLabels()); + } +} + +TEST_F(NameTest, isWildcard) +{ + EXPECT_EQ(false, example_name.isWildcard()); + EXPECT_EQ(true, Name("*.a.example.com").isWildcard()); + EXPECT_EQ(false, Name("a.*.example.com").isWildcard()); +} +} diff --git a/src/lib/dns/cpp/testdata/name_fromWire1 b/src/lib/dns/cpp/testdata/name_fromWire1 new file mode 100644 index 0000000000..42fc61d831 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire1 @@ -0,0 +1,14 @@ +# +# a global14 compression pointer +# +000a85800001000300000003 +# V i x c o m +0356697803636f6d0000020001c00c00 +02000100000e10000b05697372763102 +7061c00cc00c0002000100000e100009 +066e732d657874c00cc00c0002000100 +000e10000e036e733104676e61630363 +6f6d00c0250001000100000e100004cc +98b886c03c0001000100000e100004cc +98b840c051000100010002a14a0004c6 +97f8f6 diff --git a/src/lib/dns/cpp/testdata/name_fromWire2 b/src/lib/dns/cpp/testdata/name_fromWire2 new file mode 100644 index 0000000000..0758a68709 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire2 @@ -0,0 +1,15 @@ +# +# bogus label character (looks like a local compression pointer) +# +000a85800001000300000003 +#this is the bogus label character: +83 +76697803636f6d0000020001c00c00 +02000100000e10000b05697372763102 +7061c00cc00c0002000100000e100009 +066e732d657874c00cc00c0002000100 +000e10000e036e733104676e61630363 +6f6d00c0250001000100000e100004cc +98b886c03c0001000100000e100004cc +98b840c051000100010002a14a0004c6 +97f8f6 diff --git a/src/lib/dns/cpp/testdata/name_fromWire3_1 b/src/lib/dns/cpp/testdata/name_fromWire3_1 new file mode 100644 index 0000000000..e38efcccf5 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire3_1 @@ -0,0 +1,11 @@ +# +# a bad compression pointer starting with the bits 1111 (too big pointer) +# +000a85800001000300000003 +03766978 03636f6d 00 0002 0001 +f00c 0002 0001 0000 0e10 000b 056973727631 027061 c00c +c00c 0002 0001 0000 0e10 0009 066e732d657874 c00c +c00c 0002 0001 0000 0e10 000e 036e7331 04676e6163 03636f6d 00 +c025 0001 0001 0000 0e10 0004 cc98b886 +c03c 0001 0001 0000 0e10 0004 cc98b840 +c051 0001 0001 0002 a14a 0004 c697f8f6 diff --git a/src/lib/dns/cpp/testdata/name_fromWire3_2 b/src/lib/dns/cpp/testdata/name_fromWire3_2 new file mode 100644 index 0000000000..c377bb19c1 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire3_2 @@ -0,0 +1,13 @@ +# +# a bad compression pointer due to forward reference of 0x30 to +# another compression pointer with a valid backreference +# +000a85800001000300000003 +03766978 03636f6d 00 0002 0001 +#'30' is the forward reference, 'c00c' at the end is the valid pointer: +c030 0002 0001 0000 0e10 000b 056973727631 027061 c00c +c00c 0002 0001 0000 0e10 0009 066e732d657874 c00c +c00c 0002 0001 0000 0e10 000e 036e7331 04676e6163 03636f6d 00 +c025 0001 0001 0000 0e10 0004 cc98b886 +c03c 0001 0001 0000 0e10 0004 cc98b840 +c051 0001 0001 0002 a14a 0004 c697f8f6 diff --git a/src/lib/dns/cpp/testdata/name_fromWire4 b/src/lib/dns/cpp/testdata/name_fromWire4 new file mode 100644 index 0000000000..dba6035ab5 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire4 @@ -0,0 +1,45 @@ +# +# invalid name length, pointer at offset 0x0226 points to +# long name at offset 0x25 +# +000a 8580 0001 0003 0000 0001 +03 766978 03 636f6d 00 0002 0001 +c00c 0002 0001 00000e10 +0101 +# long name starts here +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a +03616263 0358595a 03616263 0358595a +03414243 0378797a 03414243 0378797a 00 +# compression pointer start here and refers back to long name +c023 0002 0001 00000e10 0009 066e732d657874 c00c +c00c 0002 0001 00000e10 000e 036e733104676e616303636f6d00 +c025 0001 0001 00000e10 0004 cc98b886 diff --git a/src/lib/dns/cpp/testdata/name_fromWire6 b/src/lib/dns/cpp/testdata/name_fromWire6 new file mode 100644 index 0000000000..fa1abe6a3b --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire6 @@ -0,0 +1,14 @@ +# +# a bad pointer +# +000a85800001000300000003 +# the bad pointer is f00c (offset = 300c) which is too large +0376697803636f6d0000020001 f00c 00 +02000100000e10000b05697372763102 +7061c00cc00c0002000100000e100009 +066e732d657874c00cc00c0002000100 +000e10000e036e733104676e61630363 +6f6d00c0250001000100000e100004cc +98b886c03c0001000100000e100004cc +98b840c051000100010002a14a0004c6 +97f8f6 diff --git a/src/lib/dns/cpp/testdata/name_fromWire7 b/src/lib/dns/cpp/testdata/name_fromWire7 new file mode 100644 index 0000000000..2dedd4adac --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire7 @@ -0,0 +1,6 @@ +# +# input ends unexpectedly +# +000a85800001000300000003 +# parser will start at the ending 'c0', which is an incomplete sequence. +0376697803636f6d0000020001 c0 diff --git a/src/lib/dns/cpp/testdata/name_fromWire8 b/src/lib/dns/cpp/testdata/name_fromWire8 new file mode 100644 index 0000000000..575563d4a7 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire8 @@ -0,0 +1,27 @@ +# +# many hops of compression. absolutely many, but should be decompressed. +# +000a85800001000300000013 +03 766978 03 636f6d 00 0002 0001 +c00c 0002 0001 00000e10 000b 056973727631027061 c00c +c019 0002 0001 00000e10 0009 066e732d657874 c00c +c030 0002 0001 00000e10 000e 036e7331 04676e6163 03636f6d 00 +c045 0001 0001 00000e10 0004 cc98b886 +c05f 0001 0001 00000e10 0004 cc98b840 +c06f 0001 0001 0002a14a 0004 c697f8f6 +c07f 0001 0001 0002a14a 0004 c697f8f6 +c08f 0001 0001 0002a14a 0004 c697f8f6 +c09f 0001 0001 0002a14a 0004 c697f8f6 +c0af 0001 0001 0002a14a 0004 c697f8f6 +c0bf 0001 0001 0002a14a 0004 c697f8f6 +c0cf 0001 0001 0002a14a 0004 c697f8f6 +c0df 0001 0001 0002a14a 0004 c697f8f6 +c0ef 0001 0001 0002a14a 0004 c697f8f6 +c0ff 0001 0001 0002a14a 0004 c697f8f6 +c10f 0001 0001 0002a14a 0004 c697f8f6 +c11f 0001 0001 0002a14a 0004 c697f8f6 +c12f 0001 0001 0002a14a 0004 c697f8f6 +c13f 0001 0001 0002a14a 0004 c697f8f6 +c14f 0001 0001 0002a14a 0004 c697f8f6 +c15f 0001 0001 0002a14a 0004 c697f8f6 +c16f 0001 0001 0002a14a 0004 c697f8f6 diff --git a/src/lib/dns/cpp/testdata/name_toWire1 b/src/lib/dns/cpp/testdata/name_toWire1 new file mode 100644 index 0000000000..c06ec4baeb --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_toWire1 @@ -0,0 +1,12 @@ +# +# Rendering 3 names with compression. [x] means a compression pointer pointing +# to offset 'x'. +# +#bytes: +# 0 1 2 3 4 5 6 7 8 9 a b c d e +#(1)a(7)e x a m p l e(3)c o m . + 0161076578616d706c6503636f6d00 +#(1)b [2] + 0162c002 +# a . e x a m p l e . o r g . + 0161076578616d706c65036f726700 diff --git a/src/lib/dns/cpp/testdata/name_toWire2 b/src/lib/dns/cpp/testdata/name_toWire2 new file mode 100644 index 0000000000..2377121a0c --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_toWire2 @@ -0,0 +1,14 @@ +# +# Rendering names in a large buffer. [x] means a compression pointer pointing +# to offset 'x'. +# +#bytes: +#3f 40 +#ff 00 +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#[3fff] = a.example.com: can be compressed + ffff +#(1) b(7) e x a m p l e (3) c o m .; cannot compress as the pointer +# (0x4001) would exceed 0x4000 + 01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 diff --git a/src/lib/dns/cpp/testdata/name_toWire3 b/src/lib/dns/cpp/testdata/name_toWire3 new file mode 100644 index 0000000000..09428b3cdc --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_toWire3 @@ -0,0 +1,14 @@ +# +# Rendering names including one explicitly uncompressed. +# [x] means a compression pointer pointing to offset 'x'. +# +# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 (bytes) +#(1) a (7) e x a m p l e (3) c o m . + 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 + +#15 29 (bytes) +#(1) b(7) e x a m p l e (3) c o m .; specified not compressed, but +# can be pointed to from others + 01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 +#[0f] + c0 0f diff --git a/src/lib/dns/cpp/unittest_util.cc b/src/lib/dns/cpp/unittest_util.cc new file mode 100644 index 0000000000..67a9d791de --- /dev/null +++ b/src/lib/dns/cpp/unittest_util.cc @@ -0,0 +1,74 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#include +#include +#include +#include +#include +#include + +#include "unittest_util.h" + +using isc::UnitTestUtil; + +void +UnitTestUtil::readWireData(const char* datafile, + std::vector& data) +{ + std::ifstream ifs; + + ifs.open(datafile, std::ios_base::in); + if ((ifs.rdstate() & std::istream::failbit) != 0) { + throw std::runtime_error("failed to open data file: " + + std::string(datafile)); + } + + data.clear(); + + std::string s; + while (getline(ifs, s), !ifs.eof()) { + if (ifs.bad() || ifs.fail()) { + throw std::runtime_error("unexpected data line"); + } + if (s.empty() || s[0] == '#') { + continue; + } + + readWireData(s, data); + } +} + +void +UnitTestUtil::readWireData(const std::string& datastr, + std::vector& data) +{ + std::istringstream iss(datastr); + + do { + std::string bytes; + iss >> bytes; + if (iss.bad() || iss.fail() || (bytes.size() % 2) != 0) { + throw std::runtime_error("unexpected input or I/O error"); + } + + for (int pos = 0; pos < bytes.size(); pos += 2) { + unsigned int ch; + std::istringstream(bytes.substr(pos, 2)) >> std::hex >> ch; + data.push_back(static_cast(ch)); + } + } while (!iss.eof()); +} diff --git a/src/lib/dns/cpp/unittest_util.h b/src/lib/dns/cpp/unittest_util.h new file mode 100644 index 0000000000..c443c799eb --- /dev/null +++ b/src/lib/dns/cpp/unittest_util.h @@ -0,0 +1,46 @@ +// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// $Id$ + +#ifndef __UNITTEST_UTIL_H +#define __UNITTEST_UTIL_H 1 + +#include +#include + +namespace isc { + +class UnitTestUtil { +public: + /// + /// read text format wire data from a file and put it to the given vector. + /// + static void readWireData(const char*datafile, + std::vector& data); + + /// + /// convert a sequence of hex strings into the corresponding list of + /// 8-bit integers, and append them to the vector. + /// + static void readWireData(const std::string& datastr, + std::vector& data); +}; + +} +#endif // __UNITTEST_UTIL_H + +// Local Variables: +// mode: c++ +// End: From cb3010b6108eb475da7d9c678b253472d6e23a0d Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 11 Dec 2009 09:22:16 +0000 Subject: [PATCH 07/54] improved wire data comparison test by making failure messages more meaningful. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@356 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/messagerenderer_unittest.cc | 4 +-- src/lib/dns/cpp/unittest_util.cc | 32 +++++++++++++++++++++ src/lib/dns/cpp/unittest_util.h | 23 +++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/cpp/messagerenderer_unittest.cc b/src/lib/dns/cpp/messagerenderer_unittest.cc index d4c01ef233..6ad4373fa7 100644 --- a/src/lib/dns/cpp/messagerenderer_unittest.cc +++ b/src/lib/dns/cpp/messagerenderer_unittest.cc @@ -68,7 +68,7 @@ TEST_F(MessageRendererTest, toWireTBD) renderer.writeName(Name("a.example.com.")); renderer.writeName(Name("b.example.com."), false); renderer.writeName(Name("b.example.com.")); - EXPECT_EQ(true, buffer.getLength() == data.size() && - memcmp(buffer.getData(), &data[0], data.size()) == 0); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(), + buffer.getLength(), &data[0], data.size()); } } diff --git a/src/lib/dns/cpp/unittest_util.cc b/src/lib/dns/cpp/unittest_util.cc index 67a9d791de..9eac8ce410 100644 --- a/src/lib/dns/cpp/unittest_util.cc +++ b/src/lib/dns/cpp/unittest_util.cc @@ -21,6 +21,8 @@ #include #include +#include + #include "unittest_util.h" using isc::UnitTestUtil; @@ -72,3 +74,33 @@ UnitTestUtil::readWireData(const std::string& datastr, } } while (!iss.eof()); } + +::testing::AssertionResult +UnitTestUtil::matchWireData(const char* dataexp1, const char* lenexp1, + const char* dataexp2, const char* lenexp2, + const void* data1, size_t len1, + const void* data2, size_t len2) +{ + ::testing::Message msg; + size_t cmplen = std::min(len1, len2); + + for (int i = 0; i < cmplen; i++) { + uint8_t ch1 = static_cast(data1)[i]; + uint8_t ch2 = static_cast(data2)[i]; + if (ch1 != ch2) { + msg << "Wire data mismatch at " << i << "th byte\n" + << " Actual: " << std::dec << + static_cast(ch1) << "\n" + << "Expected: " << std::dec << + static_cast(ch2) << "\n"; + return (::testing::AssertionFailure(msg)); + } + } + if (len1 != len2) { + msg << "Wire data mismatch in length:\n" + << " Actual: " << len1 << "\n" + << "Expected: " << len2 << "\n"; + return (::testing::AssertionFailure(msg)); + } + return ::testing::AssertionSuccess(); +} diff --git a/src/lib/dns/cpp/unittest_util.h b/src/lib/dns/cpp/unittest_util.h index c443c799eb..4bce7edb4e 100644 --- a/src/lib/dns/cpp/unittest_util.h +++ b/src/lib/dns/cpp/unittest_util.h @@ -20,6 +20,8 @@ #include #include +#include + namespace isc { class UnitTestUtil { @@ -36,6 +38,27 @@ public: /// static void readWireData(const std::string& datastr, std::vector& data); + + /// + /// Compare len1 bytes of data1 with len2 bytes of data2 as binary data. + /// If they don't match report the point of mismatch in the google test + /// format. This method is expected to be used from the EXPECT_PRED_FORMAT4 + /// macro of google test as follows: + /// \code EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + /// actual_data, actual_data_len, + /// expected_data, expected_data_len); \endcode + /// Parameters from dataexp1 to lenexp2 are passed via the macro but will + /// be ignored by this method. + /// Note: newer versions of google test supports the direct use of + /// AssertionResult with the EXPECT_TRUE macro, which would be more + /// intuitive, but to be as compatible as possible we use the more primitive + /// macro, i.e., EXPECT_PRED_FORMAT4. + /// + static ::testing::AssertionResult + matchWireData(const char* dataexp1, const char* lenexp1, + const char* dataexp2, const char* lenexp2, + const void* data1, size_t len1, + const void* data2, size_t len2); }; } From f1be7b266666a3dca282ab5b68738b8e874151c3 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 11 Dec 2009 09:26:49 +0000 Subject: [PATCH 08/54] comment fix git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@357 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/testdata/name_toWire3 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/cpp/testdata/name_toWire3 b/src/lib/dns/cpp/testdata/name_toWire3 index 09428b3cdc..643768e4b7 100644 --- a/src/lib/dns/cpp/testdata/name_toWire3 +++ b/src/lib/dns/cpp/testdata/name_toWire3 @@ -7,8 +7,8 @@ 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 #15 29 (bytes) -#(1) b(7) e x a m p l e (3) c o m .; specified not compressed, but -# can be pointed to from others +#(1) b(7) e x a m p l e (3) c o m .; specified to be not compressed, +# but can be pointed to from others 01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -#[0f] +#[0f] referring to the second (uncompressed name) c0 0f From 02e6a2016c813c2cde26eddf7fd2614a080ea12c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 11 Dec 2009 20:47:41 +0000 Subject: [PATCH 09/54] untabified git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@358 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/buffer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h index c259933bfd..bb2178f49d 100644 --- a/src/lib/dns/cpp/buffer.h +++ b/src/lib/dns/cpp/buffer.h @@ -154,8 +154,8 @@ public: } cp = &data_[position_]; - data = ((unsigned int)(cp[0])) << 8; - data |= ((unsigned int)(cp[1])); + data = ((unsigned int)(cp[0])) << 8; + data |= ((unsigned int)(cp[1])); position_ += sizeof(data); return (data); @@ -175,10 +175,10 @@ public: } cp = &data_[position_]; - data = ((unsigned int)(cp[0])) << 24; - data |= ((unsigned int)(cp[1])) << 16; - data |= ((unsigned int)(cp[2])) << 8; - data |= ((unsigned int)(cp[3])); + data = ((unsigned int)(cp[0])) << 24; + data |= ((unsigned int)(cp[1])) << 16; + data |= ((unsigned int)(cp[2])) << 8; + data |= ((unsigned int)(cp[3])); position_ += sizeof(data); return (data); From 9a6060ff5061545289fc93449a952abc8c8b3a6a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 11 Dec 2009 22:24:14 +0000 Subject: [PATCH 10/54] simplified git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@359 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/unittest_util.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/dns/cpp/unittest_util.cc b/src/lib/dns/cpp/unittest_util.cc index 9eac8ce410..a25b8810ce 100644 --- a/src/lib/dns/cpp/unittest_util.cc +++ b/src/lib/dns/cpp/unittest_util.cc @@ -85,14 +85,12 @@ UnitTestUtil::matchWireData(const char* dataexp1, const char* lenexp1, size_t cmplen = std::min(len1, len2); for (int i = 0; i < cmplen; i++) { - uint8_t ch1 = static_cast(data1)[i]; - uint8_t ch2 = static_cast(data2)[i]; + int ch1 = static_cast(data1)[i]; + int ch2 = static_cast(data2)[i]; if (ch1 != ch2) { msg << "Wire data mismatch at " << i << "th byte\n" - << " Actual: " << std::dec << - static_cast(ch1) << "\n" - << "Expected: " << std::dec << - static_cast(ch2) << "\n"; + << " Actual: " << ch1 << "\n" + << "Expected: " << ch2 << "\n"; return (::testing::AssertionFailure(msg)); } } From 6e28ebbdbda2f5b7db3fba5df55efcf91aa39b66 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 11 Dec 2009 22:33:07 +0000 Subject: [PATCH 11/54] additional test case git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@360 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/messagerenderer_unittest.cc | 24 +++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/lib/dns/cpp/messagerenderer_unittest.cc b/src/lib/dns/cpp/messagerenderer_unittest.cc index 6ad4373fa7..bb28e45554 100644 --- a/src/lib/dns/cpp/messagerenderer_unittest.cc +++ b/src/lib/dns/cpp/messagerenderer_unittest.cc @@ -44,8 +44,8 @@ TEST_F(MessageRendererTest, toWire) renderer.writeName(Name("a.example.com.")); renderer.writeName(Name("b.example.com.")); renderer.writeName(Name("a.example.org.")); - EXPECT_EQ(true, buffer.getLength() == data.size() && - memcmp(buffer.getData(), &data[0], data.size()) == 0); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(), + buffer.getLength(), &data[0], data.size()); } TEST_F(MessageRendererTest, toWireInLargeBuffer) @@ -57,12 +57,13 @@ TEST_F(MessageRendererTest, toWireInLargeBuffer) renderer.writeName(Name("a.example.com.")); renderer.writeName(Name("a.example.com.")); renderer.writeName(Name("b.example.com.")); - EXPECT_EQ(true, buffer.getLength() == data.size() + offset && - memcmp(static_cast(buffer.getData()) + offset, - &data[0], data.size()) == 0); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + static_cast(buffer.getData()) + offset, + buffer.getLength() - offset, + &data[0], data.size()); } -TEST_F(MessageRendererTest, toWireTBD) +TEST_F(MessageRendererTest, toWireWithUncompressed) { UnitTestUtil::readWireData("testdata/name_toWire3", data); renderer.writeName(Name("a.example.com.")); @@ -71,4 +72,15 @@ TEST_F(MessageRendererTest, toWireTBD) EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(), buffer.getLength(), &data[0], data.size()); } + +TEST_F(MessageRendererTest, toWireCaseCompress) +{ + UnitTestUtil::readWireData("testdata/name_toWire1", data); + renderer.writeName(Name("a.example.com.")); + // this should match the first name in terms of compression: + renderer.writeName(Name("b.exAmple.CoM.")); + renderer.writeName(Name("a.example.org.")); + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(), + buffer.getLength(), &data[0], data.size()); +} } From 201aa42b67df3c82f3ea250e20539a867f8dd2f5 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 12 Dec 2009 02:44:57 +0000 Subject: [PATCH 12/54] added the concatenate() method fixed signed/unsigned bug added some more test cases git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@361 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 156 +++++++++++++++++++------------ src/lib/dns/cpp/name.h | 45 +++++---- src/lib/dns/cpp/name_unittest.cc | 33 +++++++ 3 files changed, 158 insertions(+), 76 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index 9c8b934d7d..ebb81eaacf 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -16,6 +16,7 @@ #include #include +#include #include "buffer.h" #include "name.h" @@ -76,11 +77,6 @@ typedef enum { Name::Name(const std::string &namestring, bool downcase) { - char c; - std::vector offsets; - offsets.reserve(128); - offsets.push_back(0); - // // Initialize things to make the compiler happy; they're not required. // @@ -97,13 +93,20 @@ Name::Name(const std::string &namestring, bool downcase) bool is_root = false; ft_state state = ft_init; + std::vector offsets; + offsets.reserve(128); + offsets.push_back(0); + + std::string ndata; + ndata.reserve(Name::MAX_WIRE); + // should we refactor this code using, e.g, the state pattern? Probably // 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++; + while (ndata.size() < Name::MAX_WIRE && s != send && !done) { + char c = *s++; switch (state) { case ft_init: @@ -121,31 +124,31 @@ Name::Name(const std::string &namestring, bool downcase) } if (is_root) { - ndata_.push_back(0); + ndata.push_back(0); done = true; break; } // FALLTHROUGH case ft_start: // begin of a label - ndata_.push_back(0); // placeholder for the label length field + ndata.push_back(0); // placeholder for the label length field count = 0; if (c == '\\') { state = ft_initialescape; break; } state = ft_ordinary; - assert(ndata_.size() < Name::MAX_WIRE); + assert(ndata.size() < Name::MAX_WIRE); // FALLTHROUGH case ft_ordinary: // parsing a normal label if (c == '.') { if (count == 0) { dns_throw(EmptyLabel, "duplicate period"); } - ndata_[offsets.back()] = count; - offsets.push_back(ndata_.size()); + ndata.at(offsets.back()) = count; + offsets.push_back(ndata.size()); if (s == send) { - ndata_.push_back(0); + ndata.push_back(0); done = true; } state = ft_start; @@ -155,7 +158,7 @@ Name::Name(const std::string &namestring, bool downcase) if (++count > Name::MAX_LABELLEN) { dns_throw(TooLongLabel, "label is too long"); } - ndata_.push_back(downcase ? maptolower[c] : c); + ndata.push_back(downcase ? maptolower[c] : c); } break; case ft_initialescape: // just found '\' @@ -171,7 +174,7 @@ Name::Name(const std::string &namestring, bool downcase) if (++count > Name::MAX_LABELLEN) { dns_throw(TooLongLabel, "label is too long"); } - ndata_.push_back(downcase ? maptolower[c] : c); + ndata.push_back(downcase ? maptolower[c] : c); state = ft_ordinary; break; } @@ -193,7 +196,7 @@ Name::Name(const std::string &namestring, bool downcase) if (++count > Name::MAX_LABELLEN) { dns_throw(TooLongLabel, "label is too long"); } - ndata_.push_back(downcase ? maptolower[value] : value); + ndata.push_back(downcase ? maptolower[value] : value); state = ft_ordinary; } break; @@ -204,7 +207,7 @@ Name::Name(const std::string &namestring, bool downcase) } if (!done) { // no trailing '.' was found. - if (ndata_.size() == Name::MAX_WIRE) { + if (ndata.size() == Name::MAX_WIRE) { dns_throw(TooLongName, "name is too long for termination"); } assert(s == send); @@ -213,16 +216,17 @@ Name::Name(const std::string &namestring, bool downcase) } if (state == ft_ordinary) { assert(count != 0); - ndata_[offsets.back()] = count; + ndata.at(offsets.back()) = count; - offsets.push_back(ndata_.size()); + offsets.push_back(ndata.size()); // add a trailing \0 - ndata_.push_back('\0'); + ndata.push_back('\0'); } } labels_ = offsets.size(); - assert(labels_ <= 127); + assert(labels_ > 0 && labels_ <= Name::MAX_LABELS); + ndata_.assign(ndata.data(), ndata.size()); length_ = ndata_.size(); offsets_.assign(offsets.begin(), offsets.end()); } @@ -236,8 +240,8 @@ typedef enum { Name::Name(InputBuffer& buffer, bool downcase) { unsigned int new_current; - std::vector offsets; - offsets.reserve(128); + std::vector offsets; + offsets.reserve(Name::MAX_WIRE / 2); /* * Initialize things to make the compiler happy; they're not required. @@ -357,10 +361,11 @@ Name::toText(bool omit_final_dot) const return ("."); } - unsigned int count; std::string::const_iterator np = ndata_.begin(); std::string::const_iterator np_end = ndata_.end(); unsigned int labels = labels_; // use for integrity check + // init with an impossible value to catch error cases in the end: + unsigned int count = Name::MAX_LABELLEN + 1; // result string: it will roughly have the same length as the wire format // name data. reserve that length to minimize reallocation. @@ -428,12 +433,6 @@ Name::toText(bool omit_final_dot) const 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. @@ -448,32 +447,30 @@ Name::compare(const Name& other) const --l; --l1; --l2; - pos1 = offsets_[l1]; - pos2 = other.offsets_[l2]; - count1 = ndata_[pos1++]; - count2 = other.ndata_[pos2++]; - label1 = ndata_[pos1]; - label2 = other.ndata_[pos2]; + 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 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN); - cdiff = (int)count1 - (int)count2; - if (cdiff < 0) - count = count1; - else - count = count2; + int cdiff = (int)count1 - (int)count2; + unsigned int count = (cdiff < 0) ? count1 : count2; while (count > 0) { - chdiff = (int)maptolower[label1] - (int)maptolower[label2]; + unsigned char label1 = ndata_[pos1]; + unsigned char label2 = other.ndata_[pos2]; + + int chdiff = (int)maptolower[label1] - (int)maptolower[label2]; if (chdiff != 0) { return (NameComparisonResult(chdiff, nlabels, NameComparisonResult::COMMONANCESTOR)); } --count; - label1 = ndata_[++pos1]; - label2 = other.ndata_[++pos2]; + ++pos1; + ++pos2; } if (cdiff != 0) { return (NameComparisonResult(cdiff, nlabels, @@ -493,32 +490,28 @@ Name::compare(const Name& other) const return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL)); } -// Are 'this' name and 'other' equal? bool -Name::operator==(const Name& other) const +Name::equals(const Name& other) const { - unsigned int l; - unsigned char c, count; - std::string::const_iterator label1, label2; - if (length_ != other.length_ || labels_ != other.labels_) { return (false); } - l = labels_; - label1 = ndata_.begin(); - label2 = other.ndata_.begin(); - while (l > 0) { - l--; - count = *label1++; - if (count != *label2++) { + for (unsigned int l = labels_, pos = 0; l > 0; --l) { + unsigned char count = ndata_[pos]; + if (count != other.ndata_[pos]) { return (false); } + ++pos; while (count-- > 0) { - c = maptolower[(unsigned char)*label1++]; // XXX should avoid cast - if (c != maptolower[(unsigned char)*label2++]) + unsigned char label1 = ndata_[pos]; + unsigned char label2 = other.ndata_[pos]; + + if (maptolower[label1] != maptolower[label2]) { return (false); + } + ++pos; } } @@ -531,6 +524,51 @@ Name::isWildcard() const return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*'); } +namespace { // hide the local class +struct OffsetAdjuster : public std::binary_function { + unsigned char operator()(unsigned char ch, unsigned int offset) const + { + return (ch + offset); + } +}; +} + +Name +Name::concatenate(const Name& suffix) const +{ + assert(this->length_ > 0 && suffix.length_ > 0); + assert(this->labels_ > 0 && suffix.labels_ > 0); + + unsigned int length = this->length_ + suffix.length_ - 1; + if (length > Name::MAX_WIRE) { + dns_throw(TooLongName, "names are too long to concatenate"); + } + + Name retname; + retname.ndata_.reserve(length); + retname.ndata_.assign(this->ndata_, 0, this->length_ - 1); + retname.ndata_.insert(retname.ndata_.end(), + suffix.ndata_.begin(), suffix.ndata_.end()); + assert(retname.ndata_.size() == length); + retname.length_ = length; + + unsigned int labels = this->labels_ + suffix.labels_ - 1; + assert(labels <= Name::MAX_LABELS); + retname.offsets_.reserve(labels); + retname.offsets_.assign(&this->offsets_[0], + &this->offsets_[0] + this->labels_ - 1); + transform(suffix.offsets_.begin(), suffix.offsets_.end(), + back_inserter(retname.offsets_), + bind2nd(OffsetAdjuster(), this->length_ - 1)); + assert(retname.offsets_.back() == retname.length_ - 1); + assert(retname.offsets_.size() == labels); + retname.labels_ = labels; + + return (retname); +} + std::ostream& operator<<(std::ostream& os, const Name& name) { diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index af6e8f745a..a827de6bb6 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -185,13 +185,14 @@ private: /// names as a special case. /// class Name { -public: /// /// \name Constructors and Destructor /// //@{ +private: /// The default constructor Name() : length_(0), labels_(0) {} +public: /// Constructor from a string /// /// \param namestr A string representation of the name to be constructed. @@ -275,7 +276,7 @@ public: /// returns the result in the form of a NameComparisonResult /// object. /// - /// Note that this is a case-insensitive comparison. + /// Note that this is case-insensitive comparison. /// /// \param other the right-hand operand to compare against. /// \return a NameComparisonResult object representing the @@ -284,29 +285,33 @@ public: /// \brief Return true iff two names are equal. /// - /// The comparison is based on the result of the compare() method. + /// Semantically this could be implemented based on the result of the + /// \c compare() method, but the actual implementation uses different code + /// that simply performs character-by-character comparison (case + /// insensitive for the name label parts) on the two names. This is because + /// it would be much faster and the simple equality check would be pretty + /// common. + /// /// \param other the Name object to compare against. - /// \return true if compare(other).get_order() is 0; - /// otherwise false. + /// \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; + bool operator==(const Name& other) const { return (this->equals(other)); } /// \brief Return true iff two names are not equal. /// - /// The comparison is based on the result of the compare() method. - /// \param other the Name object to compare against. - /// \return true if compare(other).get_order() is non 0; - /// otherwise false. - bool nequals(const Name& other) const; + /// 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 == other)); } + bool operator!=(const Name& other) const { return (this->nequals(other)); } /// \brief Less-than or equal comparison for Name against other /// - /// The comparison is based on the result of the compare() method. + /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. /// \return true if compare(other).get_order() <= 0; /// otherwise false. @@ -318,7 +323,7 @@ public: /// \brief Greater-than or equal comparison for Name against /// other /// - /// The comparison is based on the result of the compare() method. + /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. /// \return true if compare(other).get_order() >= 0; /// otherwise false. @@ -329,7 +334,7 @@ public: /// \brief Less-than comparison for Name against other /// - /// The comparison is based on the result of the compare() method. + /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. /// \return true if compare(other).get_order() < 0; /// otherwise false. @@ -340,7 +345,7 @@ public: /// \brief Greater-than comparison for Name against other /// - /// The comparison is based on the result of the compare() method. + /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. /// \return true if compare(other).get_order() > 0; /// otherwise false. @@ -393,13 +398,19 @@ public: /// \brief Max allowable length of domain names. static const size_t MAX_WIRE = 255; + /// \brief Max allowable labels of domain names. + /// + /// This is ceil(MAX_WIRE / 2), 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; //@} private: std::string ndata_; - std::vector offsets_; + std::vector offsets_; unsigned int length_; unsigned int labels_; diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index cb6395674b..9af636a3d3 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -38,6 +38,7 @@ protected: NameTest() : example_name("www.example.com") {} Name example_name; + static const size_t MAX_LABELS = Name::MAX_LABELS; // // helper methods // @@ -128,6 +129,17 @@ TEST_F(NameTest, fromText) "123")); // \DDD must consist of 3 digits. EXPECT_THROW(Name("\\12"), isc::dns::BadLabelType); + + // a name with the max number of labels. should be constructed without + // an error, and its length should be the max value. + Name maxlabels = Name("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 40 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 80 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 120 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 160 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 200 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 240 + "0.1.2.3.4.5.6."); + EXPECT_EQ(MAX_LABELS, maxlabels.getLabels()); } TEST_F(NameTest, fromWire) @@ -250,10 +262,31 @@ TEST_F(NameTest, compare) } } +TEST_F(NameTest, equal) +{ + EXPECT_TRUE(example_name == Name("WWW.EXAMPLE.COM.")); + EXPECT_TRUE(example_name.equals(Name("WWW.EXAMPLE.COM."))); + EXPECT_TRUE(example_name != Name("www.example.org.")); + EXPECT_TRUE(example_name.nequals(Name("www.example.org."))); +} + TEST_F(NameTest, isWildcard) { EXPECT_EQ(false, example_name.isWildcard()); EXPECT_EQ(true, Name("*.a.example.com").isWildcard()); EXPECT_EQ(false, Name("a.*.example.com").isWildcard()); } + +TEST_F(NameTest, concatenate) +{ + NameComparisonResult result = + Name("aaa.www.example.com.").compare(Name("aaa").concatenate(example_name)); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + result = example_name.compare(Name(".").concatenate(example_name)); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + result = example_name.compare(example_name.concatenate(Name("."))); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); +} } From 9e6879509aeb9e460634d942d1a699e7ec7d96ce Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sun, 13 Dec 2009 19:49:49 +0000 Subject: [PATCH 13/54] indentation fix git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@362 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/unittest_util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/unittest_util.cc b/src/lib/dns/cpp/unittest_util.cc index a25b8810ce..4cc884381d 100644 --- a/src/lib/dns/cpp/unittest_util.cc +++ b/src/lib/dns/cpp/unittest_util.cc @@ -33,7 +33,7 @@ UnitTestUtil::readWireData(const char* datafile, { std::ifstream ifs; - ifs.open(datafile, std::ios_base::in); + ifs.open(datafile, std::ios_base::in); if ((ifs.rdstate() & std::istream::failbit) != 0) { throw std::runtime_error("failed to open data file: " + std::string(datafile)); From 3cf09942c9977df1ab9f2d13e2a0a04cd0256525 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Mon, 14 Dec 2009 20:04:54 +0000 Subject: [PATCH 14/54] more test cases git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@363 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 3 +-- src/lib/dns/cpp/name_unittest.cc | 19 +++++++++++++++++++ src/lib/dns/cpp/testdata/name_fromWire10 | 12 ++++++++++++ src/lib/dns/cpp/testdata/name_fromWire11 | 12 ++++++++++++ src/lib/dns/cpp/testdata/name_fromWire12 | 13 +++++++++++++ src/lib/dns/cpp/testdata/name_fromWire9 | 12 ++++++++++++ 6 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/lib/dns/cpp/testdata/name_fromWire10 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire11 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire12 create mode 100644 src/lib/dns/cpp/testdata/name_fromWire9 diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index ebb81eaacf..5139409309 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -241,7 +241,7 @@ Name::Name(InputBuffer& buffer, bool downcase) { unsigned int new_current; std::vector offsets; - offsets.reserve(Name::MAX_WIRE / 2); + offsets.reserve(Name::MAX_LABELS); /* * Initialize things to make the compiler happy; they're not required. @@ -562,7 +562,6 @@ Name::concatenate(const Name& suffix) const transform(suffix.offsets_.begin(), suffix.offsets_.end(), back_inserter(retname.offsets_), bind2nd(OffsetAdjuster(), this->length_ - 1)); - assert(retname.offsets_.back() == retname.length_ - 1); assert(retname.offsets_.size() == labels); retname.labels_ = labels; diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index 9af636a3d3..7c844b55e8 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -38,6 +38,7 @@ protected: NameTest() : example_name("www.example.com") {} Name example_name; + static const size_t MAX_WIRE = Name::MAX_WIRE; static const size_t MAX_LABELS = Name::MAX_LABELS; // // helper methods @@ -176,6 +177,24 @@ TEST_F(NameTest, fromWire) EXPECT_EQ(true, nameFactoryFromWire("testdata/name_fromWire8", 383) == Name("vix.com")); + // + // Additional test cases + // + + // large names, a long but valid one, and invalid (too long) one. + EXPECT_EQ(MAX_WIRE, + nameFactoryFromWire("testdata/name_fromWire9", 0).getLength()); + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire10", 0).getLength(), + isc::dns::TooLongName); + + // A name with possible maximum number of labels; awkward but valid + EXPECT_EQ(nameFactoryFromWire("testdata/name_fromWire11", 0).getLabels(), + MAX_LABELS); + + // Wire format including an invalid label length + EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire12", 0), + isc::dns::BadLabelType); + // converting upper-case letters to down-case EXPECT_EQ("vix.com.", nameFactoryFromWire("testdata/name_fromWire1", 25, true).toText()); diff --git a/src/lib/dns/cpp/testdata/name_fromWire10 b/src/lib/dns/cpp/testdata/name_fromWire10 new file mode 100644 index 0000000000..65be775e2f --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire10 @@ -0,0 +1,12 @@ +# +# Too large name; should trigger an exception. +# +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 040102030400 diff --git a/src/lib/dns/cpp/testdata/name_fromWire11 b/src/lib/dns/cpp/testdata/name_fromWire11 new file mode 100644 index 0000000000..32184f6758 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire11 @@ -0,0 +1,12 @@ +# +# A name with possible maximum number of labels; should be accepted safely. +# +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 01000100010001000100 01000100010001000100 +01000100010001000100 0100010000 diff --git a/src/lib/dns/cpp/testdata/name_fromWire12 b/src/lib/dns/cpp/testdata/name_fromWire12 new file mode 100644 index 0000000000..073adda486 --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire12 @@ -0,0 +1,13 @@ +# +# Wire format including an invalid label length +# +#(1) a (7) e x a m p l e + 01 61 07 65 78 61 6d 70 6c 65 +# invalid label length: 64 +40 +# a "label" of 64 characters: shouldn't be parsed +00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f +10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f +20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f +30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f +00 diff --git a/src/lib/dns/cpp/testdata/name_fromWire9 b/src/lib/dns/cpp/testdata/name_fromWire9 new file mode 100644 index 0000000000..79b2978e8c --- /dev/null +++ b/src/lib/dns/cpp/testdata/name_fromWire9 @@ -0,0 +1,12 @@ +# +# A possible longest name; should be accepted safely. +# +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 09010203040506070809 09010203040506070809 +09010203040506070809 0301020300 From dd091fe6c7bfeb6300343b84cc084d629d0b061c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 15 Dec 2009 04:59:44 +0000 Subject: [PATCH 15/54] fixed a doxygen error git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@364 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h index bb2178f49d..9844839fb5 100644 --- a/src/lib/dns/cpp/buffer.h +++ b/src/lib/dns/cpp/buffer.h @@ -114,7 +114,7 @@ public: /// /// The new position must be in the valid range of the buffer; otherwise /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown. - /// \param Position The new position (offset from the beginning of the + /// \param position The new position (offset from the beginning of the /// buffer). void setPosition(size_t position) { From 95447e6897682d7f48818c115cd53de97a09182a Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 15 Dec 2009 05:00:19 +0000 Subject: [PATCH 16/54] add a utility for checking name equality git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@365 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/unittest_util.cc | 20 ++++++++++++++++++++ src/lib/dns/cpp/unittest_util.h | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/lib/dns/cpp/unittest_util.cc b/src/lib/dns/cpp/unittest_util.cc index 4cc884381d..3cbe8c2795 100644 --- a/src/lib/dns/cpp/unittest_util.cc +++ b/src/lib/dns/cpp/unittest_util.cc @@ -23,9 +23,11 @@ #include +#include "name.h" #include "unittest_util.h" using isc::UnitTestUtil; +using isc::dns::NameComparisonResult; void UnitTestUtil::readWireData(const char* datafile, @@ -102,3 +104,21 @@ UnitTestUtil::matchWireData(const char* dataexp1, const char* lenexp1, } return ::testing::AssertionSuccess(); } + +::testing::AssertionResult +UnitTestUtil::matchName(const char* nameexp1, const char* nameexp2, + const isc::dns::Name& name1, + const isc::dns::Name& name2) +{ + ::testing::Message msg; + + NameComparisonResult cmpresult = name1.compare(name2); + if (cmpresult.getOrder() != 0 || + cmpresult.getRelation() != NameComparisonResult::EQUAL) { + msg << "Two names are expected to be equal but not:\n" + << " One: " << name1 << "\n" + << "Other: " << name2 << "\n"; + return (::testing::AssertionFailure(msg)); + } + return ::testing::AssertionSuccess(); +} diff --git a/src/lib/dns/cpp/unittest_util.h b/src/lib/dns/cpp/unittest_util.h index 4bce7edb4e..4d5ced39d8 100644 --- a/src/lib/dns/cpp/unittest_util.h +++ b/src/lib/dns/cpp/unittest_util.h @@ -20,6 +20,8 @@ #include #include +#include "name.h" + #include namespace isc { @@ -41,6 +43,7 @@ public: /// /// Compare len1 bytes of data1 with len2 bytes of data2 as binary data. + /// /// If they don't match report the point of mismatch in the google test /// format. This method is expected to be used from the EXPECT_PRED_FORMAT4 /// macro of google test as follows: @@ -59,6 +62,19 @@ public: const char* dataexp2, const char* lenexp2, const void* data1, size_t len1, const void* data2, size_t len2); + + /// + /// Compare two names. + /// + /// This check method uses \c Name::compare() for comparison, which performs + /// deeper checks including the equality of offsets, and should be better + /// than EXPECT_EQ, which uses operater==. Like the \c matchWireData() + /// method, the usage is a bit awkward; the caller should use + /// \c EXPECT_PRED_FORMAT2. + /// + static ::testing::AssertionResult + matchName(const char* nameexp1, const char* nameexp2, + const isc::dns::Name& name1, const isc::dns::Name& name2); }; } From a782f38489c26692bcc10a3141b27c56835bfa41 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 15 Dec 2009 05:00:32 +0000 Subject: [PATCH 17/54] added Name::split() method. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@366 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 48 +++++++++++++++++++++++++++----- src/lib/dns/cpp/name.h | 48 ++++++++++++++++++++++++-------- src/lib/dns/cpp/name_unittest.cc | 40 ++++++++++++++++++++++---- 3 files changed, 111 insertions(+), 25 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index 5139409309..d925478ce2 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include "buffer.h" #include "name.h" @@ -525,10 +526,12 @@ Name::isWildcard() const } 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 operator()(unsigned char ch, unsigned int offset) const + int, unsigned char> { + unsigned char operator()(unsigned char ch, int offset) const { return (ch + offset); } @@ -554,6 +557,11 @@ Name::concatenate(const Name& suffix) const 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->labels_ + suffix.labels_ - 1; assert(labels <= Name::MAX_LABELS); retname.offsets_.reserve(labels); @@ -568,12 +576,38 @@ Name::concatenate(const Name& suffix) const return (retname); } +Name +Name::split(unsigned int first, unsigned int n) const +{ + if (n == 0 || first + n > labels_) { + dns_throw(OutOfRange, "Name::split: invalid split range"); + } + + Name retname; + unsigned int newlabels = (first + n == labels_) ? n : n + 1; + + retname.offsets_.reserve(newlabels); + transform(offsets_.begin() + first, offsets_.begin() + first + newlabels, + back_inserter(retname.offsets_), + bind2nd(OffsetAdjuster(), -offsets_[first])); + + retname.ndata_.reserve(retname.offsets_.back() + 1); + retname.ndata_.assign(ndata_, offsets_[first], retname.offsets_.back()); + // add a trailing dot + retname.ndata_.push_back(0); + + retname.length_ = retname.ndata_.size(); + retname.labels_ = retname.offsets_.size(); + assert(retname.labels_ == newlabels); + + return (retname); +} +} +} + std::ostream& -operator<<(std::ostream& os, const Name& name) +operator<<(std::ostream& os, const isc::dns::Name& name) { os << name.toText(); return (os); } - -} -} diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index a827de6bb6..01b3f2f7ec 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -244,7 +244,7 @@ public: /// /// 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. + /// an exception of class \c BadLabelType will be thrown. // /// \param omit_final_dot whether to omit the trailing dot in the output. /// \return a string representation of the Name. @@ -361,19 +361,43 @@ public: //@{ /// \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. + /// name.split(first, n) constructs a new name starting from + /// the first-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 n + 1, rather than \c n. For example, + /// when \c n is Name("www.example.com"), + /// both n.split(1, 2) and n.split(1, 3) + /// will produce a name corresponding to "example.com.", which has 3 labels. + /// Note also that labels are counted from 0, and so first = 1 + /// in this example specified the label "example", not "www". /// - /// \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 n + /// 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 n /// labels including and following the first label. Name split(unsigned int first, unsigned int n) const; - /// \brief Concatenate two names + /// \brief Concatenate two names. /// - /// This method appends \c suffix to \c this Name. + /// 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. @@ -416,10 +440,10 @@ private: void fromString(const std::string& namestr); }; +} +} -std::ostream& operator<<(std::ostream& os, const Name& name); -} -} +std::ostream& operator<<(std::ostream& os, const isc::dns::Name& name); #endif // __NAME_H // Local Variables: diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index 7c844b55e8..afcf117fe6 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -72,11 +72,11 @@ TEST_F(NameTest, fromText) std::vector::const_iterator it; for (it = strnames.begin(); it != strnames.end(); ++it) { - EXPECT_EQ(true, example_name == Name(*it)); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name, Name(*it)); } // root names - EXPECT_EQ(true, Name("@") == Name(".")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, Name("@"), Name(".")); // downcase EXPECT_EQ(Name("Www.eXample.coM", true).toText(), example_name.toText()); @@ -149,8 +149,9 @@ TEST_F(NameTest, fromWire) // test cases derived from BIND9 tests. // // normal case with a compression pointer - EXPECT_EQ(true, nameFactoryFromWire("testdata/name_fromWire1", 25) == - Name("vix.com")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + nameFactoryFromWire("testdata/name_fromWire1", 25), + Name("vix.com")); // bogus label character (looks like a local compression pointer) EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire2", 25), isc::dns::BadLabelType); @@ -174,8 +175,9 @@ TEST_F(NameTest, fromWire) EXPECT_THROW(nameFactoryFromWire("testdata/name_fromWire7", 25), isc::dns::IncompleteName); // many hops of compression but valid. should succeed. - EXPECT_EQ(true, nameFactoryFromWire("testdata/name_fromWire8", 383) == - Name("vix.com")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + nameFactoryFromWire("testdata/name_fromWire8", 383), + Name("vix.com")); // // Additional test cases @@ -307,5 +309,31 @@ TEST_F(NameTest, concatenate) result = example_name.compare(example_name.concatenate(Name("."))); EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + // concatenating two valid names would result in too long a name. + Name n1("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789."); + Name n2("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "1234."); + EXPECT_THROW(n1.concatenate(n2), isc::dns::TooLongName); +} + +TEST_F(NameTest, split) +{ + // normal cases with or without explicitly specifying the trailing dot. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1, 2), + Name("example.com.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1, 3), + Name("example.com.")); + // edge cases: only the first or last label. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(0, 1), + Name("www.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(3, 1), + Name(".")); + // invalid range: an exception should be thrown. + EXPECT_THROW(example_name.split(1, 0), isc::dns::OutOfRange); + EXPECT_THROW(example_name.split(2, 3), isc::dns::OutOfRange); } } From 9fec54f0b58e71c46566d8a03cb01106594d4ecc Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Tue, 15 Dec 2009 05:06:08 +0000 Subject: [PATCH 18/54] some more comments on the implementation. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@367 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index d925478ce2..1bc3449251 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -584,16 +584,27 @@ Name::split(unsigned int first, unsigned int n) const } Name retname; + // If the specified range doesn't include the trailing dot, we need one + // more label for that. unsigned int newlabels = (first + n == labels_) ? 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()); - // add a trailing dot retname.ndata_.push_back(0); retname.length_ = retname.ndata_.size(); From c86288413b919ca7f0e0d5c67036f7046882940b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 16 Dec 2009 07:01:34 +0000 Subject: [PATCH 19/54] added AC_PROG_LIBTOOL git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@371 e5f2f494-b856-4b98-b285-d166d9295462 --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 3be7a0f45e..0edc48ac5d 100644 --- a/configure.ac +++ b/configure.ac @@ -12,6 +12,8 @@ AC_PROG_CXX AC_PROG_CC AC_PROG_RANLIB +AC_PROG_LIBTOOL + # Checks for libraries. # Checks for header files. From 42cec1f71afbdbc2b8f599cf3b528da3947583c9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 16 Dec 2009 07:33:12 +0000 Subject: [PATCH 20/54] changed some definitions for libtool git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@372 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/cpp/Makefile.am b/src/lib/dns/cpp/Makefile.am index 863e67bf63..5458673edd 100644 --- a/src/lib/dns/cpp/Makefile.am +++ b/src/lib/dns/cpp/Makefile.am @@ -1,6 +1,6 @@ -lib_LIBRARIES = libdns.a -libdns_a_SOURCES = buffer.h name.cc name.h messagerenderer.h messagerenderer.cc -libdns_a_SOURCES += exceptions.h +lib_LTLIBRARIES = libdns.la +libdns_la_SOURCES = buffer.h name.cc name.h messagerenderer.h messagerenderer.cc +libdns_la_SOURCES += exceptions.h TESTS = if HAVE_GTEST From 8a23fa465fab78550fc4909375730d42d2e0b75e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 16 Dec 2009 07:40:41 +0000 Subject: [PATCH 21/54] use stricter compiler warnings git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@373 e5f2f494-b856-4b98-b285-d166d9295462 --- configure.ac | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index 0edc48ac5d..8ab02276fd 100644 --- a/configure.ac +++ b/configure.ac @@ -10,6 +10,7 @@ AC_CONFIG_HEADERS([config.h]) # Checks for programs. AC_PROG_CXX AC_PROG_CC +AM_CONDITIONAL(GCC, test "$GCC" = yes) AC_PROG_RANLIB AC_PROG_LIBTOOL @@ -22,6 +23,12 @@ AC_PROG_LIBTOOL AC_HEADER_STDBOOL AC_TYPE_SIZE_T +# default compiler warning settings +if test "X$GCC" = "Xyes"; then +CXXFLAGS="-Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare" +fi +AC_SUBST(CXXFLAGS) + # # Check availablity of gtest, which will be used for unit tests. # From 53f3f7bb587a4d27276113f6d11b25c4c802f122 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Wed, 16 Dec 2009 07:41:01 +0000 Subject: [PATCH 22/54] suppressed compiler warnings git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@374 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/buffer.h | 2 +- src/lib/dns/cpp/name.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/dns/cpp/buffer.h b/src/lib/dns/cpp/buffer.h index 9844839fb5..d053809a14 100644 --- a/src/lib/dns/cpp/buffer.h +++ b/src/lib/dns/cpp/buffer.h @@ -94,7 +94,7 @@ public: /// \param data A pointer to the data stored in the buffer. /// \param len The length of the data in bytes. InputBuffer(const void* data, size_t len) : - position_(0), len_(len), data_(static_cast(data)) {} + position_(0), data_(static_cast(data)), len_(len) {} //@} /// diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index 1bc3449251..faed32263e 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -107,7 +107,7 @@ Name::Name(const std::string &namestring, bool downcase) // 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) { - char c = *s++; + unsigned char c = *s++; switch (state) { case ft_init: From fd7002685df3b8ab1562c8c74002a4f5f6071bcf Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 17 Dec 2009 00:53:03 +0000 Subject: [PATCH 23/54] added -fPIC unless we disable shared libraries. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@376 e5f2f494-b856-4b98-b285-d166d9295462 --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index 8ab02276fd..041baf85e1 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,12 @@ AC_TYPE_SIZE_T if test "X$GCC" = "Xyes"; then CXXFLAGS="-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) # From d63da1acb57102aff50c7a11ceaf7091fe7abd73 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 17 Dec 2009 20:41:05 +0000 Subject: [PATCH 24/54] use .lib/libdns.a for unittest git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@377 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/Makefile.am b/src/lib/dns/cpp/Makefile.am index 5458673edd..2152b45c6d 100644 --- a/src/lib/dns/cpp/Makefile.am +++ b/src/lib/dns/cpp/Makefile.am @@ -11,7 +11,7 @@ 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) From 7779523efbfabee1bbd0361433373fbe60391ed9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Thu, 17 Dec 2009 23:58:42 +0000 Subject: [PATCH 25/54] added downcase() methods git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@378 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 38 ++++++++++++++++ src/lib/dns/cpp/name.h | 26 +++++++++++ src/lib/dns/cpp/name_unittest.cc | 75 ++++++++++++++++++++++++++++++-- 3 files changed, 136 insertions(+), 3 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index faed32263e..7d2e922cdc 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -613,6 +613,44 @@ Name::split(unsigned int first, unsigned int n) const return (retname); } + +Name& +Name::downcase() +{ + unsigned int nlen = length_; + unsigned int labels = labels_; + 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 <= Name::MAX_LABELLEN); + assert(nlen >= count); + + while (count > 0) { + ndata_.at(pos) = + maptolower[static_cast(ndata_.at(pos))]; + ++pos; + --nlen; + --count; + } + } + + return (*this); +} + +Name +Name::downcase() const +{ + Name newname = *this; + + return (newname.downcase()); +} + } } diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 01b3f2f7ec..a38e1e2396 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -402,6 +402,32 @@ public: /// \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; + + /// \brief Downcase all upper case alphabet characters in the name. + /// + /// We have two different versions of the \c downcase() method. This + /// version modifies \this name class object. Since this method doesn't + /// create a new object, it should be faster than the other version, and + /// it's exception free. If the caller can allow the original object to be + /// modified, this version would therefore be preferred. + /// + /// \return A reference to the calling object with being downcased. + Name& downcase(); + + /// \brief Downcase all upper case alphabet characters in a newly created + /// copy of the name. + /// + /// We have two different versions of the \c downcase() method. This + /// version makes a copy of the calling object and downcase the upper case + /// characters of the copy. The calling object will be intact. This + /// version will be slower than the other version due to the additional + /// overhead, and may throw an exception if memory allocation fails. + /// However, if the original object cannot be modified this version must be + /// the choice. + /// + /// \return A new \c Name class object where upper case characters of the + /// calling object are downcased. + Name downcase() const; //@} /// diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index afcf117fe6..b29640ff3f 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include "buffer.h" @@ -35,16 +37,26 @@ using isc::dns::NameComparisonResult; namespace { class NameTest : public ::testing::Test { protected: - NameTest() : example_name("www.example.com") {} + NameTest() : example_name("www.example.com"), + example_name_upper("WWW.EXAMPLE.COM"), + buffer_actual(0), buffer_expected(0) + {} + Name example_name; + Name example_name_upper; + OutputBuffer buffer_actual, buffer_expected; static const size_t MAX_WIRE = Name::MAX_WIRE; static const size_t MAX_LABELS = Name::MAX_LABELS; // // helper methods // - Name nameFactoryFromWire(const char* datafile, size_t position, - bool downcase = false); + static Name nameFactoryFromWire(const char* datafile, size_t position, + bool downcase = false); + // construct a name including all non-upper-case-alphabet characters. + static Name nameFactoryLowerCase(); + void compareInWireFormat(const Name& name_actual, + const Name& name_expected); }; Name @@ -60,6 +72,47 @@ NameTest::nameFactoryFromWire(const char* datafile, size_t position, return (Name(buffer, downcase)); } +Name +NameTest::nameFactoryLowerCase() +{ + std::string lowercase_namestr; + lowercase_namestr.reserve(Name::MAX_WIRE); + + unsigned int ch = 0; + unsigned int labelcount = 0; + do { + if (ch < 'A' || ch > 'Z') { + std::ostringstream ss; + ss.setf(std::ios_base::right, std::ios_base::adjustfield); + ss.width(3); + ss << std::setfill('0') << ch; + lowercase_namestr += '\\' + ss.str(); + + if (++labelcount == Name::MAX_LABELLEN) { + lowercase_namestr.push_back('.'); + labelcount = 0; + } + } + } while (++ch <= Name::MAX_WIRE); + + return (Name(lowercase_namestr)); +} + +void +NameTest::compareInWireFormat(const Name& name_actual, + const Name& name_expected) +{ + buffer_actual.clear(); + buffer_expected.clear(); + + name_actual.toWire(buffer_actual); + name_expected.toWire(buffer_expected); + + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + buffer_actual.getData(), buffer_actual.getLength(), + buffer_expected.getData(), buffer_expected.getLength()); +} + TEST_F(NameTest, fromText) { std::vector strnames; @@ -336,4 +389,20 @@ TEST_F(NameTest, split) EXPECT_THROW(example_name.split(1, 0), isc::dns::OutOfRange); EXPECT_THROW(example_name.split(2, 3), isc::dns::OutOfRange); } + +TEST_F(NameTest, downcase) +{ + compareInWireFormat(example_name_upper.downcase(), example_name); + compareInWireFormat(nameFactoryLowerCase().downcase(), + nameFactoryLowerCase()); +} + +TEST_F(NameTest, copyAndDowncase) +{ + Name name_lowercased = example_name_upper.downcase(); + compareInWireFormat(name_lowercased, example_name); + + name_lowercased = nameFactoryLowerCase().downcase(); + compareInWireFormat(name_lowercased, nameFactoryLowerCase()); +} } From d996df758596b7434d6058609b0a34d84336913c Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 03:26:42 +0000 Subject: [PATCH 26/54] on second thought, it doesn't seem to be a good idea to have two downcase() versions as the notation looks too similar and can be confusing. revised the API and noted the restriction. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@379 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 9 -------- src/lib/dns/cpp/name.h | 37 +++++++++++++++----------------- src/lib/dns/cpp/name_unittest.cc | 9 -------- 3 files changed, 17 insertions(+), 38 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index 7d2e922cdc..91acc94f2a 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -642,15 +642,6 @@ Name::downcase() return (*this); } - -Name -Name::downcase() const -{ - Name newname = *this; - - return (newname.downcase()); -} - } } diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index a38e1e2396..91bd40a4d9 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -405,29 +405,26 @@ public: /// \brief Downcase all upper case alphabet characters in the name. /// - /// We have two different versions of the \c downcase() method. This - /// version modifies \this name class object. Since this method doesn't - /// create a new object, it should be faster than the other version, and - /// it's exception free. If the caller can allow the original object to be - /// modified, this version would therefore be preferred. + /// 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 OutpubBuffer 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(); - - /// \brief Downcase all upper case alphabet characters in a newly created - /// copy of the name. - /// - /// We have two different versions of the \c downcase() method. This - /// version makes a copy of the calling object and downcase the upper case - /// characters of the copy. The calling object will be intact. This - /// version will be slower than the other version due to the additional - /// overhead, and may throw an exception if memory allocation fails. - /// However, if the original object cannot be modified this version must be - /// the choice. - /// - /// \return A new \c Name class object where upper case characters of the - /// calling object are downcased. - Name downcase() const; //@} /// diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index b29640ff3f..9640e12235 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -396,13 +396,4 @@ TEST_F(NameTest, downcase) compareInWireFormat(nameFactoryLowerCase().downcase(), nameFactoryLowerCase()); } - -TEST_F(NameTest, copyAndDowncase) -{ - Name name_lowercased = example_name_upper.downcase(); - compareInWireFormat(name_lowercased, example_name); - - name_lowercased = nameFactoryLowerCase().downcase(); - compareInWireFormat(name_lowercased, nameFactoryLowerCase()); -} } From 6c11b782aa10ac9ff9978746a8aeee59ec0de2a0 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 03:30:32 +0000 Subject: [PATCH 27/54] more test case git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@380 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name_unittest.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index 9640e12235..d6a5c3833e 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -392,8 +392,14 @@ TEST_F(NameTest, split) TEST_F(NameTest, downcase) { + // usual case: all-upper case name to all-lower case compareInWireFormat(example_name_upper.downcase(), example_name); + // confirm that non upper-case characters are intact compareInWireFormat(nameFactoryLowerCase().downcase(), nameFactoryLowerCase()); + // confirm the calling object is actually modified + example_name_upper.downcase(); + compareInWireFormat(example_name_upper, example_name); + } } From f787dfa3d7c6d2092ff89ffa3504a1f525b8c8a9 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 03:47:52 +0000 Subject: [PATCH 28/54] added <=, <, >=, > git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@381 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 24 ++++++++++++ src/lib/dns/cpp/name.h | 16 ++++---- src/lib/dns/cpp/name_unittest.cc | 64 +++++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index 91acc94f2a..e3740bfdbf 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -519,6 +519,30 @@ Name::equals(const Name& other) const 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 { diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 91bd40a4d9..2f64415c2e 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -313,46 +313,46 @@ public: /// /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. - /// \return true if compare(other).get_order() <= 0; + /// \return true if compare(other).getOrder() <= 0; /// otherwise false. bool leq(const Name& other) const; /// Same as leq() - bool operator<=(const Name& other) const; + bool operator<=(const Name& other) const { return (leq(other)); } /// \brief Greater-than or equal comparison for Name against /// other /// /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. - /// \return true if compare(other).get_order() >= 0; + /// \return true if compare(other).getOrder() >= 0; /// otherwise false. bool geq(const Name& other) const; /// Same as geq() - bool operator>=(const Name& other) const; + bool operator>=(const Name& other) const { return (geq(other)); } /// \brief Less-than comparison for Name against other /// /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. - /// \return true if compare(other).get_order() < 0; + /// \return true if compare(other).getOrder() < 0; /// otherwise false. bool lthan(const Name& other) const; /// Same as lthan() - bool operator<(const Name& other) const; + bool operator<(const Name& other) const { return (lthan(other)); } /// \brief Greater-than comparison for Name against other /// /// The comparison is based on the result of the \c compare() method. /// \param other the Name object to compare against. - /// \return true if compare(other).get_order() > 0; + /// \return true if compare(other).getOrder() > 0; /// otherwise false. bool gthan(const Name& other) const; /// Same as gthan() - bool operator>(const Name& other) const; + bool operator>(const Name& other) const { return (gthan(other)); } //@} /// diff --git a/src/lib/dns/cpp/name_unittest.cc b/src/lib/dns/cpp/name_unittest.cc index d6a5c3833e..c14c9095cd 100644 --- a/src/lib/dns/cpp/name_unittest.cc +++ b/src/lib/dns/cpp/name_unittest.cc @@ -39,11 +39,15 @@ class NameTest : public ::testing::Test { protected: NameTest() : example_name("www.example.com"), example_name_upper("WWW.EXAMPLE.COM"), + small_name("aaa.example.com"), + large_name("zzz.example.com"), buffer_actual(0), buffer_expected(0) {} - Name example_name; - Name example_name_upper; + const Name example_name; + Name example_name_upper; // this will be modified and cannot be const + const Name small_name; + const Name large_name; OutputBuffer buffer_actual, buffer_expected; static const size_t MAX_WIRE = Name::MAX_WIRE; @@ -402,4 +406,60 @@ TEST_F(NameTest, downcase) compareInWireFormat(example_name_upper, example_name); } + +// +// The following set of tests confirm the result of <=, <, >=, > +// The test logic is simple, and all tests are just straightforward variations +// of the first one. +// +TEST_F(NameTest, leq) +{ + // small <= large is true + EXPECT_TRUE(small_name.leq(large_name)); + EXPECT_TRUE(small_name <= large_name); + + // small <= small is true + EXPECT_TRUE(small_name.leq(small_name)); + EXPECT_TRUE(small_name <= small_name); + + // large <= small is false + EXPECT_FALSE(large_name.leq(small_name)); + EXPECT_FALSE(large_name <= small_name); +} + +TEST_F(NameTest, geq) +{ + EXPECT_TRUE(large_name.geq(small_name)); + EXPECT_TRUE(large_name >= small_name); + + EXPECT_TRUE(large_name.geq(large_name)); + EXPECT_TRUE(large_name >= large_name); + + EXPECT_FALSE(small_name.geq(large_name)); + EXPECT_FALSE(small_name >= large_name); +} + +TEST_F(NameTest, lthan) +{ + EXPECT_TRUE(small_name.lthan(large_name)); + EXPECT_TRUE(small_name < large_name); + + EXPECT_FALSE(small_name.lthan(small_name)); + EXPECT_FALSE(small_name < small_name); + + EXPECT_FALSE(large_name.lthan(small_name)); + EXPECT_FALSE(large_name < small_name); +} + +TEST_F(NameTest, gthan) +{ + EXPECT_TRUE(large_name.gthan(small_name)); + EXPECT_TRUE(large_name > small_name); + + EXPECT_FALSE(large_name.gthan(large_name)); + EXPECT_FALSE(large_name > large_name); + + EXPECT_FALSE(small_name.gthan(large_name)); + EXPECT_FALSE(small_name > large_name); +} } From eae7ecfc7931454fbd6d739a74bf37841d727d26 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 04:00:27 +0000 Subject: [PATCH 29/54] documented << git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@382 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 2f64415c2e..603f682dc0 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -466,7 +466,22 @@ private: } } -std::ostream& operator<<(std::ostream& os, const isc::dns::Name& name); +/// +/// \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 isc::dns::Name& name); #endif // __NAME_H // Local Variables: From 5a74492d01623988cd8194b6b5802018da2c529b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 04:00:39 +0000 Subject: [PATCH 30/54] typo git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@383 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 603f682dc0..0b25fde395 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -412,7 +412,7 @@ public: /// 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 OutpubBuffer class object, then the + /// 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: /// From 50c15b8ff2a6d84dbb2cde7013848a090622693e Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 07:35:08 +0000 Subject: [PATCH 31/54] cleanup: removed an unnecessary declaration. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@384 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 0b25fde395..98f08b5a07 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -27,7 +27,6 @@ namespace dns { class InputBuffer; class OutputBuffer; class MessageRenderer; -class NameDecompressor; /// /// \brief A standard DNS module exception that is thrown if the name parser From 38b6c4ec8ff4c8dd57c99bb226b0463b77b55a70 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 07:38:12 +0000 Subject: [PATCH 32/54] note about the private default constructor. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@385 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 98f08b5a07..2f3f0ba192 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -190,6 +190,11 @@ class Name { //@{ 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), labels_(0) {} public: /// Constructor from a string From d09d0eccb1cd7f3ca46cfe23c944bfe66a4ecf44 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Fri, 18 Dec 2009 22:48:37 +0000 Subject: [PATCH 33/54] (re)cleanup: << should be in isc::dns. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@388 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 6 +++--- src/lib/dns/cpp/name.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index e3740bfdbf..41e32762c0 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -666,12 +666,12 @@ Name::downcase() return (*this); } -} -} std::ostream& -operator<<(std::ostream& os, const isc::dns::Name& name) +operator<<(std::ostream& os, const Name& name) { os << name.toText(); return (os); } +} +} diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 2f3f0ba192..0347b091cf 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -467,8 +467,6 @@ private: void fromString(const std::string& namestr); }; -} -} /// /// \brief Insert the name as a string into stream. @@ -485,7 +483,9 @@ private: /// \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 isc::dns::Name& name); +operator<<(std::ostream& os, const Name& name); +} +} #endif // __NAME_H // Local Variables: From b9ced3f2ee0534ce0d57f93b5dd3cbf52ca1d56b Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 19 Dec 2009 02:31:33 +0000 Subject: [PATCH 34/54] defined some compression related constants to avoid hardcoding magic numbers as much as possible. git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@389 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/dns/cpp/name.h b/src/lib/dns/cpp/name.h index 0347b091cf..02c5d53f10 100644 --- a/src/lib/dns/cpp/name.h +++ b/src/lib/dns/cpp/name.h @@ -457,6 +457,17 @@ public: /// \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: From 272e807f8f4f97298a0e18474f6f1763cd727118 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 19 Dec 2009 03:15:21 +0000 Subject: [PATCH 35/54] avoid using hardcoded magic numbers git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@390 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/name.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/cpp/name.cc b/src/lib/dns/cpp/name.cc index 41e32762c0..3bb80680bb 100644 --- a/src/lib/dns/cpp/name.cc +++ b/src/lib/dns/cpp/name.cc @@ -156,7 +156,7 @@ Name::Name(const std::string &namestring, bool downcase) } else if (c == '\\') { state = ft_escape; } else { - if (++count > Name::MAX_LABELLEN) { + if (++count > MAX_LABELLEN) { dns_throw(TooLongLabel, "label is too long"); } ndata.push_back(downcase ? maptolower[c] : c); @@ -172,7 +172,7 @@ Name::Name(const std::string &namestring, bool downcase) // FALLTHROUGH case ft_escape: // begin of handling a '\'-escaped sequence if (!isdigit(c & 0xff)) { - if (++count > Name::MAX_LABELLEN) { + if (++count > MAX_LABELLEN) { dns_throw(TooLongLabel, "label is too long"); } ndata.push_back(downcase ? maptolower[c] : c); @@ -194,7 +194,7 @@ Name::Name(const std::string &namestring, bool downcase) if (value > 255) { dns_throw(BadEscape, "escaped decimal is too large"); } - if (++count > Name::MAX_LABELLEN) { + if (++count > MAX_LABELLEN) { dns_throw(TooLongLabel, "label is too long"); } ndata.push_back(downcase ? maptolower[value] : value); @@ -275,7 +275,7 @@ Name::Name(InputBuffer& buffer, bool downcase) switch (state) { case fw_start: - if (c < 64) { + if (c <= MAX_LABELLEN) { offsets.push_back(nused); if (nused + c + 1 > Name::MAX_WIRE) { dns_throw(TooLongName, "wire name is too long"); @@ -287,11 +287,11 @@ Name::Name(InputBuffer& buffer, bool downcase) } n = c; state = fw_ordinary; - } else if (c >= 192) { + } else if ((c & COMPRESS_POINTER_MARK8) == COMPRESS_POINTER_MARK8) { // // Ordinary 14-bit pointer. // - new_current = c & 0x3F; + new_current = c & ~COMPRESS_POINTER_MARK8; n = 1; state = fw_newcurrent; } else { @@ -366,7 +366,7 @@ Name::toText(bool omit_final_dot) const std::string::const_iterator np_end = ndata_.end(); unsigned int labels = labels_; // use for integrity check // init with an impossible value to catch error cases in the end: - unsigned int count = Name::MAX_LABELLEN + 1; + 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. @@ -384,7 +384,7 @@ Name::toText(bool omit_final_dot) const break; } - if (count <= Name::MAX_LABELLEN) { + if (count <= MAX_LABELLEN) { assert(np_end - np >= count); if (!result.empty()) { @@ -455,7 +455,7 @@ Name::compare(const Name& other) const // We don't support any extended label types including now-obsolete // bitstring labels. - assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN); + assert(count1 <= MAX_LABELLEN && count2 <= MAX_LABELLEN); int cdiff = (int)count1 - (int)count2; unsigned int count = (cdiff < 0) ? count1 : count2; @@ -652,7 +652,7 @@ Name::downcase() // 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 <= Name::MAX_LABELLEN); + assert(count <= MAX_LABELLEN); assert(nlen >= count); while (count > 0) { From fbf185b495eaa5097b9192661258f76ddc794f67 Mon Sep 17 00:00:00 2001 From: JINMEI Tatuya Date: Sat, 19 Dec 2009 03:16:26 +0000 Subject: [PATCH 36/54] documented the message renderer class git-svn-id: svn://bind10.isc.org/svn/bind10/branches/jinmei-dnsmessageapi@391 e5f2f494-b856-4b98-b285-d166d9295462 --- src/lib/dns/cpp/messagerenderer.cc | 70 +++++++++++++++++++++++---- src/lib/dns/cpp/messagerenderer.h | 77 ++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 10 deletions(-) diff --git a/src/lib/dns/cpp/messagerenderer.cc b/src/lib/dns/cpp/messagerenderer.cc index cc08f2faaf..938429e5e4 100644 --- a/src/lib/dns/cpp/messagerenderer.cc +++ b/src/lib/dns/cpp/messagerenderer.cc @@ -27,17 +27,37 @@ namespace isc { namespace dns { +namespace { // hide internal-only names from the public namespaces +/// +/// \brief The \c NameCompressNode class represents a pointer to a name +/// rendered in the internal buffer for the \c MessageRendererImpl object. +/// +/// A \c MessageRendererImpl object maintains a set of the \c NameCompressNode +/// objects, and searches the set for the position of the longest match +/// (ancestor) name against each new name to be rendered into the buffer. struct NameCompressNode { NameCompressNode(const OutputBuffer& buffer, size_t pos, size_t len) : buffer_(buffer), pos_(pos), len_(len) {} + /// The buffer in which the corresponding name is rendered. const OutputBuffer& buffer_; + /// The position (offset from the beginning) in the buffer where the + /// name starts. uint16_t pos_; + /// The length of the corresponding name. uint16_t len_; }; /// -/// Helper class to give ordering for MessageRendererImpl::nodeset_. +/// \brief The \c NameCompare class is a functor that gives ordering among +/// \c NameCompressNode objects stored in \c MessageRendererImpl::nodeset_. /// +/// Its only public method as a functor, \c operator(), gives the ordering +/// between two \c NameCompressNode objects in terms of equivalence, that is, +/// returns whether one is "less than" the other. +/// For our purpose we only need to distinguish two different names, so the +/// ordering is different from the canonical DNS name order used in DNSSEC; +/// basically, it gives the case-insensitive ordering of the two names as their +/// textual representation. struct NameCompare : public std::binary_function { @@ -45,6 +65,11 @@ struct NameCompare : public std::binary_function