Parcourir la source

[master] fixed conflicts in changelog (due to me forgetting push?)

JINMEI Tatuya il y a 13 ans
Parent
commit
c12cdc5ffe

+ 10 - 1
ChangeLog

@@ -1,10 +1,19 @@
-391.	[func]*		jinmei
+392.	[func]*		jinmei
 	libdns++: revised the (Abstract)MessageRenderer class so that it
 	has a default internal buffer and the buffer can be temporarily
 	switched.  The constructor interface was modified, and a new
 	method setBuffer() was added.
 	(Trac #1697, git 9cabc799f2bf9a3579dae7f1f5d5467c8bb1aa40)
 
+391.	[bug]*		vorner
+	The long time unused configuration options of Xfrout "log_name",
+	"log_file", "log_severity", "log_version" and "log_max_bytes" were
+	removed, as they had no effect (Xfrout uses the global logging
+	framework).  However, if you have them set, you need to remove
+	them from the configuration file or the configuration will be
+	rejected.
+	(Trac #1090, git ef1eba02e4cf550e48e7318702cff6d67c1ec82e)
+
 bind10-devel-20120301 released on March 1, 2012
 
 390.	[bug]		vorner

+ 7 - 5
src/bin/auth/b10-auth.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-auth
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: February 28, 2012
+.\"      Date: March 1, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-AUTH" "8" "February 28, 2012" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "March 1, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -161,16 +161,18 @@ argument to select the process ID to stop\&. (Note that the BIND 10 boss process
 .PP
 The statistics data collected by the
 \fBb10\-stats\fR
-daemon include:
+daemon for
+\(lqAuth\(rq
+include:
 .PP
-auth\&.queries\&.tcp
+queries\&.tcp
 .RS 4
 Total count of queries received by the
 \fBb10\-auth\fR
 server over TCP since startup\&.
 .RE
 .PP
-auth\&.queries\&.udp
+queries\&.udp
 .RS 4
 Total count of queries received by the
 \fBb10\-auth\fR

+ 4 - 4
src/bin/auth/b10-auth.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>February 28, 2012</date>
+    <date>March 1, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -201,20 +201,20 @@
 
     <para>
       The statistics data collected by the <command>b10-stats</command>
-      daemon include:
+      daemon for <quote>Auth</quote> include:
     </para>
 
     <variablelist>
 
       <varlistentry>
-        <term>auth.queries.tcp</term>
+        <term>queries.tcp</term>
         <listitem><simpara>Total count of queries received by the
           <command>b10-auth</command> server over TCP since startup.
         </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>auth.queries.udp</term>
+        <term>queries.udp</term>
         <listitem><simpara>Total count of queries received by the
           <command>b10-auth</command> server over UDP since startup.
         </simpara></listitem>

+ 6 - 4
src/bin/bind10/bind10.8

@@ -2,12 +2,12 @@
 .\"     Title: bind10
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: February 28, 2012
+.\"      Date: March 1, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "BIND10" "8" "February 28, 2012" "BIND10" "BIND10"
+.TH "BIND10" "8" "March 1, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -297,9 +297,11 @@ will exit\&.
 .PP
 The statistics data collected by the
 \fBb10\-stats\fR
-daemon include:
+daemon for
+\(lqBoss\(rq
+include:
 .PP
-bind10\&.boot_time
+boot_time
 .RS 4
 The date and time that the
 \fBbind10\fR

+ 3 - 3
src/bin/bind10/bind10.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>February 28, 2012</date>
+    <date>March 1, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -433,13 +433,13 @@ xfrin
 
     <para>
       The statistics data collected by the <command>b10-stats</command>
-      daemon include:
+      daemon for <quote>Boss</quote> include:
     </para>
 
     <variablelist>
 
       <varlistentry>
-        <term>bind10.boot_time</term>
+        <term>boot_time</term>
         <listitem><para>
           The date and time that the <command>bind10</command>
           process started.

+ 16 - 14
src/bin/stats/b10-stats.8

@@ -2,12 +2,12 @@
 .\"     Title: b10-stats
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: February 28, 2012
+.\"      Date: March 1, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-STATS" "8" "February 28, 2012" "BIND10" "BIND10"
+.TH "B10\-STATS" "8" "March 1, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -90,25 +90,22 @@ simply indicates that the daemon is running\&.
 .PP
 The
 \fBb10\-stats\fR
-daemon contains these statistics:
+daemon contains these
+\(lqStats\(rq
+statistics:
 .PP
-report_time
-.RS 4
-The latest report date and time in ISO 8601 format\&.
-.RE
-.PP
-stats\&.boot_time
+boot_time
 .RS 4
 The date and time when this daemon was started in ISO 8601 format\&. This is a constant which can\'t be reset except by restarting
 \fBb10\-stats\fR\&.
 .RE
 .PP
-stats\&.last_update_time
+last_update_time
 .RS 4
 The date and time (in ISO 8601 format) when this daemon last received data from another component\&.
 .RE
 .PP
-stats\&.lname
+lname
 .RS 4
 This is the name used for the
 \fBb10\-msgq\fR
@@ -116,14 +113,19 @@ command\-control channel\&. (This is a constant which can\'t be reset except by
 \fBb10\-stats\fR\&.)
 .RE
 .PP
-stats\&.start_time
+report_time
+.RS 4
+The latest report date and time in ISO 8601 format\&.
+.RE
+.PP
+start_time
 .RS 4
 This is the date and time (in ISO 8601 format) when this daemon started collecting data\&.
 .RE
 .PP
-stats\&.timestamp
+timestamp
 .RS 4
-The current date and time represented in seconds since UNIX epoch (1970\-01\-01T0 0:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&.
+The current date and time represented in seconds since UNIX epoch (1970\-01\-01T00:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&.
 .RE
 .PP
 See other manual pages for explanations for their statistics that are kept track by

+ 15 - 14
src/bin/stats/b10-stats.xml

@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>February 28, 2012</date>
+    <date>March 1, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -146,20 +146,15 @@
     <title>STATISTICS DATA</title>
 
     <para>
-      The <command>b10-stats</command> daemon contains these statistics:
+      The <command>b10-stats</command> daemon contains these
+      <quote>Stats</quote> statistics:
     </para>
 
     <variablelist>
 
-      <varlistentry>
-        <term>report_time</term>
-<!-- TODO: why not named stats.report_time? -->
-        <listitem><simpara>The latest report date and time in
-          ISO 8601 format.</simpara></listitem>
-      </varlistentry>
 
       <varlistentry>
-        <term>stats.boot_time</term>
+        <term>boot_time</term>
         <listitem><simpara>The date and time when this daemon was
           started in ISO 8601 format.
           This is a constant which can't be reset except by restarting
@@ -168,14 +163,14 @@
       </varlistentry>
 
       <varlistentry>
-        <term>stats.last_update_time</term>
+        <term>last_update_time</term>
         <listitem><simpara>The date and time (in ISO 8601 format)
           when this daemon last received data from another component.
         </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>stats.lname</term>
+        <term>lname</term>
         <listitem><simpara>This is the name used for the
           <command>b10-msgq</command> command-control channel.
           (This is a constant which can't be reset except by restarting
@@ -184,16 +179,22 @@
       </varlistentry>
 
       <varlistentry>
-        <term>stats.start_time</term>
+        <term>report_time</term>
+        <listitem><simpara>The latest report date and time in
+          ISO 8601 format.</simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>start_time</term>
         <listitem><simpara>This is the date and time (in ISO 8601 format)
           when this daemon started collecting data.
         </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>stats.timestamp</term>
+        <term>timestamp</term>
         <listitem><simpara>The current date and time represented in
-          seconds since UNIX epoch (1970-01-01T0 0:00:00Z) with
+          seconds since UNIX epoch (1970-01-01T00:00:00Z) with
           precision (delimited with a period) up to
           one hundred thousandth of second.</simpara></listitem>
       </varlistentry>

+ 0 - 27
src/bin/xfrout/b10-xfrout.xml

@@ -115,33 +115,6 @@
       See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
       The default is an empty list, that is, no zone specific configuration.
     </para>
-    <para>
-      <varname>log_name</varname>
-<!-- TODO -->
-    </para>
-    <para>
-      <varname>log_file</varname>
-<!-- TODO -->
-      The location of the log file if using a file channel.
-      If undefined, then the file channel is closed.
-      The default is
-      <filename>/usr/local/var/bind10-devel/log/Xfrout.log</filename>.
-    </para>
-    <para>
-      <varname>log_severity</varname>
-<!-- TODO -->
-      The default is "debug".
-    </para>
-    <para>
-      <varname>log_versions</varname>
-<!-- TODO -->
-      The default is 5.
-    </para>
-    <para>
-      <varname>log_max_bytes</varname>
-<!-- TODO -->
-      The default is 1048576.
-    </para>
 
 <!-- TODO: log configurations not documented yet in here. jreed
      has some but waiting on decisions ... -->

+ 0 - 30
src/bin/xfrout/xfrout.spec.pre.in

@@ -9,36 +9,6 @@
          "item_default": 10
        },
        {
-         "item_name": "log_name",
-         "item_type": "string",
-         "item_optional": false,
-         "item_default": "Xfrout"
-       },
-       {
-         "item_name": "log_file",
-         "item_type": "string",
-         "item_optional": false,
-         "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/log/Xfrout.log"
-       },
-       {
-         "item_name": "log_severity",
-         "item_type": "string",
-         "item_optional": false,
-         "item_default": "debug"
-       },
-       {
-         "item_name": "log_versions",
-         "item_type": "integer",
-         "item_optional": false,
-         "item_default": 5
-       },
-       {
-         "item_name": "log_max_bytes",
-         "item_type": "integer",
-         "item_optional": false,
-         "item_default": 1048576
-       },
-       {
          "item_name": "transfer_acl",
          "item_type": "list",
          "item_optional": false,

+ 6 - 4
src/lib/bench/tests/benchmark_unittest.cc

@@ -70,9 +70,9 @@ TEST(BenchMarkTest, run) {
     const int sleep_time = 50000; // will sleep for 50ms
     const struct timespec sleep_timespec = { 0, sleep_time * 1000 };
     // we cannot expect particular accuracy on the measured duration, so
-    // we'll include some conservative margin (25%) and perform range
+    // we'll include some conservative margin (50%) and perform range
     // comparison below.
-    const int duration_margin = 12500; // 12.5ms
+    const int duration_margin = 25000; // 25ms
     const int ONE_MILLION = 1000000;
 
     // Prerequisite check: since the tests in this case may depend on subtle
@@ -80,6 +80,8 @@ TEST(BenchMarkTest, run) {
     // where sleeping doesn't work as this test expects.  So we check the
     // conditions before the tests, and if it fails skip the tests at the
     // risk of overlooking possible bugs.
+    // We do this with a tighter margin than the checks themselves
+    const int duration_soft_margin = 12500; // 12.5ms
     struct timeval check_begin, check_end;
     gettimeofday(&check_begin, NULL);
     nanosleep(&sleep_timespec, 0);
@@ -93,8 +95,8 @@ TEST(BenchMarkTest, run) {
         --check_end.tv_sec;
     }
     if (check_end.tv_sec != 0 ||
-        sleep_time - duration_margin > check_end.tv_usec ||
-        sleep_time + duration_margin < check_end.tv_usec) {
+        sleep_time - duration_soft_margin > check_end.tv_usec ||
+        sleep_time + duration_soft_margin < check_end.tv_usec) {
         cerr << "Prerequisite check failed.  skipping test" << endl;
         return;
     }

+ 1 - 0
src/lib/datasrc/Makefile.am

@@ -21,6 +21,7 @@ libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
 libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
 libdatasrc_la_SOURCES += query.h query.cc
 libdatasrc_la_SOURCES += cache.h cache.cc
+libdatasrc_la_SOURCES += rbnode_rrset.h
 libdatasrc_la_SOURCES += rbtree.h
 libdatasrc_la_SOURCES += zonetable.h zonetable.cc
 libdatasrc_la_SOURCES += zone.h

+ 9 - 3
src/lib/datasrc/memory_datasrc.cc

@@ -34,6 +34,7 @@
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/rbtree.h>
+#include <datasrc/rbnode_rrset.h>
 #include <datasrc/logger.h>
 #include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
@@ -419,14 +420,19 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * access is without the impl_-> and it will get inlined anyway.
      */
     // Implementation of InMemoryZoneFinder::add
-    result::Result add(const ConstRRsetPtr& rrset, ZoneData& zone_data) {
+    result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data) {
         // Sanitize input.  This will cause an exception to be thrown
         // if the input RRset is empty.
-        addValidation(rrset);
+        addValidation(rawrrset);
 
         // OK, can add the RRset.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
-            arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
+            arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
+
+        // ... although instead of loading the RRset directly, we encapsulate
+        // it within an RBNodeRRset.  This contains additional information that
+        // speeds up queries.
+        ConstRRsetPtr rrset(new internal::RBNodeRRset(rawrrset));
 
         if (rrset->getType() == RRType::NSEC3()) {
             return (addNSEC3(rrset, zone_data));

+ 209 - 0
src/lib/datasrc/rbnode_rrset.h

@@ -0,0 +1,209 @@
+// Copyright (C) 2012  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.
+
+#ifndef __RBNODE_RRSET_H
+#define __RBNODE_RRSET_H
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+#include <util/buffer.h>
+
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace internal {
+
+/// \brief Special RRset for optimizing memory datasource requirement
+///
+/// To speed up the performance of the in-memory data source, at load time
+/// associate relevant "additional section" data with each RRset in the
+/// data source.
+///
+/// This class, derived from AbstractRRset, holds a "const" pointer to the
+/// underlying RRset object.  All calls to methods on the class are passed to
+/// the underlying object.  However, there are some restrictions:
+///
+/// - Calls to methods that change attributes of the underlying RRset (such as
+///   TTL or Name) cause an exception to be thrown.  The in-memory data source
+///   does not allow modification of these attributes.  In theory, it is a bad
+///   practice in that it doesn't preserve the assumed behavior of the base
+///   class.  In practice, however, it should be acceptable because this
+///   class is effectively hidden from applications and will only be given
+///   to them as a const pointer to the base class via find() variants.
+///   So the application cannot call non const methods anyway unless it
+///   intentionally breaks the constness.
+///
+/// - Calls that add the pointer to the associated RRSIG to the RRset are
+///   allowed (even though the pointer is to a "const" RRset).  The reason here
+///   is that RRSIGs are added to the in-memory data source after the
+///   RBNodeRRset objects have been created.  Thus there has to be the
+///   capability of modifying this information.
+///
+/// The class is not derived from RRset itself to simplify coding: part of the
+/// loading of the memory data source is handled in the BIND 10 "libdns++"
+/// code, which creates RRsets and passes them to the data source code.  This
+/// does not have to be altered if encapsulation, rather than inheritance, is
+/// used.
+///
+/// \note This class is exposed in this separate header file so that test code
+/// can refer to its definition, and only for that purpose.  Otherwise this is
+/// essentially a private class of the in-memory data source implementation,
+/// and an application shouldn't directly refer to this class.
+/// 
+// Note: non-Doxygen-documented methods are documented in the base class.
+
+class RBNodeRRset : public isc::dns::AbstractRRset {
+
+private:
+    // Note: The copy constructor and the assignment operator are intentionally
+    // defined as private as we would normally not duplicate a RBNodeRRset.
+    // (We use the "private" method instead of inheriting from
+    // boost::noncopyable so as to avoid multiple inheritance.)
+    RBNodeRRset(const RBNodeRRset& source);
+    RBNodeRRset& operator=(const RBNodeRRset& source);
+
+public:
+    /// \brief Usual Constructor
+    ///
+    /// Creates an RBNodeRRset from the pointer to the RRset passed to it.
+    ///
+    /// \param rrset Pointer to underlying RRset encapsulated by this object.
+    explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset) : rrset_(rrset)
+    {}
+
+    /// \brief Destructor
+    virtual ~RBNodeRRset() {}
+
+    // Getter and Setter Methods
+    //
+    // The getter methods pass the call through to the underlying RRset.  The
+    // setter methods thrown an exception - this specialisation of the RRset
+    // object does not expect the underlying RRset to be modified.
+
+    virtual unsigned int getRdataCount() const {
+        return (rrset_->getRdataCount());
+    }
+
+    virtual const isc::dns::Name& getName() const {
+        return (rrset_->getName());
+    }
+
+    virtual const isc::dns::RRClass& getClass() const {
+        return (rrset_->getClass());
+    }
+
+    virtual const isc::dns::RRType& getType() const {
+        return (rrset_->getType());
+    }
+
+    virtual const isc::dns::RRTTL& getTTL() const {
+        return (rrset_->getTTL());
+    }
+
+    virtual void setName(const isc::dns::Name&) {
+        isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
+    }
+
+    virtual void setTTL(const isc::dns::RRTTL&) {
+        isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
+    }
+
+    virtual std::string toText() const {
+        return (rrset_->toText());
+    }
+
+    virtual unsigned int toWire(
+            isc::dns::AbstractMessageRenderer& renderer) const {
+        return (rrset_->toWire(renderer));
+    }
+
+    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const {
+        return (rrset_->toWire(buffer));
+    }
+
+    virtual void addRdata(isc::dns::rdata::ConstRdataPtr) {
+        isc_throw(isc::NotImplemented,
+                  "RBNodeRRset::addRdata() not supported");
+    }
+
+    virtual void addRdata(const isc::dns::rdata::Rdata&) {
+        isc_throw(isc::NotImplemented,
+                  "RBNodeRRset::addRdata() not supported");
+    }
+
+    virtual isc::dns::RdataIteratorPtr getRdataIterator() const {
+        return (rrset_->getRdataIterator());
+    }
+
+    virtual isc::dns::RRsetPtr getRRsig() const {
+        return (rrset_->getRRsig());
+    }
+
+    // With all the RRsig methods, we have the problem that we store the
+    // underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
+    // but we need to modify it by adding or removing an RRSIG.  We overcome
+    // this by temporarily violating the "const" nature of the RRset to add the
+    // data.
+
+    virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(rdata);
+    }
+
+    virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(rdata);
+    }
+
+    virtual void addRRsig(const AbstractRRset& sigs) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(sigs);
+    }
+
+    virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(sigs);
+    }
+
+    virtual void addRRsig(const isc::dns::RRsetPtr& sigs) {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->addRRsig(sigs);
+    }
+
+    virtual void removeRRsig() {
+        AbstractRRset* p = const_cast<AbstractRRset*>(rrset_.get());
+        p->removeRRsig();
+    }
+
+    /// \brief Return underlying RRset pointer
+    ///
+    /// ... mainly for testing.
+    isc::dns::ConstRRsetPtr getUnderlyingRRset() const {
+        return (rrset_);
+    }
+
+private:
+    isc::dns::ConstRRsetPtr rrset_;     ///< Underlying RRset
+};
+
+}   // namespace internal
+}   // namespace datasrc
+}   // namespace isc
+
+#endif  // __RBNODE_RRSET_H

+ 7 - 4
src/lib/datasrc/tests/Makefile.am

@@ -79,6 +79,7 @@ run_unittests_sqlite3_LDADD = $(common_ldadd)
 # In-memory datasource tests
 run_unittests_memory_SOURCES = $(common_sources)
 run_unittests_memory_SOURCES += memory_datasrc_unittest.cc
+run_unittests_memory_SOURCES += rbnode_rrset_unittest.cc
 run_unittests_memory_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
 
 run_unittests_memory_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -109,19 +110,21 @@ endif
 endif
 
 EXTRA_DIST =  testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/diffs.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/example.com.signed
 EXTRA_DIST += testdata/example.org
 EXTRA_DIST += testdata/example.org.nsec3-signed
 EXTRA_DIST += testdata/example.org.nsec3-signed-noparam
 EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/mkbrokendb.c
 EXTRA_DIST += testdata/root.zone
+EXTRA_DIST += testdata/rrset_toWire1
+EXTRA_DIST += testdata/rrset_toWire2
+EXTRA_DIST += testdata/rwtest.sqlite3
 EXTRA_DIST += testdata/sql1.example.com.signed
 EXTRA_DIST += testdata/sql2.example.com.signed
 EXTRA_DIST += testdata/test-root.sqlite3
 EXTRA_DIST += testdata/test.sqlite3
 EXTRA_DIST += testdata/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3

+ 16 - 7
src/lib/datasrc/tests/memory_datasrc_unittest.cc

@@ -172,14 +172,23 @@ TEST_F(InMemoryClientTest, iterator) {
     EXPECT_EQ(result::SUCCESS, zone->add(aRRsetA));
     EXPECT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
     EXPECT_EQ(result::SUCCESS, zone->add(subRRsetA));
-    // Check it with full zone, one by one.
-    // It should be in ascending order in case of InMemory data source
-    // (isn't guaranteed in general)
+
+    // Check it with full zone.
+    vector<ConstRRsetPtr> expected_rrsets;
+    expected_rrsets.push_back(aRRsetA);
+    expected_rrsets.push_back(aRRsetAAAA);
+    expected_rrsets.push_back(subRRsetA);
+
     iterator = memory_client.getIterator(Name("a"));
-    EXPECT_EQ(aRRsetA, iterator->getNextRRset());
-    EXPECT_EQ(aRRsetAAAA, iterator->getNextRRset());
-    EXPECT_EQ(subRRsetA, iterator->getNextRRset());
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+    vector<ConstRRsetPtr> actual_rrsets;
+    ConstRRsetPtr actual;
+    while ((actual = iterator->getNextRRset()) != NULL) {
+        actual_rrsets.push_back(actual);
+    }
+
+    rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                actual_rrsets.begin(), actual_rrsets.end());
+
 }
 
 TEST_F(InMemoryClientTest, iterator_separate_rrs) {

+ 258 - 0
src/lib/datasrc/tests/rbnode_rrset_unittest.cc

@@ -0,0 +1,258 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdexcept>
+
+#include <exceptions/exceptions.h>
+#include <dns/rdataclass.h>
+#include <datasrc/rbnode_rrset.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+using isc::UnitTestUtil;
+
+using namespace isc;
+using namespace isc::datasrc;
+using namespace isc::datasrc::internal;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::testutils;
+using namespace isc::util;
+using namespace std;
+
+// These tests are very similar to those for RRset - indeed, this file was
+// created from those tests.  However, the significant difference in behaviour
+// between RRset and RBNodeRRset - that the "set" methods in the latter mostly
+// result in exceptions being thrown - preclude use of full type
+// parameterisation of the tests.
+
+namespace {
+const char* const RRSIG_TXT =
+    "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
+    "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
+    "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
+    "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
+    "f49t+sXKPzbipN9g+s1ZPiIyofc=";
+
+class RBNodeRRsetTest : public ::testing::Test {
+protected:
+    RBNodeRRsetTest() :
+        test_name("test.example.com"),
+        test_domain("example.com"),
+        test_nsname("ns.example.com"),
+        rrset_a(ConstRRsetPtr(new RRset(
+                test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+        rrset_a_empty(ConstRRsetPtr(new RRset(
+                      test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+        rrset_ns(ConstRRsetPtr(new RRset(
+                 test_domain, RRClass::IN(), RRType::NS(), RRTTL(86400)))),
+        rrset_ch_txt(ConstRRsetPtr(new RRset(
+                     test_domain, RRClass::CH(), RRType::TXT(), RRTTL(0)))),
+        rrset_siga(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+                   RRTTL(3600)))
+
+    {
+        // Add a couple of Rdata elements to the A RRset.  The easiest way to
+        // do this is to override the "const" restrictions.  As this is a test,
+        // we don't feel too bad about doing so.
+        AbstractRRset* a_rrset =
+            const_cast<AbstractRRset*>(rrset_a.getUnderlyingRRset().get());
+        a_rrset->addRdata(in::A("192.0.2.1"));
+        a_rrset->addRdata(in::A("192.0.2.2"));
+
+        // Create the RRSIG corresponding to the rrset_a record.  The RDATA
+        // won't match the A record it covers, although it is internally
+        // self-consistent.
+        AbstractRRset* sig_rrset =
+            const_cast<AbstractRRset*>(rrset_siga.get());
+        sig_rrset->addRdata(generic::RRSIG(RRSIG_TXT));
+    }
+
+    const Name test_name;
+    const Name test_domain;
+    const Name test_nsname;
+
+    RBNodeRRset rrset_a;
+    RBNodeRRset rrset_a_empty;
+    const RBNodeRRset rrset_ns;
+    const RBNodeRRset rrset_ch_txt;
+
+    ConstRRsetPtr rrset_siga;
+};
+
+TEST_F(RBNodeRRsetTest, getRdataCount) {
+    EXPECT_EQ(0, rrset_a_empty.getRdataCount());
+    EXPECT_EQ(2, rrset_a.getRdataCount());
+}
+
+TEST_F(RBNodeRRsetTest, getName) {
+    EXPECT_EQ(test_name, rrset_a.getName());
+    EXPECT_EQ(test_domain, rrset_ns.getName());
+}
+
+TEST_F(RBNodeRRsetTest, getClass) {
+    EXPECT_EQ(RRClass("IN"), rrset_a.getClass());
+    EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass());
+}
+
+TEST_F(RBNodeRRsetTest, getType) {
+    EXPECT_EQ(RRType("A"), rrset_a.getType());
+    EXPECT_EQ(RRType("NS"), rrset_ns.getType());
+    EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType());
+}
+
+TEST_F(RBNodeRRsetTest, getTTL) {
+    EXPECT_EQ(RRTTL(3600), rrset_a.getTTL());
+    EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL());
+    EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL());
+}
+
+TEST_F(RBNodeRRsetTest, setName) {
+    EXPECT_THROW(rrset_a.setName(test_nsname), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, setTTL) {
+    EXPECT_THROW(rrset_a.setTTL(RRTTL(86400)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, toText) {
+    EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+              "test.example.com. 3600 IN A 192.0.2.2\n",
+              rrset_a.toText());
+
+    // toText() cannot be performed for an empty RRset.
+    EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
+}
+
+// Note: although the next two tests are essentially the same and used common
+// test code, they use different test data: the MessageRenderer produces
+// compressed wire data whereas the OutputBuffer does not.
+
+template <typename T>
+void
+performToWireTest(T& dataHolder, const RBNodeRRset& rrset,
+                  const RBNodeRRset& rrset_empty, const char* testdata)
+{
+    rrset.toWire(dataHolder);
+
+    std::vector<unsigned char> wiredata;
+    UnitTestUtil::readWireData(testdata, wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, dataHolder.getData(),
+                        dataHolder.getLength(), &wiredata[0], wiredata.size());
+
+    // toWire() cannot be performed for an empty RRset.
+    dataHolder.clear();
+    EXPECT_THROW(rrset_empty.toWire(dataHolder), EmptyRRset);
+}
+
+TEST_F(RBNodeRRsetTest, toWireRenderer) {
+    MessageRenderer renderer;
+    performToWireTest(renderer, rrset_a, rrset_a_empty, "rrset_toWire2");
+}
+
+TEST_F(RBNodeRRsetTest, toWireBuffer) {
+    OutputBuffer buffer(0);
+    performToWireTest(buffer, rrset_a, rrset_a_empty, "rrset_toWire1");
+}
+
+TEST_F(RBNodeRRsetTest, addRdata) {
+    EXPECT_THROW(rrset_a.addRdata(in::A("192.0.2.3")), NotImplemented);
+
+    // Check the same goes for trying to add the wrong type of data
+    EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, addRdataPtr) {
+    EXPECT_THROW(rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
+                                                    rrset_a_empty.getClass(),
+                                                    "192.0.2.1")),
+                 NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, getRDataIterator) {
+    RdataIteratorPtr it = rrset_a.getRdataIterator();
+    for (int i = 0; i < 2; ++i) {
+        ASSERT_FALSE(it->isLast());
+        ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1")));
+
+        it->next();
+        ASSERT_FALSE(it->isLast());
+        ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2")));
+
+        it->next();
+        ASSERT_TRUE(it->isLast());
+
+        // Should be able repeat the iteration by calling first().
+        it->first();
+    }
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST_F(RBNodeRRsetTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << rrset_a;
+    EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+              "test.example.com. 3600 IN A 192.0.2.2\n", oss.str());
+}
+
+// addRRSIG tests.
+TEST_F(RBNodeRRsetTest, addRRsigConstRdataPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    ConstRdataPtr data = createRdata(rrset_siga->getType(),
+                                     rrset_siga->getClass(), RRSIG_TXT);
+    rrset_a.addRRsig(data);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRdataPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    RdataPtr data = createRdata(rrset_siga->getType(), rrset_siga->getClass(),
+                                RRSIG_TXT);
+    rrset_a.addRRsig(data);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigAbstractRRset) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(*(rrset_siga.get()));
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigConstantRRsetPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(rrset_siga);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRRsetPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    RRsetPtr rrsig(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+                   RRTTL(3600)));
+    rrsig->addRdata(generic::RRSIG(RRSIG_TXT));
+    rrset_a.addRRsig(rrsig);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, removeRRsig) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(*(rrset_siga.get()));
+    EXPECT_TRUE(rrset_a.getRRsig());
+    rrset_a.removeRRsig();
+    EXPECT_FALSE(rrset_a.getRRsig());
+}
+
+}   // Anonymous namespace

+ 23 - 0
src/lib/datasrc/tests/testdata/rrset_toWire1

@@ -0,0 +1,23 @@
+#
+# Rendering an IN/A RRset containing 2 RRs:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+#
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6  7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: mostly the same except the RDATA
+04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02

+ 26 - 0
src/lib/datasrc/tests/testdata/rrset_toWire2

@@ -0,0 +1,26 @@
+#
+# Rendering an IN/A RRset and NS RRset as follows:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+# example.com. 1D IN NS ns.example.com.
+# Names will be compressed when possible.
+#
+# 0  1  2  3  4  5
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6  7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: the owner name is compresed
+c0 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02

+ 2 - 0
src/lib/dns/Makefile.am

@@ -91,6 +91,7 @@ libdns___la_LDFLAGS = -no-undefined -version-info 1:0:1
 libdns___la_SOURCES =
 libdns___la_SOURCES += edns.h edns.cc
 libdns___la_SOURCES += exceptions.h exceptions.cc
+libdns___la_SOURCES += labelsequence.h labelsequence.cc
 libdns___la_SOURCES += masterload.h masterload.cc
 libdns___la_SOURCES += message.h message.cc
 libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
@@ -140,6 +141,7 @@ libdns___includedir = $(includedir)/dns
 libdns___include_HEADERS = \
 	edns.h \
 	exceptions.h \
+	labelsequence.h \
 	message.h \
 	messagerenderer.h \
 	name.h \

+ 77 - 0
src/lib/dns/labelsequence.cc

@@ -0,0 +1,77 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/labelsequence.h>
+#include <exceptions/exceptions.h>
+
+#include <iostream>
+namespace isc {
+namespace dns {
+
+const char*
+LabelSequence::getData(size_t *len) const {
+    // If the labelsequence is absolute, the current last_label_ falls
+    // out of the vector (since it points to the 'label' after the
+    // root label, which doesn't exist; in that case, return
+    // the length for the 'previous' label (the root label) plus
+    // one (for the root label zero octet)
+    if (isAbsolute()) {
+        *len = name_.offsets_[last_label_ - 1] - name_.offsets_[first_label_] + 1;
+    } else {
+        *len = name_.offsets_[last_label_] - name_.offsets_[first_label_];
+    }
+    return (&name_.ndata_[name_.offsets_[first_label_]]);
+}
+
+bool
+LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
+    size_t len, other_len;
+    const char* data = getData(&len);
+    const char* other_data = other.getData(&other_len);
+
+    if (len != other_len) {
+        return (false);
+    }
+    if (case_sensitive) {
+        return (strncasecmp(data, other_data, len) == 0);
+    } else {
+        return (strncmp(data, other_data, len) == 0);
+    }
+}
+
+void
+LabelSequence::stripLeft(size_t i) {
+    if (i >= getLabelCount()) {
+        isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+                              " (labelcount: " << getLabelCount() << ")");
+    }
+    first_label_ += i;
+}
+
+void
+LabelSequence::stripRight(size_t i) {
+    if (i >= getLabelCount()) {
+        isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+                              " (labelcount: " << getLabelCount() << ")");
+    }
+    last_label_ -= i;
+}
+
+bool
+LabelSequence::isAbsolute() const {
+    return (last_label_ == name_.offsets_.size());
+}
+
+} // end namespace dns
+} // end namespace isc

+ 136 - 0
src/lib/dns/labelsequence.h

@@ -0,0 +1,136 @@
+// Copyright (C) 2012  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.
+
+#ifndef __LABELSEQUENCE_H
+#define __LABELSEQUENCE_H 1
+
+#include <dns/name.h>
+#include <util/buffer.h>
+
+namespace isc {
+namespace dns {
+
+/// \brief Light-weight Accessor to Name object
+///
+/// The purpose of this class is to easily match Names and parts of Names,
+/// without needing to copy the underlying data on each label strip.
+///
+/// It can only work on existing Name objects, and the Name object MUST
+/// remain in scope during the entire lifetime of its associated
+/// LabelSequence(s).
+///
+/// Upon creation of a LabelSequence, it records the offsets of the
+/// labels in the wireformat data of the Name. When stripLeft() or
+/// stripRight() is called on the LabelSequence, no changes in the
+/// Name's data occur, but the internal pointers of the
+/// LabelSequence are modified.
+///
+/// LabelSequences can be compared to other LabelSequences, and their
+/// data can be requested (which then points to part of the original
+/// data of the associated Name object).
+///
+class LabelSequence {
+public:
+    /// \brief Constructs a LabelSequence for the given name
+    ///
+    /// \note The associated Name MUST remain in scope during the lifetime
+    /// of this LabelSequence, since getData() refers to data from the
+    /// Name object (the only data the LabelSequence stores are pointers
+    /// to the labels in the Name object).
+    ///
+    /// \param name The Name to construct a LabelSequence for
+    LabelSequence(const Name& name): name_(name),
+                                     first_label_(0),
+                                     last_label_(name.getLabelCount())
+    {}
+
+    /// \brief Return the wire-format data for this LabelSequence
+    ///
+    /// The data, is returned as a pointer to the original wireformat
+    /// data of the original Name object, and the given len value is
+    /// set to the number of octets that match this labelsequence.
+    ///
+    /// \note The data pointed to is only valid if the original Name
+    /// object is still in scope
+    ///
+    /// \param len Pointer to a size_t where the length of the data
+    ///        will be stored (in number of octets)
+    /// \return Pointer to the wire-format data of this label sequence
+    const char* getData(size_t* len) const;
+
+    /// \brief Compares two label sequences.
+    ///
+    /// Performs a (optionally case-insensitive) comparison between this
+    /// LabelSequence and another LabelSequence.
+    ///
+    /// \param other The LabelSequence to compare with
+    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \return true if The label sequences consist are the same length,
+    ///         and contain the same data.
+    bool equals(const LabelSequence& other, bool case_sensitive = false) const;
+
+    /// \brief Remove labels from the front of this LabelSequence
+    ///
+    /// \note No actual memory is changed, this operation merely updates the
+    /// internal pointers based on the offsets in the Name object.
+    ///
+    /// \exeption OutOfRange if i is greater than or equal to the number
+    ///           of labels currently pointed to by this LabelSequence
+    ///
+    /// \param i The number of labels to remove.
+    void stripLeft(size_t i);
+
+    /// \brief Remove labels from the end of this LabelSequence
+    ///
+    /// \note No actual memory is changed, this operation merely updates the
+    /// internal pointers based on the offsets in the Name object.
+    ///
+    /// \exeption OutOfRange if i is greater than or equal to the number
+    ///           of labels currently pointed to by this LabelSequence
+    ///
+    /// \param i The number of labels to remove.
+    void stripRight(size_t i);
+
+    /// \brief Returns the current number of labels for this LabelSequence
+    ///
+    /// \return The number of labels
+    size_t getLabelCount() const { return last_label_ - first_label_; }
+
+    /// \brief Returns the original Name object associated with this
+    ///        LabelSequence
+    ///
+    /// While the Name should still be in scope during the lifetime of
+    /// the LabelSequence, it can still be useful to have access to it,
+    /// for instance in helper functions that are only passed the
+    /// LabelSequence itself.
+    ///
+    /// \return Reference to the original Name object
+    const Name& getName() const { return name_; }
+
+    /// \brief Checks whether the label sequence is absolute
+    ///
+    /// \return true if the last label is the root label
+    bool isAbsolute() const;
+
+private:
+    const Name& name_;
+    size_t first_label_;
+    size_t last_label_;
+};
+
+
+} // end namespace dns
+} // end namespace isc
+
+#endif

+ 6 - 0
src/lib/dns/name.h

@@ -210,6 +210,11 @@ private:
 /// names as a special case.
 ///
 class Name {
+    // LabelSequences use knowledge about the internal data structure
+    // of this class for efficiency (they use the offsets_ vector and
+    // the ndata_ string)
+    friend class LabelSequence;
+
     ///
     /// \name Constructors and Destructor
     ///
@@ -298,6 +303,7 @@ public:
         }
         return (ndata_[pos]);
     }
+
     /// \brief Gets the length of the <code>Name</code> in its wire format.
     ///
     /// This method never throws an exception.

+ 1 - 0
src/lib/dns/tests/Makefile.am

@@ -19,6 +19,7 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES = unittest_util.h unittest_util.cc
 run_unittests_SOURCES += edns_unittest.cc
+run_unittests_SOURCES += labelsequence_unittest.cc
 run_unittests_SOURCES += messagerenderer_unittest.cc
 run_unittests_SOURCES += name_unittest.cc
 run_unittests_SOURCES += nsec3hash_unittest.cc

+ 253 - 0
src/lib/dns/tests/labelsequence_unittest.cc

@@ -0,0 +1,253 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/labelsequence.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::dns;
+
+class LabelSequenceTest : public ::testing::Test {
+public:
+    LabelSequenceTest() : n1("example.org"), n2("example.com"),
+                          n3("example.org"), n4("foo.bar.test.example"),
+                          n5("example.ORG"), n6("ExAmPlE.org"),
+                          n7("."), n8("foo.example.org.bar"),
+                          ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5),
+                          ls6(n6), ls7(n7), ls8(n8)
+    {};
+    // Need to keep names in scope for at least the lifetime of
+    // the labelsequences
+    Name n1, n2, n3, n4, n5, n6, n7, n8;
+
+    LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8;
+};
+
+// Basic equality tests
+TEST_F(LabelSequenceTest, equals_sensitive) {
+    EXPECT_TRUE(ls1.equals(ls1));
+    EXPECT_FALSE(ls1.equals(ls2));
+    EXPECT_TRUE(ls1.equals(ls3));
+    EXPECT_FALSE(ls1.equals(ls4));
+    EXPECT_FALSE(ls1.equals(ls5));
+    EXPECT_FALSE(ls1.equals(ls6));
+    EXPECT_FALSE(ls1.equals(ls7));
+    EXPECT_FALSE(ls1.equals(ls8));
+
+    EXPECT_FALSE(ls2.equals(ls1));
+    EXPECT_TRUE(ls2.equals(ls2));
+    EXPECT_FALSE(ls2.equals(ls3));
+    EXPECT_FALSE(ls2.equals(ls4));
+    EXPECT_FALSE(ls2.equals(ls5));
+    EXPECT_FALSE(ls2.equals(ls6));
+    EXPECT_FALSE(ls2.equals(ls7));
+    EXPECT_FALSE(ls2.equals(ls8));
+
+    EXPECT_FALSE(ls4.equals(ls1));
+    EXPECT_FALSE(ls4.equals(ls2));
+    EXPECT_FALSE(ls4.equals(ls3));
+    EXPECT_TRUE(ls4.equals(ls4));
+    EXPECT_FALSE(ls4.equals(ls5));
+    EXPECT_FALSE(ls4.equals(ls6));
+    EXPECT_FALSE(ls4.equals(ls7));
+    EXPECT_FALSE(ls4.equals(ls8));
+
+    EXPECT_FALSE(ls5.equals(ls1));
+    EXPECT_FALSE(ls5.equals(ls2));
+    EXPECT_FALSE(ls5.equals(ls3));
+    EXPECT_FALSE(ls5.equals(ls4));
+    EXPECT_TRUE(ls5.equals(ls5));
+    EXPECT_FALSE(ls5.equals(ls6));
+    EXPECT_FALSE(ls5.equals(ls7));
+    EXPECT_FALSE(ls5.equals(ls8));
+}
+
+TEST_F(LabelSequenceTest, equals_insensitive) {
+    EXPECT_TRUE(ls1.equals(ls1, true));
+    EXPECT_FALSE(ls1.equals(ls2, true));
+    EXPECT_TRUE(ls1.equals(ls3, true));
+    EXPECT_FALSE(ls1.equals(ls4, true));
+    EXPECT_TRUE(ls1.equals(ls5, true));
+    EXPECT_TRUE(ls1.equals(ls6, true));
+    EXPECT_FALSE(ls1.equals(ls7, true));
+
+    EXPECT_FALSE(ls2.equals(ls1, true));
+    EXPECT_TRUE(ls2.equals(ls2, true));
+    EXPECT_FALSE(ls2.equals(ls3, true));
+    EXPECT_FALSE(ls2.equals(ls4, true));
+    EXPECT_FALSE(ls2.equals(ls5, true));
+    EXPECT_FALSE(ls2.equals(ls6, true));
+    EXPECT_FALSE(ls2.equals(ls7, true));
+
+    EXPECT_TRUE(ls3.equals(ls1, true));
+    EXPECT_FALSE(ls3.equals(ls2, true));
+    EXPECT_TRUE(ls3.equals(ls3, true));
+    EXPECT_FALSE(ls3.equals(ls4, true));
+    EXPECT_TRUE(ls3.equals(ls5, true));
+    EXPECT_TRUE(ls3.equals(ls6, true));
+    EXPECT_FALSE(ls3.equals(ls7, true));
+
+    EXPECT_FALSE(ls4.equals(ls1, true));
+    EXPECT_FALSE(ls4.equals(ls2, true));
+    EXPECT_FALSE(ls4.equals(ls3, true));
+    EXPECT_TRUE(ls4.equals(ls4, true));
+    EXPECT_FALSE(ls4.equals(ls5, true));
+    EXPECT_FALSE(ls4.equals(ls6, true));
+    EXPECT_FALSE(ls4.equals(ls7, true));
+
+    EXPECT_TRUE(ls5.equals(ls1, true));
+    EXPECT_FALSE(ls5.equals(ls2, true));
+    EXPECT_TRUE(ls5.equals(ls3, true));
+    EXPECT_FALSE(ls5.equals(ls4, true));
+    EXPECT_TRUE(ls5.equals(ls5, true));
+    EXPECT_TRUE(ls5.equals(ls6, true));
+    EXPECT_FALSE(ls5.equals(ls7, true));
+}
+
+void
+getDataCheck(const char* expected_data, size_t expected_len,
+             const LabelSequence& ls)
+{
+    size_t len;
+    const char* data = ls.getData(&len);
+    ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data <<
+                                    " name: " << ls.getName().toText();
+    for (size_t i = 0; i < len; ++i) {
+        EXPECT_EQ(expected_data[i], data[i]) << "Difference at pos " << i <<
+                                                ": Expected data: " <<
+                                                expected_data <<
+                                                " name: " <<
+                                                ls.getName().toText();;
+    }
+}
+
+TEST_F(LabelSequenceTest, getData) {
+    getDataCheck("\007example\003org\000", 13, ls1);
+    getDataCheck("\007example\003com\000", 13, ls2);
+    getDataCheck("\007example\003org\000", 13, ls3);
+    getDataCheck("\003foo\003bar\004test\007example\000", 22, ls4);
+    getDataCheck("\007example\003ORG\000", 13, ls5);
+    getDataCheck("\007ExAmPlE\003org\000", 13, ls6);
+    getDataCheck("\000", 1, ls7);
+};
+
+TEST_F(LabelSequenceTest, stripLeft) {
+    EXPECT_TRUE(ls1.equals(ls3));
+    ls1.stripLeft(0);
+    getDataCheck("\007example\003org\000", 13, ls1);
+    EXPECT_TRUE(ls1.equals(ls3));
+    ls1.stripLeft(1);
+    getDataCheck("\003org\000", 5, ls1);
+    EXPECT_FALSE(ls1.equals(ls3));
+    ls1.stripLeft(1);
+    getDataCheck("\000", 1, ls1);
+    EXPECT_TRUE(ls1.equals(ls7));
+
+    ls2.stripLeft(2);
+    getDataCheck("\000", 1, ls2);
+    EXPECT_TRUE(ls2.equals(ls7));
+}
+
+TEST_F(LabelSequenceTest, stripRight) {
+    EXPECT_TRUE(ls1.equals(ls3));
+    ls1.stripRight(1);
+    getDataCheck("\007example\003org", 12, ls1);
+    EXPECT_FALSE(ls1.equals(ls3));
+    ls1.stripRight(1);
+    getDataCheck("\007example", 8, ls1);
+    EXPECT_FALSE(ls1.equals(ls3));
+
+    ASSERT_FALSE(ls1.equals(ls2));
+    ls2.stripRight(2);
+    getDataCheck("\007example", 8, ls2);
+    EXPECT_TRUE(ls1.equals(ls2));
+}
+
+TEST_F(LabelSequenceTest, stripOutOfRange) {
+    EXPECT_THROW(ls1.stripLeft(100), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripLeft(5), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripLeft(4), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripLeft(3), isc::OutOfRange);
+    getDataCheck("\007example\003org\000", 13, ls1);
+
+    EXPECT_THROW(ls1.stripRight(100), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripRight(5), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripRight(4), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripRight(3), isc::OutOfRange);
+    getDataCheck("\007example\003org\000", 13, ls1);
+}
+
+TEST_F(LabelSequenceTest, getLabelCount) {
+    EXPECT_EQ(3, ls1.getLabelCount());
+    ls1.stripLeft(0);
+    EXPECT_EQ(3, ls1.getLabelCount());
+    ls1.stripLeft(1);
+    EXPECT_EQ(2, ls1.getLabelCount());
+    ls1.stripLeft(1);
+    EXPECT_EQ(1, ls1.getLabelCount());
+
+    EXPECT_EQ(3, ls2.getLabelCount());
+    ls2.stripRight(1);
+    EXPECT_EQ(2, ls2.getLabelCount());
+    ls2.stripRight(1);
+    EXPECT_EQ(1, ls2.getLabelCount());
+
+    EXPECT_EQ(3, ls3.getLabelCount());
+    ls3.stripRight(2);
+    EXPECT_EQ(1, ls3.getLabelCount());
+
+    EXPECT_EQ(5, ls4.getLabelCount());
+    ls4.stripRight(3);
+    EXPECT_EQ(2, ls4.getLabelCount());
+
+    EXPECT_EQ(3, ls5.getLabelCount());
+    ls5.stripLeft(2);
+    EXPECT_EQ(1, ls5.getLabelCount());
+}
+
+TEST_F(LabelSequenceTest, comparePart) {
+    EXPECT_FALSE(ls1.equals(ls8));
+
+    // strip root label from example.org.
+    ls1.stripRight(1);
+    // strip foo from foo.example.org.bar.
+    ls8.stripLeft(1);
+    // strip bar. (i.e. bar and root) too
+    ls8.stripRight(2);
+
+    EXPECT_TRUE(ls1.equals(ls8));
+
+    // Data comparison
+    size_t len;
+    const char* data = ls1.getData(&len);
+    getDataCheck(data, len, ls8);
+}
+
+TEST_F(LabelSequenceTest, isAbsolute) {
+    ASSERT_TRUE(ls1.isAbsolute());
+
+    ls1.stripLeft(1);
+    ASSERT_TRUE(ls1.isAbsolute());
+    ls1.stripRight(1);
+    ASSERT_FALSE(ls1.isAbsolute());
+
+    ASSERT_TRUE(ls2.isAbsolute());
+    ls2.stripRight(1);
+    ASSERT_FALSE(ls2.isAbsolute());
+
+    ASSERT_TRUE(ls3.isAbsolute());
+    ls3.stripLeft(2);
+    ASSERT_TRUE(ls3.isAbsolute());
+}

+ 0 - 3
tests/system/bindctl/nsx1/b10-config.db.template.in

@@ -3,8 +3,5 @@
    "listen_on": [{"address": "10.53.0.1", "port": 53210}],
    "database_file": "@abs_builddir@/zone.sqlite3",
    "statistics-interval": 1
- },
- "Xfrout": {
-   "log_file": "@abs_builddir@/Xfrout.log"
  }
 }

+ 0 - 3
tests/system/glue/nsx1/b10-config.db.in

@@ -2,8 +2,5 @@
  "Auth": {
    "listen_on": [{"address": "10.53.0.1", "port": 53210}],
    "database_file": "@abs_builddir@/zone.sqlite3"
- },
- "Xfrout": {
-   "log_file": "@abs_builddir@/Xfrout.log"
  }
 }