Browse Source

Merge branch 'master' into trac2121

Mukund Sivaraman 12 years ago
parent
commit
af3a7eee9a

+ 1 - 1
configure.ac

@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 # Process this file with autoconf to produce a configure script.
 
 
 AC_PREREQ([2.59])
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20120405, bind10-dev@isc.org)
+AC_INIT(bind10-devel, 20120712, bind10-dev@isc.org)
 AC_CONFIG_SRCDIR(README)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
 AM_INIT_AUTOMAKE
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible

+ 64 - 60
doc/guide/bind10-guide.xml

@@ -1243,20 +1243,18 @@ or accounts database -->
       <title>Configuration specification for b10-cmdctl</title>
       <title>Configuration specification for b10-cmdctl</title>
       <para>
       <para>
         The configuration items for <command>b10-cmdctl</command> are:
         The configuration items for <command>b10-cmdctl</command> are:
-key_file
+        <varname>accounts_file</varname> which defines the path to the
-cert_file
+        user accounts database (the default is
-accounts_file
+        <filename>/usr/local/etc/bind10-devel/cmdctl-accounts.csv</filename>);
+        <varname>cert_file</varname> which defines the path to the
+        PEM certificate file (the default is
+        <filename>/usr/local/etc/bind10-devel/cmdctl-certfile.pem</filename>);
+        and
+	<varname>key_file</varname> which defines the path to the
+	PEM private key file (the default is
+        <filename>/usr/local/etc/bind10-devel/cmdctl-keyfile.pem</filename>).
       </para>
       </para>
-<!-- TODO -->
-
-      <para>
-        The control commands are:
-print_settings
-<!-- TODO: remove that -->
 
 
-shutdown
-      </para>
-<!-- TODO -->
     </section>
     </section>
 
 
 <!--
 <!--
@@ -1309,7 +1307,8 @@ TODO
 
 
     <para>
     <para>
       The <command>b10-auth</command> is the authoritative DNS server.
       The <command>b10-auth</command> is the authoritative DNS server.
-      It supports EDNS0 and DNSSEC. It supports IPv6.
+      It supports EDNS0, DNSSEC, IPv6, and SQLite3 and in-memory zone
+      data backends.
       Normally it is started by the <command>bind10</command> master
       Normally it is started by the <command>bind10</command> master
       process.
       process.
     </para>
     </para>
@@ -1334,8 +1333,8 @@ since we used bind10 -->
               <simpara>This is an optional string to define the path to find
               <simpara>This is an optional string to define the path to find
                  the SQLite3 database file.
                  the SQLite3 database file.
 <!-- TODO: -->
 <!-- TODO: -->
-Note: Later the DNS server will use various data source backends.
+Note: This may be a temporary setting because the DNS server
-This may be a temporary setting until then.
+can use various data source backends.
               </simpara>
               </simpara>
             </listitem>
             </listitem>
           </varlistentry>
           </varlistentry>
@@ -1356,7 +1355,9 @@ This may be a temporary setting until then.
       and
       and
       <varname>zones</varname> to define
       <varname>zones</varname> to define
       the <varname>file</varname> path name,
       the <varname>file</varname> path name,
-      the <varname>filetype</varname> (e.g., <varname>sqlite3</varname>),
+      the <varname>filetype</varname> (<quote>sqlite3</quote> to load
+      from a SQLite3 database file or <quote>text</quote> to
+      load from a master text file),
       and the <varname>origin</varname> (default domain).
       and the <varname>origin</varname> (default domain).
 
 
       By default, this is empty.
       By default, this is empty.
@@ -1528,13 +1529,13 @@ This may be a temporary setting until then.
 &gt; <userinput>config commit</userinput></screen>
 &gt; <userinput>config commit</userinput></screen>
 
 
 	  The authoritative server will begin serving it immediately
 	  The authoritative server will begin serving it immediately
-	  after it is loaded.
+	  after the zone data is loaded from the master text file.
 	</para>
 	</para>
 
 
       </section>
       </section>
 
 
       <section id="in-memory-datasource-with-sqlite3-backend">
       <section id="in-memory-datasource-with-sqlite3-backend">
-	<title>In-memory Data Source With SQLite3 Backend</title>
+	<title>In-memory Data Source with SQLite3 Backend</title>
 
 
 	<para>
 	<para>
 <!--	  How to configure it. -->
 <!--	  How to configure it. -->
@@ -1556,7 +1557,7 @@ This may be a temporary setting until then.
 &gt; <userinput>config commit</userinput></screen>
 &gt; <userinput>config commit</userinput></screen>
 
 
 	  The authoritative server will begin serving it immediately
 	  The authoritative server will begin serving it immediately
-	  after it is loaded.
+	  after the zone data is loaded from the database file.
 	</para>
 	</para>
 
 
       </section>
       </section>
@@ -1707,7 +1708,7 @@ TODO
       <command>b10-auth</command>.
       <command>b10-auth</command>.
       In combination with <command>b10-zonemgr</command> (for
       In combination with <command>b10-zonemgr</command> (for
       automated SOA checks), this allows the BIND 10 server to
       automated SOA checks), this allows the BIND 10 server to
-      provide <quote>secondary</quote> service.
+      provide <emphasis>secondary</emphasis> service.
     </para>
     </para>
 
 
     <para>
     <para>
@@ -1809,11 +1810,8 @@ what if a NOTIFY is sent?
 
 
       <screen>&gt; <userinput>config add Zonemgr/secondary_zones</userinput>
       <screen>&gt; <userinput>config add Zonemgr/secondary_zones</userinput>
 &gt; <userinput>config set Zonemgr/secondary_zones[0]/name "<option>example.com</option>"</userinput>
 &gt; <userinput>config set Zonemgr/secondary_zones[0]/name "<option>example.com</option>"</userinput>
-&gt; <userinput>config set Zonemgr/secondary_zones[0]/class "<option>IN</option>"</userinput>
 &gt; <userinput>config commit</userinput></screen>
 &gt; <userinput>config commit</userinput></screen>
 
 
-<!-- TODO: remove the IN class example above when it is the default -->
-
       </para>
       </para>
 
 
       <para>
       <para>
@@ -1848,6 +1846,9 @@ what if a NOTIFY is sent?
         automatically sent a <varname>loadzone</varname> command to
         automatically sent a <varname>loadzone</varname> command to
         reload the corresponding zone into memory from the backend.
         reload the corresponding zone into memory from the backend.
       </para>
       </para>
+<!-- TODO: currently it delays the queries; see
+http://bind10.isc.org/wiki/ScalableZoneLoadDesign#a7.2UpdatingaZone
+-->
 
 
       <para>
       <para>
 	The administrator doesn't have to do anything for
 	The administrator doesn't have to do anything for
@@ -1870,7 +1871,7 @@ what if a NOTIFY is sent?
       When the <command>b10-auth</command> authoritative DNS server
       When the <command>b10-auth</command> authoritative DNS server
       receives an AXFR or IXFR request, <command>b10-auth</command>
       receives an AXFR or IXFR request, <command>b10-auth</command>
       internally forwards the request to <command>b10-xfrout</command>,
       internally forwards the request to <command>b10-xfrout</command>,
-      which handles the rest of request processing.
+      which handles the rest of this request processing.
       This is used to provide primary DNS service to share zones
       This is used to provide primary DNS service to share zones
       to secondary name servers.
       to secondary name servers.
       The <command>b10-xfrout</command> is also used to send
       The <command>b10-xfrout</command> is also used to send
@@ -1919,8 +1920,9 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</screen>
 &gt; <userinput>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]</userinput>
 &gt; <userinput>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]</userinput>
 &gt; <userinput>config commit</userinput></screen>
 &gt; <userinput>config commit</userinput></screen>
 
 
-    <para>Both Xfrout and Auth will use the system wide keyring to check
+    <para>Both <command>b10-xfrout</command> and <command>b10-auth</command>
-    TSIGs in the incoming messages and to sign responses.</para>
+      will use the system wide keyring to check
+      TSIGs in the incoming messages and to sign responses.</para>
 
 
     <note><simpara>
     <note><simpara>
         The way to specify zone specific configuration (ACLs, etc) is
         The way to specify zone specific configuration (ACLs, etc) is
@@ -1954,14 +1956,14 @@ what is XfroutClient xfr_client??
       When the <command>b10-auth</command> authoritative DNS server
       When the <command>b10-auth</command> authoritative DNS server
       receives an UPDATE request, it internally forwards the request
       receives an UPDATE request, it internally forwards the request
       to <command>b10-ddns</command>, which handles the rest of
       to <command>b10-ddns</command>, which handles the rest of
-      request processing.
+      this request processing.
-      When the processing is completed <command>b10-ddns</command>
+      When the processing is completed, <command>b10-ddns</command>
-      will send a response to the client with the RCODE set to the
+      will send a response to the client as specified in RFC 2136
-      value as specified in RFC 2136 (NOERROR for successful update,
+      (NOERROR for successful update, REFUSED if rejected due to
-      REFUSED if rejected due to ACL check, etc).
+      ACL check, etc).
       If the zone has been changed as a result, it will internally
       If the zone has been changed as a result, it will internally
       notify <command>b10-xfrout</command> so that other secondary
       notify <command>b10-xfrout</command> so that other secondary
-      servers will be notified via the DNS notify protocol.
+      servers will be notified via the DNS NOTIFY protocol.
       In addition, if <command>b10-auth</command> serves the updated
       In addition, if <command>b10-auth</command> serves the updated
       zone from its in-memory cache (as described in
       zone from its in-memory cache (as described in
       <xref linkend="in-memory-datasource-with-sqlite3-backend" />),
       <xref linkend="in-memory-datasource-with-sqlite3-backend" />),
@@ -1987,10 +1989,10 @@ what is XfroutClient xfr_client??
       As of this writing <command>b10-ddns</command> does not support
       As of this writing <command>b10-ddns</command> does not support
       update forwarding for secondary zones.
       update forwarding for secondary zones.
       If it receives an update request for a secondary zone, it will
       If it receives an update request for a secondary zone, it will
-      immediately return a response with an RCODE of NOTIMP.
+      immediately return a <quote>not implemented</quote> response.
       <note><simpara>
       <note><simpara>
-	  For feature completeness update forwarding should be
+	  For feature completeness, update forwarding should be
-	  eventually supported.  But right now it's considered a lower
+	  eventually supported.  But currently it's considered a lower
 	  priority task and there is no specific plan of implementing
 	  priority task and there is no specific plan of implementing
 	  this feature.
 	  this feature.
 <!-- See Trac #2063 -->
 <!-- See Trac #2063 -->
@@ -2020,9 +2022,9 @@ what is XfroutClient xfr_client??
         underlying data source storing the zone data be writable.
         underlying data source storing the zone data be writable.
         In the current implementation this means the zone must be stored
         In the current implementation this means the zone must be stored
         in an SQLite3-based data source.
         in an SQLite3-based data source.
-        Also, right now, the <command>b10-ddns</command> component
+	Also, in this development version, the <command>b10-ddns</command>
-        configures itself with the data source referring to the
+	component configures itself with the data source referring to the
-        <quote>database_file</quote> configuration parameter of
+        <varname>database_file</varname> configuration parameter of
         <command>b10-auth</command>.
         <command>b10-auth</command>.
         So this information must be configured correctly before starting
         So this information must be configured correctly before starting
         <command>b10-ddns</command>.
         <command>b10-ddns</command>.
@@ -2056,14 +2058,16 @@ what is XfroutClient xfr_client??
 &gt; <userinput>config commit</userinput>
 &gt; <userinput>config commit</userinput>
 </screen>
 </screen>
       <note><simpara>
       <note><simpara>
-	  In theory "kind" could be omitted because "dispensable" is its
+	  In theory <varname>kind</varname> could be omitted because
-	  default.  But there's some peculiar behavior (which should
+	  "dispensable" is its default.
-	  be a bug and should be fixed eventually; see Trac ticket
+	  But there's some peculiar behavior (which should be a
-	  #2064) with bindctl and you'll still need to specify that explicitly.
+	  bug and should be fixed eventually; see Trac ticket #2064)
-	  Likewise, "address" may look unnecessary because
+	  with <command>bindctl</command> and you'll still need to
-	  <command>b10-ddns</command> would start and work without
+	  specify that explicitly.  Likewise, <varname>address</varname>
-	  specifying it.  But for it to shutdown gracefully this
+	  may look unnecessary because <command>b10-ddns</command>
-	  parameter should also be specified.
+	  would start and work without specifying it.  But for it
+	  to shutdown gracefully this parameter should also be
+	  specified.
       </simpara></note>
       </simpara></note>
       </para>
       </para>
     </section>
     </section>
@@ -2071,14 +2075,13 @@ what is XfroutClient xfr_client??
     <section>
     <section>
       <title>Access Control</title>
       <title>Access Control</title>
       <para>
       <para>
-        By default <command>b10-ddns</command> rejects any update
+        By default, <command>b10-ddns</command> rejects any update
-        requests from any clients by returning a response with an RCODE
+        requests from any clients by returning a REFUSED response.
-        of REFUSED.
         To allow updates to take effect, an access control rule
         To allow updates to take effect, an access control rule
         (called update ACL) with a policy allowing updates must explicitly be
         (called update ACL) with a policy allowing updates must explicitly be
         configured.
         configured.
         Update ACL must be configured per zone basis in the
         Update ACL must be configured per zone basis in the
-        <quote>zones</quote> configuration parameter of
+        <varname>zones</varname> configuration parameter of
         <command>b10-ddns</command>.
         <command>b10-ddns</command>.
         This is a list of per-zone configurations regarding DDNS.
         This is a list of per-zone configurations regarding DDNS.
         Each list element consists of the following parameters:
         Each list element consists of the following parameters:
@@ -2113,14 +2116,12 @@ what is XfroutClient xfr_client??
         In general, an update ACL rule that allows an update request
         In general, an update ACL rule that allows an update request
         should be configured with a TSIG key.
         should be configured with a TSIG key.
         This is an example update ACL that allows updates to the zone
         This is an example update ACL that allows updates to the zone
-        named <quote>example.org</quote> of RR class <quote>IN</quote>
+        named <quote>example.org</quote> (of default RR class <quote>IN</quote>)
         from clients that send requests signed with a TSIG whose
         from clients that send requests signed with a TSIG whose
         key name is "key.example.org" (and refuses all others):
         key name is "key.example.org" (and refuses all others):
       <screen>
       <screen>
 &gt; <userinput>config add DDNS/zones</userinput>
 &gt; <userinput>config add DDNS/zones</userinput>
 &gt; <userinput>config set DDNS/zones[0]/origin example.org</userinput>
 &gt; <userinput>config set DDNS/zones[0]/origin example.org</userinput>
-&gt; <userinput>config set DDNS/zones[0]/class IN</userinput>
-(Note: "class" can be omitted)
 &gt; <userinput>config add DDNS/zones[0]/update_acl {"action": "ACCEPT", "key": "key.example.org"}</userinput>
 &gt; <userinput>config add DDNS/zones[0]/update_acl {"action": "ACCEPT", "key": "key.example.org"}</userinput>
 &gt; <userinput>config commit</userinput>
 &gt; <userinput>config commit</userinput>
 </screen>
 </screen>
@@ -2145,11 +2146,13 @@ DDNS/zones[0]/update_acl[1]     {"action": "ACCEPT", "from": "::1", "key": "key.
 &gt; <userinput>config commit</userinput>
 &gt; <userinput>config commit</userinput>
 </screen>
 </screen>
       (Note the "add" in the first line.  Before this sequence, we
       (Note the "add" in the first line.  Before this sequence, we
-      have had only entry in zones[0]/update_acl.  The "add" command
+      have had only entry in <varname>zones[0]/update_acl</varname>.
-      with a value (rule) adds a new entry and sets it to the given rule.
+      The <command>add</command> command with a value (rule) adds
+      a new entry and sets it to the given rule.
+
       Due to a limitation of the current implementation, it doesn't
       Due to a limitation of the current implementation, it doesn't
       work if you first try to just add a new entry and then set it to
       work if you first try to just add a new entry and then set it to
-      a given rule).
+      a given rule.)
       </para>
       </para>
 
 
       <note><simpara>
       <note><simpara>
@@ -2171,6 +2174,7 @@ DDNS/zones[0]/update_acl[1]     {"action": "ACCEPT", "from": "::1", "key": "key.
 	which is rejecting any requests in the case of
 	which is rejecting any requests in the case of
 	<command>b10-ddns</command>.
 	<command>b10-ddns</command>.
       </para>
       </para>
+<!-- TODO: what are the other defaults? -->
 
 
       <para>
       <para>
 	Other actions than "ACCEPT", namely "REJECT" and "DROP", can be
 	Other actions than "ACCEPT", namely "REJECT" and "DROP", can be
@@ -2209,8 +2213,8 @@ DDNS/zones[0]/update_acl[1]     {"action": "ACCEPT", "from": "::1", "key": "key.
       <title>Miscellaneous Operational Issues</title>
       <title>Miscellaneous Operational Issues</title>
       <para>
       <para>
         Unlike BIND 9, BIND 10 currently does not support automatic
         Unlike BIND 9, BIND 10 currently does not support automatic
-        resigning of DNSSEC-signed zone when it's updated via DDNS.
+        re-signing of DNSSEC-signed zone when it's updated via DDNS.
-        It could be possible to resign the updated zone afterwards
+        It could be possible to re-sign the updated zone afterwards
         or make sure the update request also updates related DNSSEC
         or make sure the update request also updates related DNSSEC
         records, but that will be pretty error-prone operation.
         records, but that will be pretty error-prone operation.
         In general, it's not advisable to allow DDNS for a signed zone
         In general, it's not advisable to allow DDNS for a signed zone
@@ -2234,8 +2238,8 @@ DDNS/zones[0]/update_acl[1]     {"action": "ACCEPT", "from": "::1", "key": "key.
         <command>b10-zonemgr</command>.  Zones listed in
         <command>b10-zonemgr</command>.  Zones listed in
         <quote>secondary_zones</quote> will never be updated via DDNS
         <quote>secondary_zones</quote> will never be updated via DDNS
         regardless of the update ACL configuration;
         regardless of the update ACL configuration;
-	<command>b10-ddns</command> will return a response with an
+	<command>b10-ddns</command> will return a NOTAUTH (server
-	RCODE of NOTAUTH as specified in RFC 2136.
+        not authoritative for the zone) response.
         If you have a "conceptual" secondary zone whose content is a
         If you have a "conceptual" secondary zone whose content is a
         copy of some external source but is not updated via the
         copy of some external source but is not updated via the
         standard zone transfers and therefore not listed in
         standard zone transfers and therefore not listed in

+ 80 - 28
src/lib/datasrc/client_list.cc

@@ -16,6 +16,7 @@
 #include "client.h"
 #include "client.h"
 #include "factory.h"
 #include "factory.h"
 #include "memory_datasrc.h"
 #include "memory_datasrc.h"
+#include "logger.h"
 
 
 #include <memory>
 #include <memory>
 #include <boost/foreach.hpp>
 #include <boost/foreach.hpp>
@@ -30,11 +31,19 @@ namespace datasrc {
 
 
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
     DataSourceClient* data_src_client,
     DataSourceClient* data_src_client,
-    const DataSourceClientContainerPtr& container, bool hasCache) :
+    const DataSourceClientContainerPtr& container, bool has_cache) :
     data_src_client_(data_src_client),
     data_src_client_(data_src_client),
     container_(container)
     container_(container)
 {
 {
-    if (hasCache) {
+    if (has_cache) {
+        cache_.reset(new InMemoryClient);
+    }
+}
+
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(bool has_cache) :
+    data_src_client_(NULL)
+{
+    if (has_cache) {
         cache_.reset(new InMemoryClient);
         cache_.reset(new InMemoryClient);
     }
     }
 }
 }
@@ -58,49 +67,92 @@ ConfigurableClientList::configure(const Element& config, bool allow_cache) {
             if (paramConf == ConstElementPtr()) {
             if (paramConf == ConstElementPtr()) {
                 paramConf.reset(new NullElement());
                 paramConf.reset(new NullElement());
             }
             }
-            // TODO: Special-case the master files type.
-            // Ask the factory to create the data source for us
-            const DataSourcePair ds(this->getDataSourceClient(type,
-                                                              paramConf));
             const bool want_cache(allow_cache &&
             const bool want_cache(allow_cache &&
                                   dconf->contains("cache-enable") &&
                                   dconf->contains("cache-enable") &&
                                   dconf->get("cache-enable")->boolValue());
                                   dconf->get("cache-enable")->boolValue());
-            // And put it into the vector
+
-            new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
+            if (type == "MasterFiles") {
-                                                      want_cache));
+                // In case the cache is not allowed, we just skip the master
+                // files (at least for now)
+                if (!allow_cache) {
+                    // We're not going to load these zones. Issue warnings about it.
+                    const map<string, ConstElementPtr>
+                        zones_files(paramConf->mapValue());
+                    for (map<string, ConstElementPtr>::const_iterator
+                         it(zones_files.begin()); it != zones_files.end();
+                         ++it) {
+                        LOG_WARN(logger, DATASRC_LIST_NOT_CACHED).
+                            arg(it->first).arg(rrclass_);
+                    }
+                    continue;
+                }
+                if (!want_cache) {
+                    isc_throw(ConfigurationError, "The cache must be enabled "
+                              "for the MasterFiles type");
+                }
+                new_data_sources.push_back(DataSourceInfo(true));
+            } else {
+                // Ask the factory to create the data source for us
+                const DataSourcePair ds(this->getDataSourceClient(type,
+                                                                  paramConf));
+                // And put it into the vector
+                new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
+                                                          want_cache));
+            }
+
             if (want_cache) {
             if (want_cache) {
-                if (!dconf->contains("cache-zones")) {
+                if (!dconf->contains("cache-zones") && type != "MasterFiles") {
                     isc_throw(isc::NotImplemented, "Auto-detection of zones "
                     isc_throw(isc::NotImplemented, "Auto-detection of zones "
                               "to cache is not yet implemented, supply "
                               "to cache is not yet implemented, supply "
                               "cache-zones parameter");
                               "cache-zones parameter");
                     // TODO: Auto-detect list of all zones in the
                     // TODO: Auto-detect list of all zones in the
                     // data source.
                     // data source.
                 }
                 }
-                const ConstElementPtr zones(dconf->get("cache-zones"));
+
+                // List the zones we are loading
+                vector<string> zones_origins;
+                if (type == "MasterFiles") {
+                    const map<string, ConstElementPtr>
+                        zones_files(paramConf->mapValue());
+                    for (map<string, ConstElementPtr>::const_iterator
+                         it(zones_files.begin()); it != zones_files.end();
+                         ++it) {
+                        zones_origins.push_back(it->first);
+                    }
+                } else {
+                    const ConstElementPtr zones(dconf->get("cache-zones"));
+                    for (size_t i(0); i < zones->size(); ++i) {
+                        zones_origins.push_back(zones->get(i)->stringValue());
+                    }
+                }
+
                 const shared_ptr<InMemoryClient>
                 const shared_ptr<InMemoryClient>
                     cache(new_data_sources.back().cache_);
                     cache(new_data_sources.back().cache_);
                 const DataSourceClient* const
                 const DataSourceClient* const
                     client(new_data_sources.back().data_src_client_);
                     client(new_data_sources.back().data_src_client_);
-                for (size_t i(0); i < zones->size(); ++i) {
+                for (vector<string>::const_iterator it(zones_origins.begin());
-                    const Name origin(zones->get(i)->stringValue());
+                     it != zones_origins.end(); ++it) {
-                    const DataSourceClient::FindResult
+                    const Name origin(*it);
-                        zone(client->findZone(origin));
-                    if (zone.code != result::SUCCESS) {
-                        // The data source does not contain the zone, it can't
-                        // be cached.
-                        isc_throw(ConfigurationError, "Unable to cache "
-                                  "non-existent zone " << origin);
-                    }
                     shared_ptr<InMemoryZoneFinder>
                     shared_ptr<InMemoryZoneFinder>
                         finder(new
                         finder(new
-                            InMemoryZoneFinder(zone.zone_finder->getClass(),
+                            InMemoryZoneFinder(rrclass_, origin));
-                                               origin));
+                    if (type == "MasterFiles") {
-                    ZoneIteratorPtr iterator(client->getIterator(origin));
+                        finder->load(paramConf->get(*it)->stringValue());
-                    if (!iterator) {
+                    } else {
-                        isc_throw(isc::Unexpected, "Got NULL iterator for "
+                        ZoneIteratorPtr iterator;
-                                  "zone " << origin);
+                        try {
+                            iterator = client->getIterator(origin);
+                        }
+                        catch (const DataSourceError&) {
+                            isc_throw(ConfigurationError, "Unable to cache "
+                                      "non-existent zone " << origin);
+                        }
+                        if (!iterator) {
+                            isc_throw(isc::Unexpected, "Got NULL iterator for "
+                                      "zone " << origin);
+                        }
+                        finder->load(*iterator);
                     }
                     }
-                    finder->load(*iterator);
                     cache->addZone(finder);
                     cache->addZone(finder);
                 }
                 }
             }
             }

+ 12 - 8
src/lib/datasrc/client_list.h

@@ -16,6 +16,7 @@
 #define DATASRC_CONTAINER_H
 #define DATASRC_CONTAINER_H
 
 
 #include <dns/name.h>
 #include <dns/name.h>
+#include <dns/rrclass.h>
 #include <cc/data.h>
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
@@ -186,6 +187,12 @@ typedef boost::shared_ptr<const ClientList> ConstClientListPtr;
 /// inherited except for tests.
 /// inherited except for tests.
 class ConfigurableClientList : public ClientList {
 class ConfigurableClientList : public ClientList {
 public:
 public:
+    /// \brief Constructor
+    ///
+    /// \param rrclass For which class the list should work.
+    ConfigurableClientList(const isc::dns::RRClass &rrclass) :
+        rrclass_(rrclass)
+    {}
     /// \brief Exception thrown when there's an error in configuration.
     /// \brief Exception thrown when there's an error in configuration.
     class ConfigurationError : public Exception {
     class ConfigurationError : public Exception {
     public:
     public:
@@ -229,16 +236,11 @@ public:
     ///
     ///
     /// \todo The content yet to be defined.
     /// \todo The content yet to be defined.
     struct DataSourceInfo {
     struct DataSourceInfo {
-        /// \brief Default constructor.
+        // Plays a role of default constructor too (for vector)
-        ///
+        DataSourceInfo(bool has_cache = false);
-        /// Don't use directly. It is here so the structure can live in
-        /// a vector.
-        DataSourceInfo() :
-            data_src_client_(NULL)
-        {}
         DataSourceInfo(DataSourceClient* data_src_client,
         DataSourceInfo(DataSourceClient* data_src_client,
                        const DataSourceClientContainerPtr& container,
                        const DataSourceClientContainerPtr& container,
-                       bool hasCache);
+                       bool has_cache);
         DataSourceClient* data_src_client_;
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
         DataSourceClientContainerPtr container_;
         boost::shared_ptr<InMemoryClient> cache_;
         boost::shared_ptr<InMemoryClient> cache_;
@@ -286,6 +288,8 @@ public:
     /// it might be, so it is just made public (there's no real reason to
     /// it might be, so it is just made public (there's no real reason to
     /// hide it).
     /// hide it).
     const DataSources& getDataSources() const { return (data_sources_); }
     const DataSources& getDataSources() const { return (data_sources_); }
+private:
+    const isc::dns::RRClass rrclass_;
 };
 };
 
 
 } // namespace datasrc
 } // namespace datasrc

+ 7 - 0
src/lib/datasrc/datasrc_messages.mes

@@ -298,6 +298,13 @@ not contain RRs the requested type.  AN NXRRSET indication is returned.
 A debug message indicating that a query for the given name and RR type is being
 A debug message indicating that a query for the given name and RR type is being
 processed.
 processed.
 
 
+% DATASRC_LIST_NOT_CACHED zone %1/%2 not cached, cache disabled globally. Will not be available.
+The process disabled caching of RR data completely. However, the given zone
+is provided as a master file and it can be served from memory cache only.
+Therefore, the zone will not be available for this process. If this is
+a problem, you should move the zone to some database backend (sqlite3, for
+example) and use it from there.
+
 % DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
 % DATASRC_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
 Debug information. An RRset is being added to the in-memory data source.
 Debug information. An RRset is being added to the in-memory data source.
 
 

+ 80 - 3
src/lib/datasrc/tests/client_list_unittest.cc

@@ -45,7 +45,7 @@ public:
         Name getOrigin() const { return (origin_); }
         Name getOrigin() const { return (origin_); }
         // The rest is not to be called, so just have them
         // The rest is not to be called, so just have them
         RRClass getClass() const {
         RRClass getClass() const {
-            return (RRClass::IN());
+            isc_throw(isc::NotImplemented, "Not implemented");
         }
         }
         shared_ptr<Context> find(const Name&, const RRType&,
         shared_ptr<Context> find(const Name&, const RRType&,
                                  const FindOptions)
                                  const FindOptions)
@@ -106,6 +106,8 @@ public:
         type_(type),
         type_(type),
         configuration_(configuration)
         configuration_(configuration)
     {
     {
+        EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
+            "and it never should be created as a data source client";
         if (configuration_->getType() == Element::list) {
         if (configuration_->getType() == Element::list) {
             for (size_t i(0); i < configuration_->size(); ++i) {
             for (size_t i(0); i < configuration_->size(); ++i) {
                 zones.insert(Name(configuration_->get(i)->stringValue()));
                 zones.insert(Name(configuration_->get(i)->stringValue()));
@@ -148,7 +150,12 @@ public:
         } else if (name == Name("null.org")) {
         } else if (name == Name("null.org")) {
             return (ZoneIteratorPtr());
             return (ZoneIteratorPtr());
         } else {
         } else {
-            return (ZoneIteratorPtr(new Iterator(name)));
+            FindResult result(findZone(name));
+            if (result.code == isc::datasrc::result::SUCCESS) {
+                return (ZoneIteratorPtr(new Iterator(name)));
+            } else {
+                isc_throw(DataSourceError, "No such zone");
+            }
         }
         }
     }
     }
     const string type_;
     const string type_;
@@ -162,6 +169,9 @@ private:
 // some methods to dig directly in the internals, for the tests.
 // some methods to dig directly in the internals, for the tests.
 class TestedList : public ConfigurableClientList {
 class TestedList : public ConfigurableClientList {
 public:
 public:
+    TestedList(const RRClass& rrclass) :
+        ConfigurableClientList(rrclass)
+    {}
     DataSources& getDataSources() { return (data_sources_); }
     DataSources& getDataSources() { return (data_sources_); }
     // Overwrite the list's method to get a data source with given type
     // Overwrite the list's method to get a data source with given type
     // and configuration. We mock the data source and don't create the
     // and configuration. We mock the data source and don't create the
@@ -210,7 +220,7 @@ class ListTest : public ::testing::Test {
 public:
 public:
     ListTest() :
     ListTest() :
         // The empty list corresponds to a list with no elements inside
         // The empty list corresponds to a list with no elements inside
-        list_(new TestedList()),
+        list_(new TestedList(RRClass::IN())),
         config_elem_(Element::fromJSON("["
         config_elem_(Element::fromJSON("["
             "{"
             "{"
             "   \"type\": \"test_type\","
             "   \"type\": \"test_type\","
@@ -498,6 +508,47 @@ TEST_F(ListTest, wrongConfig) {
          "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": 13}]",
          "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": 13}]",
         "[{\"type\": \"test_type\", \"params\": 13}, "
         "[{\"type\": \"test_type\", \"params\": 13}, "
          "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": {}}]",
          "{\"type\": \"x\", \"cache-enable\": true, \"cache-zones\": {}}]",
+        // Some bad inputs for MasterFiles special case
+
+        // It must have the cache enabled
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {}}]",
+        // No cache-zones allowed here
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": true,"
+         "\"param\": {}, \"cache-zones\": []}]",
+        // Some bad types of params
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": []}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": 13}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": true}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": null}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": \"x\"}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": 13}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": true}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": null}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": []}}]",
+        "[{\"type\": \"test_type\", \"params\": 13}, "
+         "{\"type\": \"MasterFiles\", \"cache-enable\": false,"
+         "\"params\": {\".\": {}}}]",
         NULL
         NULL
     };
     };
     // Put something inside to see it survives the exception
     // Put something inside to see it survives the exception
@@ -611,6 +662,8 @@ TEST_F(ListTest, cacheZones) {
     EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
     EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.org")).code);
     EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.com")).code);
     EXPECT_EQ(result::SUCCESS, cache->findZone(Name("example.com")).code);
     EXPECT_EQ(result::NOTFOUND, cache->findZone(Name("example.cz")).code);
     EXPECT_EQ(result::NOTFOUND, cache->findZone(Name("example.cz")).code);
+    EXPECT_EQ(RRClass::IN(),
+              cache->findZone(Name("example.org")).zone_finder->getClass());
 
 
     // These are cached and answered from the cache
     // These are cached and answered from the cache
     positiveResult(list_->find(Name("example.com.")), ds_[0],
     positiveResult(list_->find(Name("example.com.")), ds_[0],
@@ -670,4 +723,28 @@ TEST_F(ListTest, badCache) {
     checkDS(0, "test_type", "{}", false);
     checkDS(0, "test_type", "{}", false);
 }
 }
 
 
+TEST_F(ListTest, masterFiles) {
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   }"
+        "}]"));
+    list_->configure(*elem, true);
+
+    // It has only the cache
+    EXPECT_EQ(static_cast<const DataSourceClient*>(NULL),
+              list_->getDataSources()[0].data_src_client_);
+
+    // And it can search
+    positiveResult(list_->find(Name(".")), ds_[0], Name("."), true, "com",
+                   true);
+
+    // If cache is not enabled, nothing is loaded
+    list_->configure(*elem, false);
+    EXPECT_EQ(0, list_->getDataSources().size());
+}
+
 }
 }

+ 126 - 14
src/lib/dhcp/iface_mgr.cc

@@ -19,11 +19,14 @@
 #include <netinet/in.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <sys/select.h>
 #include <sys/select.h>
+#include <asio.hpp>
 
 
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/io_error.h>
 #include <util/io/pktinfo_utilities.h>
 #include <util/io/pktinfo_utilities.h>
 
 
 using namespace std;
 using namespace std;
@@ -197,7 +200,7 @@ void IfaceMgr::stubDetectIfaces() {
         iface.flag_up_ = true;
         iface.flag_up_ = true;
         iface.flag_running_ = true;
         iface.flag_running_ = true;
 
 
-        // note that we claim that this is not a loopback. iface_mgr tries to open a
+        // Note that we claim that this is not a loopback. iface_mgr tries to open a
         // socket on all interaces that are up, running and not loopback. As this is
         // socket on all interaces that are up, running and not loopback. As this is
         // the only interface we were able to detect, let's pretend this is a normal
         // the only interface we were able to detect, let's pretend this is a normal
         // interface.
         // interface.
@@ -228,8 +231,8 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
     int sock;
     int sock;
     int count = 0;
     int count = 0;
 
 
-    for (IfaceCollection::iterator iface=ifaces_.begin();
+    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface!=ifaces_.end();
+         iface != ifaces_.end();
          ++iface) {
          ++iface) {
 
 
         cout << "Trying opening socket on interface " << iface->getFullName() << endl;
         cout << "Trying opening socket on interface " << iface->getFullName() << endl;
@@ -243,18 +246,17 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
         }
         }
 
 
         AddressCollection addrs = iface->getAddresses();
         AddressCollection addrs = iface->getAddresses();
-
+        for (AddressCollection::iterator addr = addrs.begin();
-        for (AddressCollection::iterator addr= addrs.begin();
              addr != addrs.end();
              addr != addrs.end();
              ++addr) {
              ++addr) {
 
 
-            // skip IPv6 addresses
+            // Skip IPv6 addresses
             if (addr->getFamily() != AF_INET) {
             if (addr->getFamily() != AF_INET) {
                 continue;
                 continue;
             }
             }
 
 
             sock = openSocket(iface->getName(), *addr, port);
             sock = openSocket(iface->getName(), *addr, port);
-            if (sock<0) {
+            if (sock < 0) {
                 cout << "Failed to open unicast socket." << endl;
                 cout << "Failed to open unicast socket." << endl;
                 return (false);
                 return (false);
             }
             }
@@ -270,8 +272,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
     int sock;
     int sock;
     int count = 0;
     int count = 0;
 
 
-    for (IfaceCollection::iterator iface=ifaces_.begin();
+    for (IfaceCollection::iterator iface = ifaces_.begin();
-         iface!=ifaces_.end();
+         iface != ifaces_.end();
          ++iface) {
          ++iface) {
 
 
         if (iface->flag_loopback_ ||
         if (iface->flag_loopback_ ||
@@ -281,8 +283,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
         }
         }
 
 
         AddressCollection addrs = iface->getAddresses();
         AddressCollection addrs = iface->getAddresses();
-
+        for (AddressCollection::iterator addr = addrs.begin();
-        for (AddressCollection::iterator addr= addrs.begin();
              addr != addrs.end();
              addr != addrs.end();
              ++addr) {
              ++addr) {
 
 
@@ -292,7 +293,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             }
             }
 
 
             sock = openSocket(iface->getName(), *addr, port);
             sock = openSocket(iface->getName(), *addr, port);
-            if (sock<0) {
+            if (sock < 0) {
                 cout << "Failed to open unicast socket." << endl;
                 cout << "Failed to open unicast socket." << endl;
                 return (false);
                 return (false);
             }
             }
@@ -301,7 +302,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             // works well on Mac OS (and possibly other BSDs), but does not work
             // works well on Mac OS (and possibly other BSDs), but does not work
             // on Linux.
             // on Linux.
             if ( !joinMulticast(sock, iface->getName(),
             if ( !joinMulticast(sock, iface->getName(),
-                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
+                                string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
                 close(sock);
                 close(sock);
                 isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                 isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << " multicast group.");
                           << " multicast group.");
@@ -317,7 +318,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             int sock2 = openSocket(iface->getName(),
             int sock2 = openSocket(iface->getName(),
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    port);
                                    port);
-            if (sock2<0) {
+            if (sock2 < 0) {
                 isc_throw(Unexpected, "Failed to open multicast socket on "
                 isc_throw(Unexpected, "Failed to open multicast socket on "
                           << " interface " << iface->getFullName());
                           << " interface " << iface->getFullName());
                 iface->delSocket(sock); // delete previously opened socket
                 iface->delSocket(sock); // delete previously opened socket
@@ -397,6 +398,117 @@ int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
     }
     }
 }
 }
 
 
+int IfaceMgr::openSocketFromIface(const std::string& ifname,
+                                  const uint16_t port,
+                                  const uint8_t family) {
+    // Search for specified interface among detected interfaces.
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end();
+         ++iface) {
+
+        if ((iface->getFullName() != ifname) &&
+            (iface->getName() != ifname)) {
+            continue;
+        }
+
+        // Interface is now detected. Search for address on interface
+        // that matches address family (v6 or v4).
+        AddressCollection addrs = iface->getAddresses();
+        AddressCollection::iterator addr_it = addrs.begin();
+        while (addr_it != addrs.end()) {
+            if (addr_it->getFamily() == family) {
+                // We have interface and address so let's open socket.
+                // This may cause isc::Unexpected exception.
+                return (openSocket(iface->getName(), *addr_it, port));
+            }
+            ++addr_it;
+        }
+        // If we are at the end of address collection it means that we found
+        // interface but there is no address for family specified.
+        if (addr_it == addrs.end()) {
+            // Stringify the family value to append it to exception string.
+            std::string family_name("AF_INET");
+            if (family == AF_INET6) {
+                family_name = "AF_INET6";
+            }
+            // We did not find address on the interface.
+            isc_throw(BadValue, "There is no address for interface: "
+                      << ifname << ", port: " << port << ", address "
+                      " family: " << family_name);
+        }
+    }
+    // If we got here it means that we had not found the specified interface.
+    // Otherwise we would have returned from previous exist points.
+    isc_throw(BadValue, "There is no " << ifname << " interface present.");
+}
+
+int IfaceMgr::openSocketFromAddress(const IOAddress& addr,
+                                    const uint16_t port) {
+    // Search through detected interfaces and addresses to match
+    // local address we got.
+    for (IfaceCollection::iterator iface = ifaces_.begin();
+         iface != ifaces_.end();
+         ++iface) {
+
+        AddressCollection addrs = iface->getAddresses();
+
+        for (AddressCollection::iterator addr_it = addrs.begin();
+             addr_it != addrs.end();
+             ++addr_it) {
+
+            // Local address must match one of the addresses
+            // on detected interfaces. If it does, we have
+            // address and interface detected so we can open
+            // socket.
+            if (*addr_it == addr) {
+                // Open socket using local interface, address and port.
+                // This may cause isc::Unexpected exception.
+                return (openSocket(iface->getName(), *addr_it, port));
+            }
+        }
+    }
+    // If we got here it means that we did not find specified address
+    // on any available interface.
+    isc_throw(BadValue, "There is no such address " << addr.toText());
+}
+
+int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr,
+                                          const uint16_t port) {
+    // Get local address to be used to connect to remote location.
+    IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
+    return openSocketFromAddress(local_address, port);
+}
+
+isc::asiolink::IOAddress
+IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
+    // Create remote endpoint, we will be connecting to it.
+    boost::scoped_ptr<const UDPEndpoint>
+        remote_endpoint(static_cast<const UDPEndpoint*>
+                        (UDPEndpoint::create(IPPROTO_UDP, remote_addr, port)));
+    if (!remote_endpoint) {
+        isc_throw(Unexpected, "Unable to create remote endpoint");
+    }
+
+    // Create socket that will be used to connect to remote endpoint.
+    asio::io_service io_service;
+    asio::ip::udp::socket sock(io_service);
+
+    // Try to connect to remote endpoint and check if attempt is successful.
+    asio::error_code err_code;
+    sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
+    if (err_code) {
+        isc_throw(Unexpected,"Failed to connect to remote endpoint.");
+    }
+
+    // Once we are connected socket object holds local endpoint.
+    asio::ip::udp::socket::endpoint_type local_endpoint =
+        sock.local_endpoint();
+    asio::ip::address local_address(local_endpoint.address());
+
+    // Return address of local endpoint.
+    return IOAddress(local_address);
+}
+
 int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 
 
     cout << "Creating UDP4 socket on " << iface.getFullName()
     cout << "Creating UDP4 socket on " << iface.getFullName()

+ 69 - 1
src/lib/dhcp/iface_mgr.h

@@ -374,7 +374,58 @@ public:
     /// @return socket descriptor, if socket creation, binding and multicast
     /// @return socket descriptor, if socket creation, binding and multicast
     /// group join were all successful.
     /// group join were all successful.
     int openSocket(const std::string& ifname,
     int openSocket(const std::string& ifname,
-                   const isc::asiolink::IOAddress& addr, const uint16_t port);
+                   const isc::asiolink::IOAddress& addr,
+                   const uint16_t port);
+
+    /// @brief Opens UDP/IP socket and binds it to interface specified.
+    ///
+    /// This method differs from \ref openSocket in that it does not require
+    /// the specification of a local address to which socket will be bound.
+    /// Instead, the method searches through the addresses on the specified
+    /// interface and selects one that matches the address family.
+    ///
+    /// @param ifname name of the interface
+    /// @param port UDP port
+    /// @param family address family (AF_INET or AF_INET6)
+    /// @return socket descriptor, if socket creation, binding and multicast
+    /// group join were all successful.
+    /// @throw isc::Unexpected if failed to create and bind socket.
+    /// @throw isc::BadValue if there is no address on specified interface
+    /// that belongs to given family.
+    int openSocketFromIface(const std::string& ifname,
+                            const uint16_t port,
+                            const uint8_t family);
+
+    /// @brief Opens UDP/IP socket and binds to address specified
+    ///
+    /// This methods differs from \ref openSocket in that it does not require
+    /// the specification of the interface to which the socket will be bound.
+    ///
+    /// @param addr address to be bound
+    /// @param port UDP port
+    /// @return socket descriptor, if socket creation, binding and multicast
+    /// group join were all successful.
+    /// @throw isc::Unexpected if failed to create and bind socket
+    /// @throw isc::BadValue if specified address is not available on
+    /// any interface
+    int openSocketFromAddress(const isc::asiolink::IOAddress& addr,
+                              const uint16_t port);
+
+    /// @brief Opens UDP/IP socket to be used to connect to remote address
+    ///
+    /// This method identifies the local address to be used to connect to the
+    /// remote address specified as argument.  Once the local address is
+    /// identified, \ref openSocket is called to open a socket and bind it to
+    /// the interface, address and port.
+    ///
+    /// @param remote_addr remote address to connect to
+    /// @param port UDP port
+    /// @return socket descriptor, if socket creation, binding and multicast
+    /// group join were all successful.
+    /// @throw isc::Unexpected if failed to create and bind socket
+    int openSocketFromRemoteAddress(const isc::asiolink::IOAddress& remote_addr,
+                                    const uint16_t port);
+
 
 
     /// Opens IPv6 sockets on detected interfaces.
     /// Opens IPv6 sockets on detected interfaces.
     ///
     ///
@@ -548,6 +599,23 @@ private:
     joinMulticast(int sock, const std::string& ifname,
     joinMulticast(int sock, const std::string& ifname,
                   const std::string& mcast);
                   const std::string& mcast);
 
 
+    /// @brief Identifies local network address to be used to
+    /// connect to remote address.
+    ///
+    /// This method identifies local network address that can be used
+    /// to connect to remote address specified.
+    /// It first creates socket and makes attempt to connect
+    /// to remote location via this socket. If connection
+    /// is established successfully, the local address to which
+    /// socket is bound is returned.
+    ///
+    /// @param remote_addr remote address to connect to
+    /// @param port port to be used
+    /// @return local address to be used to connect to remote address
+    /// @throw isc::Unexpected if unable to indentify local address
+    isc::asiolink::IOAddress
+    getLocalAddress(const isc::asiolink::IOAddress& remote_addr,
+                    const uint16_t port);
 };
 };
 
 
 }; // namespace isc::dhcp
 }; // namespace isc::dhcp

+ 85 - 7
src/lib/dhcp/tests/iface_mgr_unittest.cc

@@ -19,6 +19,7 @@
 
 
 #include <unistd.h>
 #include <unistd.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
+#include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
@@ -31,12 +32,17 @@ using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 
 
-// name of loopback interface detection
-const size_t buf_size = 32;
-char LOOPBACK[buf_size] = "lo";
-
 namespace {
 namespace {
 
 
+// Name of loopback interface detection
+const size_t BUF_SIZE = 32;
+char LOOPBACK[BUF_SIZE] = "lo";
+
+// Ports used during testing
+const uint16_t PORT1 = 10547;   // V6 socket
+const uint16_t PORT2 = 10548;   // V4 socket
+
+
 class NakedIfaceMgr: public IfaceMgr {
 class NakedIfaceMgr: public IfaceMgr {
     // "naked" Interface Manager, exposes internal fields
     // "naked" Interface Manager, exposes internal fields
 public:
 public:
@@ -69,10 +75,10 @@ TEST_F(IfaceMgrTest, loDetect) {
     // is implemented
     // is implemented
     if (if_nametoindex("lo") > 0) {
     if (if_nametoindex("lo") > 0) {
         cout << "This is Linux, using lo as loopback." << endl;
         cout << "This is Linux, using lo as loopback." << endl;
-        snprintf(LOOPBACK, buf_size - 1, "lo");
+        snprintf(LOOPBACK, BUF_SIZE - 1, "lo");
     } else if (if_nametoindex("lo0") > 0) {
     } else if (if_nametoindex("lo0") > 0) {
         cout << "This is BSD, using lo0 as loopback." << endl;
         cout << "This is BSD, using lo0 as loopback." << endl;
-        snprintf(LOOPBACK, buf_size - 1, "lo0");
+        snprintf(LOOPBACK, BUF_SIZE - 1, "lo0");
     } else {
     } else {
         cout << "Failed to detect loopback interface. Neither "
         cout << "Failed to detect loopback interface. Neither "
              << "lo nor lo0 worked. I give up." << endl;
              << "lo nor lo0 worked. I give up." << endl;
@@ -80,7 +86,7 @@ TEST_F(IfaceMgrTest, loDetect) {
     }
     }
 }
 }
 
 
-// uncomment this test to create packet writer. It will
+// Uncomment this test to create packet writer. It will
 // write incoming DHCPv6 packets as C arrays. That is useful
 // write incoming DHCPv6 packets as C arrays. That is useful
 // for generating test sequences based on actual traffic
 // for generating test sequences based on actual traffic
 //
 //
@@ -241,6 +247,78 @@ TEST_F(IfaceMgrTest, sockets6) {
     delete ifacemgr;
     delete ifacemgr;
 }
 }
 
 
+TEST_F(IfaceMgrTest, socketsFromIface) {
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Open v6 socket on loopback interface and bind to port
+    int socket1 = 0;
+    EXPECT_NO_THROW(
+        socket1 = ifacemgr->openSocketFromIface(LOOPBACK, PORT1, AF_INET6);
+    );
+    // Socket descriptor must be positive integer
+    EXPECT_GT(socket1, 0);
+    close(socket1);
+
+    // Open v4 socket on loopback interface and bind to different port
+    int socket2 = 0;
+    EXPECT_NO_THROW(
+        socket2 = ifacemgr->openSocketFromIface(LOOPBACK, PORT2, AF_INET);
+    );
+    // socket descriptor must be positive integer
+    EXPECT_GT(socket2, 0);
+    close(socket2);
+
+}
+
+
+TEST_F(IfaceMgrTest, socketsFromAddress) {
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Open v6 socket on loopback interface and bind to port
+    int socket1 = 0;
+    IOAddress loAddr6("::1");
+    EXPECT_NO_THROW(
+        socket1 = ifacemgr->openSocketFromAddress(loAddr6, PORT1);
+    );
+    // socket descriptor must be positive integer
+    EXPECT_GT(socket1, 0);
+    close(socket1);
+
+    // Open v4 socket on loopback interface and bind to different port
+    int socket2 = 0;
+    IOAddress loAddr("127.0.0.1");
+    EXPECT_NO_THROW(
+        socket2 = ifacemgr->openSocketFromAddress(loAddr, PORT2);
+    );
+    // socket descriptor must be positive integer
+    EXPECT_GT(socket2, 0);
+    close(socket2);
+}
+
+TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
+
+    // Open v6 socket to connect to remote address.
+    // Loopback address is the only one that we know
+    // so let's treat it as remote address.
+    int socket1 = 0;
+    IOAddress loAddr6("::1");
+    EXPECT_NO_THROW(
+        socket1 = ifacemgr->openSocketFromRemoteAddress(loAddr6, PORT1);
+    );
+    EXPECT_GT(socket1, 0);
+    close(socket1);
+
+    // Open v4 socket to connect to remote address.
+    int socket2 = 0;
+    IOAddress loAddr("127.0.0.1");
+    EXPECT_NO_THROW(
+        socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2);
+    );
+    EXPECT_GT(socket2, 0);
+    close(socket2);
+}
+
 // TODO: disabled due to other naming on various systems
 // TODO: disabled due to other naming on various systems
 // (lo in Linux, lo0 in BSD systems)
 // (lo in Linux, lo0 in BSD systems)
 TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {
 TEST_F(IfaceMgrTest, DISABLED_sockets6Mcast) {

+ 122 - 14
src/lib/dns/labelsequence.cc

@@ -23,10 +23,47 @@
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
 
 
+LabelSequence::LabelSequence(const uint8_t* data,
+                             const uint8_t* offsets,
+                             size_t offsets_size) : data_(data),
+                                                    offsets_(offsets),
+                                                    offsets_size_(offsets_size),
+                                                    first_label_(0),
+                                                    last_label_(offsets_size_)
+{
+    if (data == NULL || offsets == NULL) {
+        isc_throw(BadValue, "Null pointer passed to LabelSequence constructor");
+    }
+    if (offsets_size == 0) {
+        isc_throw(BadValue, "Zero offsets to LabelSequence constructor");
+    }
+    if (offsets_size > Name::MAX_LABELS) {
+        isc_throw(BadValue, "MAX_LABELS exceeded");
+    }
+    for (size_t cur_offset = 0; cur_offset < offsets_size; ++cur_offset) {
+        if (offsets[cur_offset] > Name::MAX_LABELLEN) {
+            isc_throw(BadValue, "MAX_LABEL_LEN exceeded");
+        }
+        if (cur_offset > 0 && offsets[cur_offset] <= offsets[cur_offset - 1]) {
+            isc_throw(BadValue, "Offset smaller than previous offset");
+        }
+    }
+}
+
+
 const uint8_t*
 const uint8_t*
 LabelSequence::getData(size_t *len) const {
 LabelSequence::getData(size_t *len) const {
     *len = getDataLength();
     *len = getDataLength();
-    return (&name_.ndata_[name_.offsets_[first_label_]]);
+    return (&data_[offsets_[first_label_]]);
+}
+
+void
+LabelSequence::getOffsetData(size_t* len,
+                             uint8_t placeholder[Name::MAX_LABELS]) const {
+  *len = last_label_ - first_label_;
+  for (size_t i = 0; i < *len; i++) {
+      placeholder[i] = offsets_[first_label_ + i] - offsets_[first_label_];
+  }
 }
 }
 
 
 size_t
 size_t
@@ -37,10 +74,10 @@ LabelSequence::getDataLength() const {
     // the length for the 'previous' label (the root label) plus
     // the length for the 'previous' label (the root label) plus
     // one (for the root label zero octet)
     // one (for the root label zero octet)
     if (isAbsolute()) {
     if (isAbsolute()) {
-        return (name_.offsets_[last_label_ - 1] -
+        return (offsets_[last_label_ - 1] -
-                name_.offsets_[first_label_] + 1);
+                offsets_[first_label_] + 1);
     } else {
     } else {
-        return (name_.offsets_[last_label_] - name_.offsets_[first_label_]);
+        return (offsets_[last_label_] - offsets_[first_label_]);
     }
     }
 }
 }
 
 
@@ -78,12 +115,83 @@ LabelSequence::compare(const LabelSequence& other,
         return (NameComparisonResult(0, 0, NameComparisonResult::NONE));
         return (NameComparisonResult(0, 0, NameComparisonResult::NONE));
     }
     }
 
 
-    return (name_.compare(other.name_,
+    // Determine the relative ordering under the DNSSEC order relation of
-                          first_label_,
+    // 'this' and 'other', and also determine the hierarchical relationship
-                          other.first_label_,
+    // of the names.
-                          last_label_,
+
-                          other.last_label_,
+    unsigned int nlabels = 0;
-                          case_sensitive));
+    int l1 = last_label_ - first_label_;
+    int l2 = other.last_label_ - other.first_label_;
+    int ldiff = (int)l1 - (int)l2;
+    unsigned int l = (ldiff < 0) ? l1 : l2;
+
+    while (l > 0) {
+        --l;
+        --l1;
+        --l2;
+        size_t pos1 = offsets_[l1 + first_label_];
+        size_t pos2 = other.offsets_[l2 + other.first_label_];
+        unsigned int count1 = data_[pos1++];
+        unsigned int count2 = other.data_[pos2++];
+
+        // We don't support any extended label types including now-obsolete
+        // bitstring labels.
+        assert(count1 <= Name::MAX_LABELLEN && count2 <= Name::MAX_LABELLEN);
+
+        int cdiff = (int)count1 - (int)count2;
+        unsigned int count = (cdiff < 0) ? count1 : count2;
+
+        while (count > 0) {
+            uint8_t label1 = data_[pos1];
+            uint8_t label2 = other.data_[pos2];
+            int chdiff;
+
+            if (case_sensitive) {
+                chdiff = (int)label1 - (int)label2;
+            } else {
+                chdiff = (int)isc::dns::name::internal::maptolower[label1] -
+                         (int)isc::dns::name::internal::maptolower[label2];
+            }
+
+            if (chdiff != 0) {
+                if ((nlabels == 0) &&
+                     (!isAbsolute() ||
+                     ((last_label_ < getLabelCount()) ||
+                      (other.last_label_ < other.getLabelCount())))) {
+                    return (NameComparisonResult(0, 0,
+                                                 NameComparisonResult::NONE));
+                } else {
+                    return (NameComparisonResult(chdiff, nlabels,
+                                                 NameComparisonResult::COMMONANCESTOR));
+                }
+            }
+            --count;
+            ++pos1;
+            ++pos2;
+        }
+        if (cdiff != 0) {
+            if ((nlabels == 0) &&
+                ((last_label_ < getLabelCount()) ||
+                 (other.last_label_ < other.getLabelCount()))) {
+                return (NameComparisonResult(0, 0,
+                                             NameComparisonResult::NONE));
+            } else {
+                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));
 }
 }
 
 
 void
 void
@@ -106,7 +214,7 @@ LabelSequence::stripRight(size_t i) {
 
 
 bool
 bool
 LabelSequence::isAbsolute() const {
 LabelSequence::isAbsolute() const {
-    return (last_label_ == name_.offsets_.size());
+    return (last_label_ == offsets_size_);
 }
 }
 
 
 size_t
 size_t
@@ -129,9 +237,9 @@ LabelSequence::getHash(bool case_sensitive) const {
 
 
 std::string
 std::string
 LabelSequence::toText(bool omit_final_dot) const {
 LabelSequence::toText(bool omit_final_dot) const {
-    Name::NameString::const_iterator np = name_.ndata_.begin() +
+    const uint8_t* np = &data_[offsets_[first_label_]];
-        name_.offsets_[first_label_];
+    const uint8_t* np_end = np + getDataLength();
-    const Name::NameString::const_iterator np_end = np + getDataLength();
+
     // use for integrity check
     // use for integrity check
     unsigned int labels = last_label_ - first_label_;
     unsigned int labels = last_label_ - first_label_;
     // init with an impossible value to catch error cases in the end:
     // init with an impossible value to catch error cases in the end:

+ 67 - 14
src/lib/dns/labelsequence.h

@@ -21,30 +21,48 @@
 namespace isc {
 namespace isc {
 namespace dns {
 namespace dns {
 
 
-/// \brief Light-weight Accessor to Name object
+/// \brief Light-weight Accessor to data of Name object
 ///
 ///
 /// The purpose of this class is to easily match Names and parts of Names,
 /// 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.
 /// without needing to copy the underlying data on each label strip.
 ///
 ///
-/// It can only work on existing Name objects, and the Name object MUST
+/// It can only work on existing Name objects, or data as provided by the
+/// Name object or another LabelSequence, and the data or Name MUST
 /// remain in scope during the entire lifetime of its associated
 /// remain in scope during the entire lifetime of its associated
 /// LabelSequence(s).
 /// LabelSequence(s).
 ///
 ///
 /// Upon creation of a LabelSequence, it records the offsets of the
 /// Upon creation of a LabelSequence, it records the offsets of the
 /// labels in the wireformat data of the Name. When stripLeft() or
 /// labels in the wireformat data of the Name. When stripLeft() or
 /// stripRight() is called on the LabelSequence, no changes in the
 /// stripRight() is called on the LabelSequence, no changes in the
-/// Name's data occur, but the internal pointers of the
+/// original data occur, but the internal pointers of the
 /// LabelSequence are modified.
 /// LabelSequence are modified.
 ///
 ///
 /// LabelSequences can be compared to other LabelSequences, and their
 /// LabelSequences can be compared to other LabelSequences, and their
 /// data can be requested (which then points to part of the original
 /// data can be requested (which then points to part of the original
-/// data of the associated Name object).
+/// data of the original Name object).
 ///
 ///
 class LabelSequence {
 class LabelSequence {
     // Name calls the private toText(bool) method of LabelSequence.
     // Name calls the private toText(bool) method of LabelSequence.
     friend std::string Name::toText(bool) const;
     friend std::string Name::toText(bool) const;
 
 
 public:
 public:
+    /// \brief Constructs a LabelSequence for the given label sequence
+    ///
+    /// \note The associated data MUST remain in scope during the lifetime
+    /// of this LabelSequence, since only the pointers are copied.
+    ///
+    /// \note No validation is done on the given data upon construction;
+    ///       use with care.
+    ///
+    /// \param ls The LabelSequence to construct a LabelSequence from
+    explicit LabelSequence(const LabelSequence& ls):
+                                     data_(ls.data_),
+                                     offsets_(ls.offsets_),
+                                     offsets_size_(ls.offsets_size_),
+                                     first_label_(ls.first_label_),
+                                     last_label_(ls.last_label_)
+    {}
+
     /// \brief Constructs a LabelSequence for the given name
     /// \brief Constructs a LabelSequence for the given name
     ///
     ///
     /// \note The associated Name MUST remain in scope during the lifetime
     /// \note The associated Name MUST remain in scope during the lifetime
@@ -53,25 +71,58 @@ public:
     /// to the labels in the Name object).
     /// to the labels in the Name object).
     ///
     ///
     /// \param name The Name to construct a LabelSequence for
     /// \param name The Name to construct a LabelSequence for
-    LabelSequence(const Name& name): name_(name),
+    explicit LabelSequence(const Name& name):
+                                     data_(&name.ndata_[0]),
+                                     offsets_(&name.offsets_[0]),
+                                     offsets_size_(name.offsets_.size()),
                                      first_label_(0),
                                      first_label_(0),
                                      last_label_(name.getLabelCount())
                                      last_label_(name.getLabelCount())
     {}
     {}
 
 
+    /// \brief Constructs a LabelSequence for the given data
+    ///
+    /// \note The associated data MUST remain in scope during the lifetime
+    /// of this LabelSequence, since only the pointers are copied.
+    ///
+    /// \note No validation is done on the given data upon construction;
+    ///       use with care.
+    ///
+    /// \exception isc::BadValue if basic checks for the input data, or
+    ///            offsets fails.
+    ///
+    /// \param data The raw data for the domain name, in wire format
+    /// \param offsets The offsets of the labels in the domain name data,
+    ///        as given by a Name object or another LabelSequence
+    /// \param offsets_size The size of the offsets data
+    LabelSequence(const uint8_t* data,
+                  const uint8_t* offsets,
+                  size_t offsets_size);
+
     /// \brief Return the wire-format data for this LabelSequence
     /// \brief Return the wire-format data for this LabelSequence
     ///
     ///
-    /// The data, is returned as a pointer to the original wireformat
+    /// The data is returned as a pointer to (the part of) the original
-    /// data of the original Name object, and the given len value is
+    /// wireformat data, from either the original Name object, or the
+    /// raw data given in the constructor, and the given len value is
     /// set to the number of octets that match this labelsequence.
     /// set to the number of octets that match this labelsequence.
     ///
     ///
     /// \note The data pointed to is only valid if the original Name
     /// \note The data pointed to is only valid if the original Name
-    /// object is still in scope
+    /// object or data is still in scope
     ///
     ///
     /// \param len Pointer to a size_t where the length of the data
     /// \param len Pointer to a size_t where the length of the data
     ///        will be stored (in number of octets)
     ///        will be stored (in number of octets)
     /// \return Pointer to the wire-format data of this label sequence
     /// \return Pointer to the wire-format data of this label sequence
     const uint8_t* getData(size_t* len) const;
     const uint8_t* getData(size_t* len) const;
 
 
+    /// \brief Return the offset data for this LabelSequence
+    ///
+    /// The offsets are returned in the <code>placeholder</code> array.
+    ///
+    /// \param len Pointer to a size_t where the number of offsets
+    ///        will be stored
+    /// \param placeholder Array where the offset data will be returned
+    void getOffsetData(size_t* len,
+                       uint8_t placeholder[Name::MAX_LABELS]) const;
+
     /// \brief Return the length of the wire-format data of this LabelSequence
     /// \brief Return the length of the wire-format data of this LabelSequence
     ///
     ///
     /// This method returns the number of octets for the data that would
     /// This method returns the number of octets for the data that would
@@ -83,7 +134,7 @@ public:
     /// versa.
     /// versa.
     ///
     ///
     /// \note The data pointed to is only valid if the original Name
     /// \note The data pointed to is only valid if the original Name
-    /// object is still in scope
+    /// object or data is still in scope
     ///
     ///
     /// \return The length of the data of the label sequence in octets.
     /// \return The length of the data of the label sequence in octets.
     size_t getDataLength() const;
     size_t getDataLength() const;
@@ -125,7 +176,7 @@ public:
     /// \brief Remove labels from the end of this LabelSequence
     /// \brief Remove labels from the end of this LabelSequence
     ///
     ///
     /// \note No actual memory is changed, this operation merely updates the
     /// \note No actual memory is changed, this operation merely updates the
-    /// internal pointers based on the offsets in the Name object.
+    /// internal pointers based on the offsets originally provided.
     ///
     ///
     /// \exception OutOfRange if i is greater than or equal to the number
     /// \exception OutOfRange if i is greater than or equal to the number
     ///           of labels currently pointed to by this LabelSequence
     ///           of labels currently pointed to by this LabelSequence
@@ -144,13 +195,13 @@ public:
     /// LabelSequence as a string.  The returned string ends with a dot
     /// LabelSequence as a string.  The returned string ends with a dot
     /// '.' if the label sequence is absolute.
     /// '.' if the label sequence is absolute.
     ///
     ///
-    /// This function assumes the underlying name is in proper
+    /// This function assumes the underlying data is in proper
     /// uncompressed wire format.  If it finds an unexpected label
     /// uncompressed wire format.  If it finds an unexpected label
     /// character including compression pointer, an exception of class
     /// character including compression pointer, an exception of class
     /// \c BadLabelType will be thrown.  In addition, if resource
     /// \c BadLabelType will be thrown.  In addition, if resource
     /// allocation for the result string fails, a corresponding standard
     /// allocation for the result string fails, a corresponding standard
     /// exception will be thrown.
     /// exception will be thrown.
-    //
+    ///
     /// \return a string representation of the <code>LabelSequence</code>.
     /// \return a string representation of the <code>LabelSequence</code>.
     std::string toText() const;
     std::string toText() const;
 
 
@@ -201,7 +252,9 @@ public:
     bool isAbsolute() const;
     bool isAbsolute() const;
 
 
 private:
 private:
-    const Name& name_;
+    const uint8_t* data_;
+    const uint8_t* offsets_;
+    size_t offsets_size_;
     size_t first_label_;
     size_t first_label_;
     size_t last_label_;
     size_t last_label_;
 };
 };
@@ -218,7 +271,7 @@ private:
 ///
 ///
 /// \param os A \c std::ostream object on which the insertion operation is
 /// \param os A \c std::ostream object on which the insertion operation is
 /// performed.
 /// performed.
-/// \param name The \c LabelSequence object output by the operation.
+/// \param label_sequence The \c LabelSequence object output by the operation.
 /// \return A reference to the same \c std::ostream object referenced by
 /// \return A reference to the same \c std::ostream object referenced by
 /// parameter \c os after the insertion operation.
 /// parameter \c os after the insertion operation.
 std::ostream&
 std::ostream&

+ 13 - 5
src/lib/dns/messagerenderer.cc

@@ -289,8 +289,8 @@ MessageRenderer::setCompressMode(const CompressMode mode) {
 }
 }
 
 
 void
 void
-MessageRenderer::writeName(const Name& name, const bool compress) {
+MessageRenderer::writeName(const LabelSequence& ls, const bool compress) {
-    LabelSequence sequence(name);
+    LabelSequence sequence(ls);
     const size_t nlabels = sequence.getLabelCount();
     const size_t nlabels = sequence.getLabelCount();
     size_t data_len;
     size_t data_len;
     const uint8_t* data;
     const uint8_t* data;
@@ -302,6 +302,10 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
     const bool case_sensitive = (impl_->compress_mode_ ==
     const bool case_sensitive = (impl_->compress_mode_ ==
                                  MessageRenderer::CASE_SENSITIVE);
                                  MessageRenderer::CASE_SENSITIVE);
     for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
     for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
+        if (nlabels_uncomp > 0) {
+            sequence.stripLeft(1);
+        }
+
         data = sequence.getData(&data_len);
         data = sequence.getData(&data_len);
         if (data_len == 1) { // trailing dot.
         if (data_len == 1) { // trailing dot.
             ++nlabels_uncomp;
             ++nlabels_uncomp;
@@ -317,14 +321,13 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
         if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
         if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
             break;
             break;
         }
         }
-        sequence.stripLeft(1);
     }
     }
 
 
     // Record the current offset before updating the offset table
     // Record the current offset before updating the offset table
     size_t offset = getLength();
     size_t offset = getLength();
     // Write uncompress part:
     // Write uncompress part:
     if (nlabels_uncomp > 0 || !compress) {
     if (nlabels_uncomp > 0 || !compress) {
-        LabelSequence uncomp_sequence(name);
+        LabelSequence uncomp_sequence(ls);
         if (compress && nlabels > nlabels_uncomp) {
         if (compress && nlabels > nlabels_uncomp) {
             // If there's compressed part, strip off that part.
             // If there's compressed part, strip off that part.
             uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
             uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
@@ -342,7 +345,7 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
     // in the hash table.  The renderer's buffer has just stored the
     // in the hash table.  The renderer's buffer has just stored the
     // corresponding data, so we use the rendered data to get the length
     // corresponding data, so we use the rendered data to get the length
     // of each label of the names.
     // of each label of the names.
-    size_t seqlen = name.getLength();
+    size_t seqlen = ls.getDataLength();
     for (size_t i = 0; i < nlabels_uncomp; ++i) {
     for (size_t i = 0; i < nlabels_uncomp; ++i) {
         const uint8_t label_len = getBuffer()[offset];
         const uint8_t label_len = getBuffer()[offset];
         if (label_len == 0) { // offset for root doesn't need to be stored.
         if (label_len == 0) { // offset for root doesn't need to be stored.
@@ -359,6 +362,11 @@ MessageRenderer::writeName(const Name& name, const bool compress) {
     }
     }
 }
 }
 
 
+void
+MessageRenderer::writeName(const Name& name, const bool compress) {
+    writeName(LabelSequence(name), compress);
+}
+
 AbstractMessageRenderer::AbstractMessageRenderer() :
 AbstractMessageRenderer::AbstractMessageRenderer() :
     local_buffer_(0), buffer_(&local_buffer_)
     local_buffer_(0), buffer_(&local_buffer_)
 {
 {

+ 18 - 0
src/lib/dns/messagerenderer.h

@@ -22,6 +22,7 @@ namespace isc {
 namespace dns {
 namespace dns {
 // forward declarations
 // forward declarations
 class Name;
 class Name;
+class LabelSequence;
 
 
 /// \brief The \c AbstractMessageRenderer class is an abstract base class
 /// \brief The \c AbstractMessageRenderer class is an abstract base class
 /// that provides common interfaces for rendering a DNS message into a buffer
 /// that provides common interfaces for rendering a DNS message into a buffer
@@ -372,6 +373,23 @@ public:
 
 
     virtual void clear();
     virtual void clear();
     virtual void writeName(const Name& name, bool compress = true);
     virtual void writeName(const Name& name, bool compress = true);
+
+    /// \brief Write a \c LabelSequence object into the internal buffer
+    /// in wire format, with or without name compression.
+    ///
+    /// If the optional parameter \c compress is \c true, this method tries to
+    /// compress the \c ls if possible, searching the entire message that has
+    /// been rendered.  Otherwise name compression is omitted.  Its default
+    /// value is \c true.
+    ///
+    /// Note: even if \c compress is \c true, the position of the \c ls (and
+    /// possibly its ancestor names) in the message is recorded and may be used
+    /// for compressing subsequent names.
+    ///
+    /// \param ls A \c LabelSequence object to be written.
+    /// \param compress A boolean indicating whether to enable name compression.
+    void writeName(const LabelSequence& ls, bool compress = true);
+
 private:
 private:
     struct MessageRendererImpl;
     struct MessageRendererImpl;
     MessageRendererImpl* impl_;
     MessageRendererImpl* impl_;

+ 4 - 97
src/lib/dns/name.cc

@@ -127,7 +127,7 @@ typedef enum {
 
 
     // Unused at this moment.  We'll revisit this when we support master file
     // Unused at this moment.  We'll revisit this when we support master file
     // parser where @ is used to mean an origin name.
     // parser where @ is used to mean an origin name.
-    ft_at                  
+    ft_at
 } ft_state;
 } ft_state;
 }
 }
 
 
@@ -435,101 +435,7 @@ Name::toText(bool omit_final_dot) const {
 
 
 NameComparisonResult
 NameComparisonResult
 Name::compare(const Name& other) const {
 Name::compare(const Name& other) const {
-    return (compare(other, 0, 0, labelcount_, other.labelcount_));
+    return LabelSequence(*this).compare(LabelSequence(other));
-}
-
-NameComparisonResult
-Name::compare(const Name& other,
-              unsigned int first_label,
-              unsigned int first_label_other,
-              unsigned int last_label,
-              unsigned int last_label_other,
-              bool case_sensitive) const {
-    // Determine the relative ordering under the DNSSEC order relation of
-    // 'this' and 'other', and also determine the hierarchical relationship
-    // of the names.
-
-    if ((first_label > last_label) ||
-        (first_label_other > last_label_other)) {
-        isc_throw(BadValue, "Bad label index ranges were passed");
-    }
-
-    if ((first_label > labelcount_) ||
-        (first_label_other > other.labelcount_)) {
-        isc_throw(BadValue, "Bad first label indices were passed");
-    }
-
-    unsigned int nlabels = 0;
-    int l1 = last_label - first_label;
-    int l2 = last_label_other - first_label_other;
-    int ldiff = (int)l1 - (int)l2;
-    unsigned int l = (ldiff < 0) ? l1 : l2;
-
-    while (l > 0) {
-        --l;
-        --l1;
-        --l2;
-        size_t pos1 = offsets_[l1 + first_label];
-        size_t pos2 = other.offsets_[l2 + first_label_other];
-        unsigned int count1 = ndata_[pos1++];
-        unsigned int count2 = other.ndata_[pos2++];
-
-        // We don't support any extended label types including now-obsolete
-        // bitstring labels.
-        assert(count1 <= MAX_LABELLEN && count2 <= MAX_LABELLEN);
-
-        int cdiff = (int)count1 - (int)count2;
-        unsigned int count = (cdiff < 0) ? count1 : count2;
-
-        while (count > 0) {
-            uint8_t label1 = ndata_[pos1];
-            uint8_t label2 = other.ndata_[pos2];
-            int chdiff;
-
-            if (case_sensitive) {
-                chdiff = (int)label1 - (int)label2;
-            } else {
-                chdiff = (int)maptolower[label1] - (int)maptolower[label2];
-            }
-
-            if (chdiff != 0) {
-                if ((nlabels == 0) &&
-                    ((last_label < labelcount_) ||
-                     (last_label_other < other.labelcount_))) {
-                    return (NameComparisonResult(0, 0,
-                                                 NameComparisonResult::NONE));
-                } else {
-                    return (NameComparisonResult(chdiff, nlabels,
-                                                 NameComparisonResult::COMMONANCESTOR));
-                }
-            }
-            --count;
-            ++pos1;
-            ++pos2;
-        }
-        if (cdiff != 0) {
-            if ((nlabels == 0) &&
-                ((last_label < labelcount_) ||
-                 (last_label_other < other.labelcount_))) {
-                return (NameComparisonResult(0, 0,
-                                             NameComparisonResult::NONE));
-            } else {
-                return (NameComparisonResult(cdiff, nlabels,
-                                             NameComparisonResult::COMMONANCESTOR));
-            }
-        }
-        ++nlabels;
-    }
-
-    if (ldiff < 0) {
-        return (NameComparisonResult(ldiff, nlabels,
-                                     NameComparisonResult::SUPERDOMAIN));
-    } else if (ldiff > 0) {
-        return (NameComparisonResult(ldiff, nlabels,
-                                     NameComparisonResult::SUBDOMAIN));
-    }
-
-    return (NameComparisonResult(ldiff, nlabels, NameComparisonResult::EQUAL));
 }
 }
 
 
 bool
 bool
@@ -581,7 +487,7 @@ Name::gthan(const Name& other) const {
 
 
 bool
 bool
 Name::isWildcard() const {
 Name::isWildcard() const {
-    return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*'); 
+    return (length_ >= 2 && ndata_[0] == 1 && ndata_[1] == '*');
 }
 }
 
 
 Name
 Name
@@ -729,5 +635,6 @@ operator<<(std::ostream& os, const Name& name) {
     os << name.toText();
     os << name.toText();
     return (os);
     return (os);
 }
 }
+
 }
 }
 }
 }

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

@@ -137,7 +137,7 @@ public:
     /// want to distinguish "com" and "com.", and the current definition would
     /// want to distinguish "com" and "com.", and the current definition would
     /// be more compatible for that purpose.
     /// be more compatible for that purpose.
     /// If, on the other hand, we finally decide we really don't need that
     /// If, on the other hand, we finally decide we really don't need that
-    /// notion, we'll probably reconsider the design here, too. 
+    /// notion, we'll probably reconsider the design here, too.
     enum NameRelation {
     enum NameRelation {
         SUPERDOMAIN = 0,
         SUPERDOMAIN = 0,
         SUBDOMAIN = 1,
         SUBDOMAIN = 1,
@@ -405,35 +405,6 @@ public:
     /// comparison result.
     /// comparison result.
     NameComparisonResult compare(const Name& other) const;
     NameComparisonResult compare(const Name& other) const;
 
 
-private:
-    /// \brief Partially compare two <code>Name</code>s.
-    ///
-    /// This method performs a partial comparison of the
-    /// <code>Name</code> and <code>other</code> and returns the result
-    /// in the form of a <code>NameComparisonResult</code> object.
-    ///
-    /// This method can throw the BadValue exception if bad label
-    /// indices are passed.
-    ///
-    /// \param other the right-hand operand to compare against.
-    /// \param first_label the left-most label of <code>Name</code> to
-    /// begin comparing from.
-    /// \param first_label_other the left-most label of
-    /// <code>other</code> to begin comparing from.
-    /// \param last_label the right-most label of <code>Name</code> to
-    /// end comparing at.
-    /// \param last_label_other the right-most label of
-    /// <code>other</code> to end comparing at.
-    /// \param case_sensitive If true, comparison is case-insensitive
-    /// \return a <code>NameComparisonResult</code> object representing the
-    /// comparison result.
-    NameComparisonResult compare(const Name& other,
-                                 unsigned int first_label,
-                                 unsigned int first_label_other,
-                                 unsigned int last_label,
-                                 unsigned int last_label_other,
-                                 bool case_sensitive = false) const;
-
 public:
 public:
     /// \brief Return true iff two names are equal.
     /// \brief Return true iff two names are equal.
     ///
     ///
@@ -551,7 +522,7 @@ public:
     /// \param first The start position (in labels) of the extracted name
     /// \param first The start position (in labels) of the extracted name
     /// \param n Number of labels of the extracted name
     /// \param n Number of labels of the extracted name
     /// \return A new Name object based on the Name containing <code>n</code>
     /// \return A new Name object based on the Name containing <code>n</code>
-    /// labels including and following the <code>first</code> label.  
+    /// labels including and following the <code>first</code> label.
     Name split(unsigned int first, unsigned int n) const;
     Name split(unsigned int first, unsigned int n) const;
 
 
     /// \brief Extract a specified super domain name of Name.
     /// \brief Extract a specified super domain name of Name.
@@ -623,7 +594,7 @@ public:
     /// \brief Reverse the labels of a name
     /// \brief Reverse the labels of a name
     ///
     ///
     /// This method reverses the labels of a name.  For example, if
     /// This method reverses the labels of a name.  For example, if
-    /// \c this is "www.example.com.", this method will return 
+    /// \c this is "www.example.com.", this method will return
     /// "com.example.www."  (This is useful because DNSSEC sort order
     /// "com.example.www."  (This is useful because DNSSEC sort order
     /// is equivalent to a lexical sort of label-reversed names.)
     /// is equivalent to a lexical sort of label-reversed names.)
     Name reverse() const;
     Name reverse() const;
@@ -743,10 +714,11 @@ Name::ROOT_NAME() {
 /// parameter \c os after the insertion operation.
 /// parameter \c os after the insertion operation.
 std::ostream&
 std::ostream&
 operator<<(std::ostream& os, const Name& name);
 operator<<(std::ostream& os, const Name& name);
+
 }
 }
 }
 }
 #endif // __NAME_H
 #endif // __NAME_H
 
 
-// Local Variables: 
+// Local Variables:
 // mode: c++
 // mode: c++
-// End: 
+// End:

+ 3 - 2
src/lib/dns/python/name_python.cc

@@ -522,8 +522,9 @@ Name_isWildCard(s_Name* self) {
 
 
 Py_hash_t
 Py_hash_t
 Name_hash(PyObject* pyself) {
 Name_hash(PyObject* pyself) {
-    s_Name* const self = static_cast<s_Name*>(pyself);
+    const s_Name* const self = static_cast<s_Name*>(pyself);
-    return (LabelSequence(*self->cppobj).getHash(false));
+    return (convertToPyHash<size_t>(
+                LabelSequence(*self->cppobj).getHash(false)));
 }
 }
 
 
 } // end of unnamed namespace
 } // end of unnamed namespace

+ 55 - 0
src/lib/dns/python/pydnspp_common.h

@@ -17,6 +17,9 @@
 
 
 #include <Python.h>
 #include <Python.h>
 
 
+#include <boost/static_assert.hpp>
+
+#include <climits>  // for CHAR_BIT
 #include <stdexcept>
 #include <stdexcept>
 #include <string>
 #include <string>
 
 
@@ -48,6 +51,58 @@ int addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
 #if PY_MINOR_VERSION < 2
 #if PY_MINOR_VERSION < 2
 typedef long Py_hash_t;
 typedef long Py_hash_t;
 #endif
 #endif
+
+/// \brief Convert a hash value of arbitrary type to a Python hash value.
+///
+/// This templated function is a convenient wrapper to produce a valid hash
+/// value of type Py_hash_t, which is expected to be used as the return value
+/// of a PyTypeObject.tp_hash implementation.  Py_hash_t is a signed integer
+/// the same size as Py_ssize_t.
+///
+/// The major concern is that -1 means an error in tp_hash and so we need to
+/// ensure that this value is never returned.
+///
+/// If the size of the original hash type is small enough, we convert
+/// the original hash value to a value of Py_hash_t, resetting all higher bits
+/// to 0.  Due to the assumption on the sizes, the conversion to HashvalType
+/// is safe, and (after clearing the higher bits) results in a valid positive
+/// value.
+///
+/// If the size of the input and return types is the same, we clear the
+/// highest bit of the original hash so the resulting value will be in a valid
+/// positive range of Py_hash_t.  If the original type is unsigned, there's
+/// probably no better conversion than this because otherwise the conversion
+/// to Py_hash_t could result in an undefined behavior.  If the original type
+/// is signed, this conversion reduces the effective value range of the
+/// original hash.  If this is not desired, the caller should convert it
+/// by itself (note that -1 should be still avoided).
+///
+/// This function does not support the case where the size of the original
+/// hash type is larger than that of Py_hash_t.
+template <typename HashvalType>
+Py_hash_t
+convertToPyHash(HashvalType val) {
+    BOOST_STATIC_ASSERT(sizeof(HashvalType) <= sizeof(Py_hash_t));
+
+    // Some versions of g++ doesn't ignore the impossible case of if/else
+    // below (depending on the size of HashvalType) and triggers a false
+    // warning.
+    // To work around it we use an intermediate mutable variable.
+    // See Trac #1883 for details.
+    size_t hash_val_bits = CHAR_BIT * sizeof(HashvalType);
+
+    if (sizeof(HashvalType) < sizeof(Py_hash_t)) {
+        // The original hash type is small enough.  Do trivial conversion.
+        const Py_hash_t mask = ~(static_cast<Py_hash_t>(-1) << hash_val_bits);
+        return (static_cast<Py_hash_t>(val) & mask);
+    } else {
+        // Clear the highest bit of the original hash so the conversion is
+        // safe and avoids -1.
+        HashvalType mask = ~(static_cast<HashvalType>(1) <<
+                             (hash_val_bits - 1));
+        return (val & mask);
+    }
+}
 } // namespace python
 } // namespace python
 } // namespace dns
 } // namespace dns
 } // namespace isc
 } // namespace isc

+ 2 - 2
src/lib/dns/python/rrclass_python.cc

@@ -267,8 +267,8 @@ PyObject* RRClass_ANY(s_RRClass*) {
 
 
 Py_hash_t
 Py_hash_t
 RRClass_hash(PyObject* pyself) {
 RRClass_hash(PyObject* pyself) {
-    s_RRClass* const self = static_cast<s_RRClass*>(pyself);
+    const s_RRClass* const self = static_cast<s_RRClass*>(pyself);
-    return (self->cppobj->getCode());
+    return (convertToPyHash<uint16_t>(self->cppobj->getCode()));
 }
 }
 
 
 } // end anonymous namespace
 } // end anonymous namespace

+ 7 - 1
src/lib/dns/python/rrtype_python.cc

@@ -49,6 +49,7 @@ PyObject* RRType_str(PyObject* self);
 PyObject* RRType_toWire(s_RRType* self, PyObject* args);
 PyObject* RRType_toWire(s_RRType* self, PyObject* args);
 PyObject* RRType_getCode(s_RRType* self);
 PyObject* RRType_getCode(s_RRType* self);
 PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op);
 PyObject* RRType_richcmp(s_RRType* self, s_RRType* other, int op);
+Py_hash_t RRType_hash(PyObject* pyself);
 PyObject* RRType_NSEC3PARAM(s_RRType *self);
 PyObject* RRType_NSEC3PARAM(s_RRType *self);
 PyObject* RRType_DNAME(s_RRType *self);
 PyObject* RRType_DNAME(s_RRType *self);
 PyObject* RRType_PTR(s_RRType *self);
 PyObject* RRType_PTR(s_RRType *self);
@@ -368,6 +369,11 @@ RRType_ANY(s_RRType*) {
     return (RRType_createStatic(RRType::ANY()));
     return (RRType_createStatic(RRType::ANY()));
 }
 }
 
 
+Py_hash_t
+RRType_hash(PyObject* pyself) {
+    const s_RRType* const self = static_cast<s_RRType*>(pyself);
+    return (convertToPyHash<uint16_t>(self->cppobj->getCode()));
+}
 } // end anonymous namespace
 } // end anonymous namespace
 
 
 namespace isc {
 namespace isc {
@@ -394,7 +400,7 @@ PyTypeObject rrtype_type = {
     NULL,                               // tp_as_number
     NULL,                               // tp_as_number
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_sequence
     NULL,                               // tp_as_mapping
     NULL,                               // tp_as_mapping
-    NULL,                               // tp_hash
+    RRType_hash,                        // tp_hash
     NULL,                               // tp_call
     NULL,                               // tp_call
     RRType_str,                         // tp_str
     RRType_str,                         // tp_str
     NULL,                               // tp_getattro
     NULL,                               // tp_getattro

+ 9 - 0
src/lib/dns/python/tests/rrtype_python_test.py

@@ -116,6 +116,15 @@ class TestModuleSpec(unittest.TestCase):
 
 
         self.assertFalse(self.rrtype_1 == 1)
         self.assertFalse(self.rrtype_1 == 1)
 
 
+    def test_hash(self):
+        # Exploiting the knowledge that the hash value is the numeric class
+        # value, we can predict the comparison result.
+        self.assertEqual(hash(RRType.AAAA()), hash(RRType("AAAA")))
+        self.assertEqual(hash(RRType("aaaa")), hash(RRType("AAAA")))
+        self.assertEqual(hash(RRType(28)), hash(RRType("AAAA")))
+        self.assertNotEqual(hash(RRType.A()), hash(RRType.NS()))
+        self.assertNotEqual(hash(RRType.AAAA()), hash(RRType("Type65535")))
+
     def test_statics(self):
     def test_statics(self):
         self.assertEqual(RRType("NSEC3PARAM"), RRType.NSEC3PARAM())
         self.assertEqual(RRType("NSEC3PARAM"), RRType.NSEC3PARAM())
         self.assertEqual(RRType("DNAME"), RRType.DNAME())
         self.assertEqual(RRType("DNAME"), RRType.DNAME())

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

@@ -400,6 +400,93 @@ TEST_F(LabelSequenceTest, getData) {
     getDataCheck("\000", 1, ls7);
     getDataCheck("\000", 1, ls7);
 };
 };
 
 
+TEST_F(LabelSequenceTest, getOffsetData) {
+    size_t len;
+    uint8_t placeholder[Name::MAX_LABELS];
+
+    Name nx("x.isc.example.org");
+    LabelSequence lsx(nx);
+
+    // x.isc.example.org.
+    lsx.getOffsetData(&len, placeholder);
+    EXPECT_EQ(5, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(2, placeholder[1]);
+    EXPECT_EQ(6, placeholder[2]);
+    EXPECT_EQ(14, placeholder[3]);
+    EXPECT_EQ(18, placeholder[4]);
+
+    lsx.stripLeft(2);
+
+    // example.org.
+    lsx.getOffsetData(&len, placeholder);
+    EXPECT_EQ(3, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(8, placeholder[1]);
+    EXPECT_EQ(12, placeholder[2]);
+
+    lsx.stripLeft(1);
+
+    // org.
+    lsx.getOffsetData(&len, placeholder);
+    EXPECT_EQ(2, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(4, placeholder[1]);
+
+    lsx.stripLeft(1);
+
+    // .
+    lsx.getOffsetData(&len, placeholder);
+    EXPECT_EQ(1, len);
+    EXPECT_EQ(0, placeholder[0]);
+
+    Name ny("y.isc.example.org");
+    LabelSequence lsy(ny);
+
+    // y.isc.example.org.
+    lsy.getOffsetData(&len, placeholder);
+    EXPECT_EQ(5, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(2, placeholder[1]);
+    EXPECT_EQ(6, placeholder[2]);
+    EXPECT_EQ(14, placeholder[3]);
+    EXPECT_EQ(18, placeholder[4]);
+
+    lsy.stripRight(1);
+
+    // y.isc.example.org
+    lsy.getOffsetData(&len, placeholder);
+    EXPECT_EQ(4, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(2, placeholder[1]);
+    EXPECT_EQ(6, placeholder[2]);
+    EXPECT_EQ(14, placeholder[3]);
+
+    lsy.stripRight(1);
+
+    // y.isc.example
+    lsy.getOffsetData(&len, placeholder);
+    EXPECT_EQ(3, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(2, placeholder[1]);
+    EXPECT_EQ(6, placeholder[2]);
+
+    lsy.stripLeft(1);
+
+    // isc.example
+    lsy.getOffsetData(&len, placeholder);
+    EXPECT_EQ(2, len);
+    EXPECT_EQ(0, placeholder[0]);
+    EXPECT_EQ(4, placeholder[1]);
+
+    lsy.stripLeft(1);
+
+    // example
+    lsy.getOffsetData(&len, placeholder);
+    EXPECT_EQ(1, len);
+    EXPECT_EQ(0, placeholder[0]);
+};
+
 TEST_F(LabelSequenceTest, stripLeft) {
 TEST_F(LabelSequenceTest, stripLeft) {
     EXPECT_TRUE(ls1.equals(ls3));
     EXPECT_TRUE(ls1.equals(ls3));
     ls1.stripLeft(0);
     ls1.stripLeft(0);
@@ -674,4 +761,79 @@ TEST_F(LabelSequenceTest, LeftShiftOperator) {
     oss << ls1;
     oss << ls1;
     EXPECT_EQ(ls1.toText(), oss.str());
     EXPECT_EQ(ls1.toText(), oss.str());
 }
 }
+
+// Test different ways of construction, and see if they compare
+TEST(LabelSequence, rawConstruction) {
+    Name n("example.org");
+
+    uint8_t data[] = { 0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
+                       0x03, 'o', 'r', 'g',
+                       0x00 };
+    uint8_t offsets[] = { 0, 8, 12 };
+    size_t offsets_size = 3;
+
+    LabelSequence s1(n);
+    LabelSequence s2(s1);
+    LabelSequence s3(data, offsets, offsets_size);
+
+    // Assuming equality is transitive, so only comparing 1 to 2 and 1 to 3
+    NameComparisonResult result = s1.compare(s2);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    result = s1.compare(s3);
+    EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL,
+              result.getRelation());
+    EXPECT_EQ(0, result.getOrder());
+    EXPECT_EQ(3, result.getCommonLabels());
+
+    // Modify the data and make sure it's not equal anymore
+    data[2] = 'f';
+    result = s1.compare(s3);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(2, result.getCommonLabels());
+
+    s1.stripRight(1);
+    s3.stripRight(1);
+
+    result = s1.compare(s3);
+    EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR,
+              result.getRelation());
+    EXPECT_EQ(1, result.getCommonLabels());
+
+    data[9] = 'f';
+    result = s1.compare(s3);
+    EXPECT_EQ(isc::dns::NameComparisonResult::NONE,
+              result.getRelation());
+    EXPECT_EQ(0, result.getCommonLabels());
+}
+
+// Test with some data that exceeds limits (MAX_LABELS and MAX_LABEL_LEN)
+TEST(LabelSequence, badRawConstruction) {
+    uint8_t data[1] = { 0 };
+    uint8_t offsets[1] = { 0 };
+
+    EXPECT_THROW(LabelSequence(NULL, offsets, 1), isc::BadValue);
+    EXPECT_THROW(LabelSequence(data, NULL, 1), isc::BadValue);
+    EXPECT_THROW(LabelSequence(data, offsets, 0), isc::BadValue);
+
+    // exceed MAX_LABELS
+    EXPECT_THROW(LabelSequence(data, offsets, 127), isc::BadValue);
+
+    // exceed MAX_LABEL_LEN
+    uint8_t offsets_toolonglabel[1] = { 64 };
+    EXPECT_THROW(LabelSequence(data, offsets_toolonglabel, 1), isc::BadValue);
+
+    // Add an offset that is lower than the previous offset
+    uint8_t offsets_lower[3] = { 0, 8, 4 };
+    EXPECT_THROW(LabelSequence(data, offsets_lower, 3), isc::BadValue);
+
+    // Add an offset that is equal to the previous offset
+    uint8_t offsets_noincrease[3] = { 0, 8, 8 };
+    EXPECT_THROW(LabelSequence(data, offsets_noincrease, 3), isc::BadValue);
+}
+
 }
 }

+ 63 - 0
src/lib/dns/tests/messagerenderer_unittest.cc

@@ -15,6 +15,7 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
 #include <util/buffer.h>
 #include <dns/name.h>
 #include <dns/name.h>
+#include <dns/labelsequence.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
 
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/unittest_util.h>
@@ -28,6 +29,7 @@
 
 
 using isc::UnitTestUtil;
 using isc::UnitTestUtil;
 using isc::dns::Name;
 using isc::dns::Name;
+using isc::dns::LabelSequence;
 using isc::dns::MessageRenderer;
 using isc::dns::MessageRenderer;
 using isc::util::OutputBuffer;
 using isc::util::OutputBuffer;
 using boost::lexical_cast;
 using boost::lexical_cast;
@@ -176,6 +178,67 @@ TEST_F(MessageRendererTest, writeRootName) {
                         expected.getLength());
                         expected.getLength());
 }
 }
 
 
+TEST_F(MessageRendererTest, writeNameLabelSequence1) {
+    UnitTestUtil::readWireData("name_toWire7", data);
+
+    Name n1("a.example.com");
+    LabelSequence ls1(n1);
+
+    // a.example.com.
+    renderer.writeName(ls1);
+
+    ls1.stripLeft(1);
+
+    // example.com.
+    renderer.writeName(ls1);
+
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
+}
+
+TEST_F(MessageRendererTest, writeNameLabelSequence2) {
+    UnitTestUtil::readWireData("name_toWire8", data);
+
+    Name n1("a.example.com");
+    LabelSequence ls1(n1);
+
+    ls1.stripRight(1);
+
+    // a.example.com (without root .)
+    renderer.writeName(ls1);
+
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
+}
+
+TEST_F(MessageRendererTest, writeNameLabelSequence3) {
+    UnitTestUtil::readWireData("name_toWire9", data);
+
+    Name n1("a.example.com");
+    LabelSequence ls1(n1);
+
+    // a.example.com.
+    renderer.writeName(ls1);
+
+    ls1.stripRight(1);
+
+    // a.example.com (without root .)
+    renderer.writeName(ls1);
+
+    ls1.stripRight(1);
+
+    // a.example
+    renderer.writeName(ls1);
+
+    ls1.stripLeft(1);
+
+    // example
+    renderer.writeName(ls1);
+
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
+}
+
 TEST_F(MessageRendererTest, setBuffer) {
 TEST_F(MessageRendererTest, setBuffer) {
     OutputBuffer new_buffer(0);
     OutputBuffer new_buffer(0);
     renderer.setBuffer(&new_buffer);
     renderer.setBuffer(&new_buffer);

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

@@ -93,6 +93,7 @@ EXTRA_DIST += name_fromWire9 name_fromWire10 name_fromWire11 name_fromWire12
 EXTRA_DIST += name_fromWire13 name_fromWire14
 EXTRA_DIST += name_fromWire13 name_fromWire14
 EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4
 EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4
 EXTRA_DIST += name_toWire5.spec name_toWire6.spec
 EXTRA_DIST += name_toWire5.spec name_toWire6.spec
+EXTRA_DIST += name_toWire7 name_toWire8 name_toWire9
 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2
 EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
 EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec
 EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec
 EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec

+ 1 - 1
src/lib/dns/tests/testdata/name_toWire3

@@ -7,7 +7,7 @@
  01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
  01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
 
 
 #15                                        29 (bytes)
 #15                                        29 (bytes)
-#(1) b(7)  e  x  a  m  p  l  e (3) c  o  m  .; specified to be not compressed,
+#(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
 #                                              but can be pointed to from others
  01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
  01 62 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
 #[0f] referring to the second (uncompressed name)
 #[0f] referring to the second (uncompressed name)

+ 10 - 0
src/lib/dns/tests/testdata/name_toWire7

@@ -0,0 +1,10 @@
+#
+# 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
+
+#[02] pointing to -> "example.com."
+ c0 02

+ 7 - 0
src/lib/dns/tests/testdata/name_toWire8

@@ -0,0 +1,7 @@
+#
+# Rendering names.
+# [x] means a compression pointer pointing to offset 'x'.
+#
+# 0  1  2  3  4  5  6  7  8  9 10 11 12 13 (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

+ 13 - 0
src/lib/dns/tests/testdata/name_toWire9

@@ -0,0 +1,13 @@
+#
+# 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
+#(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
+#(1) a (7) e  x  a  m  p  l  e
+ 01 61 07 65 78 61 6d 70 6c 65
+#[1f] pointing to ^^ "example"
+ c0 1f

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

@@ -16,6 +16,8 @@ libutil_la_SOURCES += time_utilities.h time_utilities.cc
 libutil_la_SOURCES += interprocess_sync.h
 libutil_la_SOURCES += interprocess_sync.h
 libutil_la_SOURCES += interprocess_sync_file.h interprocess_sync_file.cc
 libutil_la_SOURCES += interprocess_sync_file.h interprocess_sync_file.cc
 libutil_la_SOURCES += interprocess_sync_null.h interprocess_sync_null.cc
 libutil_la_SOURCES += interprocess_sync_null.h interprocess_sync_null.cc
+libutil_la_SOURCES += memory_segment.h
+libutil_la_SOURCES += memory_segment_local.h memory_segment_local.cc
 libutil_la_SOURCES += range_utilities.h
 libutil_la_SOURCES += range_utilities.h
 libutil_la_SOURCES += hash/sha1.h hash/sha1.cc
 libutil_la_SOURCES += hash/sha1.h hash/sha1.cc
 libutil_la_SOURCES += encode/base16_from_binary.h
 libutil_la_SOURCES += encode/base16_from_binary.h

+ 69 - 0
src/lib/util/memory_segment.h

@@ -0,0 +1,69 @@
+// 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 __MEMORY_SEGMENT_H__
+#define __MEMORY_SEGMENT_H__
+
+#include <stdlib.h>
+
+namespace isc {
+namespace util {
+
+/// \brief Memory Segment Class
+///
+/// This class specifies an interface for allocating memory
+/// segments. This is an abstract class and a real
+/// implementation such as MemorySegmentLocal should be used
+/// in code.
+class MemorySegment {
+public:
+    /// \brief Destructor
+    virtual ~MemorySegment() {}
+
+    /// \brief Allocate/acquire a segment of memory. The source of the
+    /// memory is dependent on the implementation used.
+    ///
+    /// Throws <code>std::bad_alloc</code> if the implementation cannot
+    /// allocate the requested storage.
+    ///
+    /// \param size The size of the memory requested in bytes.
+    /// \return Returns pointer to the memory allocated.
+    virtual void* allocate(size_t size) = 0;
+
+    /// \brief Free/release a segment of memory.
+    ///
+    /// This method may throw <code>isc::OutOfRange</code> if \c size is
+    /// not equal to the originally allocated size. \c size could be
+    /// used by some implementations such as a slice allocator, where
+    /// freeing memory also requires the size to be specified. We also
+    /// use this argument in some implementations to test if all allocated
+    /// memory was deallocated properly.
+    ///
+    /// \param ptr Pointer to the block of memory to free/release. This
+    /// should be equal to a value returned by <code>allocate()</code>.
+    /// \param size The size of the memory to be freed in bytes. This
+    /// should be equal to the number of bytes originally allocated.
+    virtual void deallocate(void* ptr, size_t size) = 0;
+
+    /// \brief Check if all allocated memory was deallocated.
+    ///
+    /// \return Returns <code>true</code> if all allocated memory was
+    /// deallocated, <code>false</code> otherwise.
+    virtual bool allMemoryDeallocated() const = 0;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // __MEMORY_SEGMENT_H__

+ 55 - 0
src/lib/util/memory_segment_local.cc

@@ -0,0 +1,55 @@
+// 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 "memory_segment_local.h"
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace util {
+
+void*
+MemorySegmentLocal::allocate(size_t size) {
+    void* ptr = malloc(size);
+    if (ptr == NULL) {
+        throw std::bad_alloc();
+    }
+
+    allocated_size_ += size;
+    return (ptr);
+}
+
+void
+MemorySegmentLocal::deallocate(void* ptr, size_t size) {
+    if (ptr == NULL) {
+        // Return early if NULL is passed to be deallocated (without
+        // modifying allocated_size, or comparing against it).
+        return;
+    }
+
+    if (size > allocated_size_) {
+      isc_throw(OutOfRange, "Invalid size to deallocate: " << size
+                << "; currently allocated size: " << allocated_size_);
+    }
+
+    allocated_size_ -= size;
+    free(ptr);
+}
+
+bool
+MemorySegmentLocal::allMemoryDeallocated() const {
+    return (allocated_size_ == 0);
+}
+
+} // namespace util
+} // namespace isc

+ 76 - 0
src/lib/util/memory_segment_local.h

@@ -0,0 +1,76 @@
+// 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 __MEMORY_SEGMENT_LOCAL_H__
+#define __MEMORY_SEGMENT_LOCAL_H__
+
+#include <util/memory_segment.h>
+
+namespace isc {
+namespace util {
+
+/// \brief malloc/free based Memory Segment class
+///
+/// This class specifies a concrete implementation for a malloc/free
+/// based MemorySegment. Please see the MemorySegment class
+/// documentation for usage.
+class MemorySegmentLocal : public MemorySegment {
+public:
+    /// \brief Constructor
+    ///
+    /// Creates a local memory segment object
+    MemorySegmentLocal() : allocated_size_(0) {
+    }
+
+    /// \brief Destructor
+    virtual ~MemorySegmentLocal() {}
+
+    /// \brief Allocate/acquire a segment of memory. The source of the
+    /// memory is libc's malloc().
+    ///
+    /// Throws <code>std::bad_alloc</code> if the implementation cannot
+    /// allocate the requested storage.
+    ///
+    /// \param size The size of the memory requested in bytes.
+    /// \return Returns pointer to the memory allocated.
+    virtual void* allocate(size_t size);
+
+    /// \brief Free/release a segment of memory.
+    ///
+    /// This method may throw <code>isc::OutOfRange</code> if \c size is
+    /// not equal to the originally allocated size.
+    ///
+    /// \param ptr Pointer to the block of memory to free/release. This
+    /// should be equal to a value returned by <code>allocate()</code>.
+    /// \param size The size of the memory to be freed in bytes. This
+    /// should be equal to the number of bytes originally allocated.
+    virtual void deallocate(void* ptr, size_t size);
+
+    /// \brief Check if all allocated memory was deallocated.
+    ///
+    /// \return Returns <code>true</code> if all allocated memory was
+    /// deallocated, <code>false</code> otherwise.
+    virtual bool allMemoryDeallocated() const;
+
+private:
+    // allocated_size_ can underflow, wrap around to max size_t (which
+    // is unsigned). But because we only do a check against 0 and not a
+    // relation comparison, this is okay.
+    size_t allocated_size_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // __MEMORY_SEGMENT_LOCAL_H__

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

@@ -33,6 +33,7 @@ run_unittests_SOURCES += io_utilities_unittest.cc
 run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += lru_list_unittest.cc
 run_unittests_SOURCES += interprocess_sync_file_unittest.cc
 run_unittests_SOURCES += interprocess_sync_file_unittest.cc
 run_unittests_SOURCES += interprocess_sync_null_unittest.cc
 run_unittests_SOURCES += interprocess_sync_null_unittest.cc
+run_unittests_SOURCES += memory_segment_local_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += qid_gen_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
 run_unittests_SOURCES += random_number_generator_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc
 run_unittests_SOURCES += sha1_unittest.cc

+ 108 - 0
src/lib/util/tests/memory_segment_local_unittest.cc

@@ -0,0 +1,108 @@
+// 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 "util/memory_segment_local.h"
+#include <exceptions/exceptions.h>
+#include <gtest/gtest.h>
+#include <memory>
+
+using namespace std;
+using namespace isc::util;
+
+namespace {
+
+TEST(MemorySegmentLocal, TestLocal) {
+    auto_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+    // By default, nothing is allocated.
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+
+    void* ptr = segment->allocate(1024);
+
+    // Now, we have an allocation:
+    EXPECT_FALSE(segment->allMemoryDeallocated());
+
+    void* ptr2 = segment->allocate(42);
+
+    // Still:
+    EXPECT_FALSE(segment->allMemoryDeallocated());
+
+    // These should not fail, because the buffers have been allocated.
+    EXPECT_NO_FATAL_FAILURE(memset(ptr, 0, 1024));
+    EXPECT_NO_FATAL_FAILURE(memset(ptr, 0, 42));
+
+    segment->deallocate(ptr, 1024);
+
+    // Still:
+    EXPECT_FALSE(segment->allMemoryDeallocated());
+
+    segment->deallocate(ptr2, 42);
+
+    // Now, we have an deallocated everything:
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+}
+
+TEST(MemorySegmentLocal, TestTooMuchMemory) {
+    auto_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+    EXPECT_THROW(segment->allocate(0x7fffffffffffffff), bad_alloc);
+}
+
+TEST(MemorySegmentLocal, TestBadDeallocate) {
+    auto_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+    // By default, nothing is allocated.
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+
+    void* ptr = segment->allocate(1024);
+
+    // Now, we have an allocation:
+    EXPECT_FALSE(segment->allMemoryDeallocated());
+
+    // This should not throw
+    EXPECT_NO_THROW(segment->deallocate(ptr, 1024));
+
+    // Now, we have an deallocated everything:
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+
+    ptr = segment->allocate(1024);
+
+    // Now, we have another allocation:
+    EXPECT_FALSE(segment->allMemoryDeallocated());
+
+    // This should throw as the size passed to deallocate() is larger
+    // than what was allocated.
+    EXPECT_THROW(segment->deallocate(ptr, 2048), isc::OutOfRange);
+
+    // This should not throw
+    EXPECT_NO_THROW(segment->deallocate(ptr, 1024));
+
+    // Now, we have an deallocated everything:
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+}
+
+TEST(MemorySegmentLocal, TestNullDeallocate) {
+    auto_ptr<MemorySegment> segment(new MemorySegmentLocal());
+
+    // By default, nothing is allocated.
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+
+    // NULL deallocation is a no-op.
+    EXPECT_NO_THROW(segment->deallocate(NULL, 1024));
+
+    // This should still return true.
+    EXPECT_TRUE(segment->allMemoryDeallocated());
+}
+
+} // anonymous namespace