BIND 10 trac2156, updated. 791b970a1fcc8fa6525a9bb8ec5fc94afc0c6f7c [2156] removed duplicated include

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Apr 3 11:20:39 UTC 2013


The branch, trac2156 has been updated
       via  791b970a1fcc8fa6525a9bb8ec5fc94afc0c6f7c (commit)
       via  d838db74cc1be9656648dc97d08ed2a6a73e5a31 (commit)
       via  62c273605b092d2c6f8058a31c7fd318eeff0b8b (commit)
       via  9349c08779d8325508dabd4818eac3628189d947 (commit)
       via  70fd53cf6bbfc536163e70d4621659f840fed83a (commit)
       via  ce7f53b2de60e2411483b4aa31c714763a36da64 (commit)
       via  cd17ffda53e213f39c0e02f38b415b31806c331d (commit)
       via  119eed9938b17cbad3a74c823aa9eddb7cd337c2 (commit)
       via  49726757166a3a3e1374091f76f12b4203782c98 (commit)
       via  b6b3a1a1073b2f674c8ba189635069d3ecadc8bd (commit)
       via  5156bc4dbb05a5803b87e57751ffc7ba94e4ac98 (commit)
       via  68e9b6e28b981e394f1fd55de00840e861695044 (commit)
       via  b59d780f0b9fd66c06eb7d06082e41249b778f94 (commit)
       via  ad797b98b8ae4f0f003374c13649147aef3c1a83 (commit)
       via  8802d414240a4b453b13573e1bbc381eb0d14343 (commit)
       via  b321b2e7708069b82bae12eb1b4461f4eb9e4a03 (commit)
       via  fe630ca4be50f0f0bd048eec264b8a21df9c6b38 (commit)
       via  b41986aa9d6b0d78642082abeb8186ef5d7bf431 (commit)
       via  0a2f3129eb9f3c9319a3b203fe1bb8bc386ef212 (commit)
       via  cdb550b4be0db3a37ca40426d288672615335cee (commit)
       via  7f62c19c5d561e755570b8aeab625dd275ecae3a (commit)
       via  4c90b6cf817c2087052ea8b22e91a3b7f8ae39f0 (commit)
       via  1450d8d486cba3bee8be46e8001d66898edd370c (commit)
       via  88d5e6c5a78f2b55fd5518a865bb138c52e910b0 (commit)
       via  f290b63a15ebe4d4f26908c2c9948b43188c4a15 (commit)
       via  03b6bd8461d7aee84ac34da1c737fed4a3c16c22 (commit)
       via  cbaab234b0fe61e09b559c7623e3f29bc89ba1b9 (commit)
       via  946126b36db6b9cd21c194a84a61417dd6bd567c (commit)
       via  8c6afd880d47f33174b656ed12a2e5e4c821e14b (commit)
       via  22407c48db5999e77a6fbd6e4f42ba2a3fe36762 (commit)
       via  bc87d1113832e7900d3627663c0a1203c3e7b2da (commit)
       via  6fadd98cb5a9beeb79c58ba0a2768d9ad895c210 (commit)
       via  9a551ccda02b841bf296ecbce5c0bbf04721024a (commit)
       via  47b58321cd15762b9cdd0f09e66e9f5b29e67eec (commit)
       via  e1c60036dfd2d4e1fcd372807e4a3f0f201028cb (commit)
       via  6e0725b08ad12bafcc939622daa5a2f7b734ecbe (commit)
       via  8377d0dcf3c758cc50111a98a7a9dafdc3237c44 (commit)
       via  5e9811640dc3e514ee012b16287522f7993ade46 (commit)
       via  d007842f66f874ad8ceacf664f8c0645d2ec4226 (commit)
       via  d25f0e1e01a6cc3991ae63f0957c61ec171395ce (commit)
       via  0be5bed016c3b12abf5e7049ab7b63d2d474efc0 (commit)
       via  2e6d5f8d602a632d8a770c13d7ccbc09a25825ad (commit)
       via  7642e09748501d041309b84de9d46b3aeab74a63 (commit)
       via  9dd3b10a7ce79fb1b1289136987f82c2bf9c2310 (commit)
       via  739735c08bb30957be78506bc4cf0d6c5f01745d (commit)
       via  01e052f372b1878b9bc40bfcea80d37e408a58ca (commit)
       via  25b99148984082fd818624cd74cde2d86d428c2b (commit)
       via  97a7be5ddb8f5a89206912eae174bacf3d230a8e (commit)
       via  f37600d5ae721b626b9e9323dbff17a404908e46 (commit)
       via  a043d8ecda6aff57922fe98a33c7c3f6155d5d64 (commit)
       via  e07f341d540ce584b3edc424a8cf9a8f1b9a542c (commit)
       via  c533897b58690ab30d12dd9837de04e08d840ee3 (commit)
       via  4317f74e10dbaa6a03a2bba9993fa2c79aa241bb (commit)
       via  b312e9ab66e413ee0708fc2db23d27e7f4b7b251 (commit)
       via  28e4d9aaa305d597c533f43c8eaaae37567d6896 (commit)
       via  6cbb6ed662f7ee7b959865eb741c6c0029d0afd8 (commit)
      from  0b1d39602715d67fd5c569994b8893431a03dec7 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 791b970a1fcc8fa6525a9bb8ec5fc94afc0c6f7c
Author: Kazunori Fujiwara <fujiwara at wide.ad.jp>
Date:   Wed Apr 3 11:13:24 2013 +0900

    [2156] removed duplicated include

commit d838db74cc1be9656648dc97d08ed2a6a73e5a31
Merge: 9349c08 62c2736
Author: Kazunori Fujiwara <fujiwara at wide.ad.jp>
Date:   Wed Apr 3 11:06:00 2013 +0900

    Merge branch 'master' into trac2156

commit 9349c08779d8325508dabd4818eac3628189d947
Merge: 0b1d396 70fd53c
Author: Kazunori Fujiwara <fujiwara at wide.ad.jp>
Date:   Tue Apr 2 05:14:20 2013 +0900

    Merge branch 'master' into trac2156

-----------------------------------------------------------------------

Summary of changes:
 AUTHORS                                            |    1 +
 ChangeLog                                          |   29 +++
 src/bin/auth/auth_messages.mes                     |   29 ++-
 src/bin/auth/auth_srv.cc                           |   32 ++-
 src/bin/auth/tests/auth_srv_unittest.cc            |   13 +-
 src/bin/bindctl/bindcmd.py                         |    6 +-
 src/bin/cfgmgr/plugins/datasrc.spec.pre.in         |    5 +
 src/bin/cfgmgr/plugins/tests/datasrc_test.py       |   95 ++++++++
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  255 ++++++++++++++++----
 src/bin/xfrin/xfrin.py.in                          |    2 +-
 src/lib/asiodns/tcp_server.h                       |    1 -
 src/lib/cc/data.h                                  |    2 +-
 src/lib/config/ccsession.h                         |   26 +-
 src/lib/datasrc/client_list.cc                     |   38 ++-
 src/lib/datasrc/client_list.h                      |   85 ++++++-
 src/lib/datasrc/tests/client_list_unittest.cc      |   95 +++++++-
 src/lib/dhcp/duid.cc                               |   17 +-
 src/lib/dhcp/duid.h                                |   12 +
 src/lib/dhcp/tests/duid_unittest.cc                |   79 +++++-
 src/lib/dhcpsrv/alloc_engine.cc                    |   16 ++
 src/lib/dhcpsrv/lease_mgr.h                        |    6 +-
 src/lib/dhcpsrv/mysql_lease_mgr.cc                 |   88 ++++++-
 src/lib/dhcpsrv/tests/alloc_engine_unittest.cc     |   64 ++++-
 src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc  |  115 ++++++++-
 src/lib/dhcpsrv/tests/test_utils.cc                |   18 +-
 src/lib/dns/gen-rdatacode.py.in                    |    1 +
 src/lib/dns/rdata/generic/afsdb_18.cc              |   67 +++--
 src/lib/dns/rdata/generic/afsdb_18.h               |    4 +-
 src/lib/dns/tests/rdata_afsdb_unittest.cc          |   37 ++-
 src/lib/testutils/mockups.h                        |   17 +-
 .../lettuce/features/xfrin_notify_handling.feature |   39 +++
 31 files changed, 1143 insertions(+), 151 deletions(-)

-----------------------------------------------------------------------
diff --git a/AUTHORS b/AUTHORS
index 67cb090..b915e25 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -14,6 +14,7 @@ Michael Graff
 Michal Vaner
 Mukund Sivaraman
 Naoki Kambe
+Paul Selkirk
 Shane Kerr
 Shen Tingting
 Stephen Morris
diff --git a/ChangeLog b/ChangeLog
index a981954..f479e6c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+597.	[func]		tmark
+	b10-dhcp6: Added unit tests for handling requests when no
+	IPv6 subnets are configured/defined. Testing these conditions
+	was overlooked during implementation of Trac #2719.
+	(Trac #2721, git ce7f53b2de60e2411483b4aa31c714763a36da64)
+
+596.	[bug]		jinmei
+	Added special handling for the case where b10-auth receives a
+	NOTIFY message, but zonemgr isn't running. Previously this was
+	logged as a communications problem at the ERROR level, resulting
+	in increasing noise when zonemgr is intentionally stopped. Other
+	than the log level there is no change in externally visible
+	behavior.
+	(Trac #2562, git 119eed9938b17cbad3a74c823aa9eddb7cd337c2)
+
+595.	[bug]		tomek
+	All DHCP components now gracefully refuse to handle too short
+	DUIDs and client-id.
+	(Trac #2723, git a043d8ecda6aff57922fe98a33c7c3f6155d5d64)
+
+594.	[func]		muks, pselkirk
+	libdns++: the NSEC, DS, DLV, and AFSDB Rdata classes now use the
+	generic lexer in constructors from text.  This means that the name
+	fields in such RRs in a zone file can now be non-absolute (the
+	origin name in that context will be used), e.g., when loaded by
+	b10-loadzone.
+	(Trac #2386, git dc0f34afb1eccc574421a802557198e6cd2363fa)
+	(Trac #2391, git 1450d8d486cba3bee8be46e8001d66898edd370c)
+
 593.	[func]		jelte
 	Address + port output and logs is now consistent according to our
 	coding guidelines, e.g. <address>:<port> in the case of IPv4, and
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index c9b167c..f0e9e46 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -374,12 +374,33 @@ XFRIN (Transfer-in) process.  It is issued during server startup is an
 indication that the initialization is proceeding normally.
 
 % AUTH_ZONEMGR_COMMS error communicating with zone manager: %1
-This is a debug message output during the processing of a NOTIFY request.
+This is an internal error during the processing of a NOTIFY request.
 An error (listed in the message) has been encountered whilst communicating
 with the zone manager. The NOTIFY request will not be honored.
+This may be some temporary failure, but is generally an unexpected
+event and is quite likely a bug.  It's probably worth filing a report.
 
 % AUTH_ZONEMGR_ERROR received error response from zone manager: %1
-This is a debug message output during the processing of a NOTIFY
-request. The zone manager component has been informed of the request,
+The zone manager component has been informed of the request,
 but has returned an error response (which is included in the message). The
-NOTIFY request will not be honored.
+NOTIFY request will not be honored.  As of this writing, this can only
+happen due to a bug inside the Zonemgr implementation.  Zonemgr itself
+may log more detailed cause of this, and these are probably worth
+filing a bug report.
+
+% AUTH_ZONEMGR_NOTEXIST received NOTIFY but Zonemgr does not exist
+This is a debug message produced by the authoritative server when it
+receives a NOTIFY message but the Zonemgr component is not running at
+that time.  Not running Zonemgr is completely valid for, e.g., primary
+only servers, so this is not necessarily a problem.  If this message
+is logged even if Zonemgr is supposed to be running, it's encouraged
+to check other logs to identify why that happens.  It may or may not
+be a real problem (for example, if it's immediately after the system
+startup, it's possible that Auth has started up and is running but
+Zonemgr is not yet).  Even if this is indeed an unexpected case,
+Zonemgr should normally be restarted by the Init process, so unless
+this repeats too often it may be negligible in practice (still it's
+worth filing a bug report).  In any case, the authoritative server
+simply drops the NOTIFY message; if it's a temporary failure or
+delayed startup, subsequently resent messages will eventually reach
+Zonemgr.
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 927ffe8..3fe9b9c 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -22,6 +22,7 @@
 #include <config/ccsession.h>
 
 #include <cc/data.h>
+#include <cc/proto_defs.h>
 
 #include <exceptions/exceptions.h>
 
@@ -789,18 +790,15 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
         return (true);
     }
 
-    // In the code that follows, we simply ignore the notify if any internal
-    // error happens rather than returning (e.g.) SERVFAIL.  RFC 1996 is
-    // silent about such cases, but there doesn't seem to be anything we can
-    // improve at the primary server side by sending an error anyway.
-    if (xfrin_session_ == NULL) {
-        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NO_XFRIN);
-        return (false);
-    }
-
     LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RECEIVED_NOTIFY)
         .arg(question->getName()).arg(question->getClass()).arg(remote_ep);
 
+    // xfrin_session_ should have been set and never be replaced except in
+    // tests; otherwise it's an internal bug.  assert() may be too strong,
+    // but processMessage() will catch all exceptions, so there's no better
+    // way.
+    assert(xfrin_session_);
+
     const string remote_ip_address = remote_ep.getAddress().toText();
     static const string command_template_start =
         "{\"command\": [\"notify\", {\"zone_name\" : \"";
@@ -816,12 +814,24 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
                 command_template_end);
         const unsigned int seq =
             xfrin_session_->group_sendmsg(notify_command, "Zonemgr",
-                                          "*", "*");
+                                          CC_INSTANCE_WILDCARD,
+                                          CC_INSTANCE_WILDCARD, true);
         ConstElementPtr env, answer, parsed_answer;
         xfrin_session_->group_recvmsg(env, answer, false, seq);
         int rcode;
         parsed_answer = parseAnswer(rcode, answer);
-        if (rcode != 0) {
+        if (rcode == CC_REPLY_NO_RECPT) {
+            // This can happen when Zonemgr is not running.  When we support
+            // notification-based membership framework, we should check if it's
+            // supposed to be running and shouldn't even send the command if
+            // not.  Until then, we log this event at the debug level as we
+            // don't know whether it's a real trouble or intentional
+            // configuration.  (Also, when it's done, maybe we should simply
+            // propagate the exception and return SERVFAIL to suppress further
+            // NOTIFY).
+            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_ZONEMGR_NOTEXIST);
+            return (false);
+        } else if (rcode != CC_REPLY_SUCCESS) {
             LOG_ERROR(auth_logger, AUTH_ZONEMGR_ERROR)
                       .arg(parsed_answer->str());
             return (false);
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 931d1e2..4f9424b 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -26,6 +26,8 @@
 #include <dns/rdataclass.h>
 #include <dns/tsig.h>
 
+#include <cc/proto_defs.h>
+
 #include <server_common/portconfig.h>
 #include <server_common/keyring.h>
 
@@ -868,10 +870,12 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
 }
 
-TEST_F(AuthSrvTest, notifyWithoutSession) {
+TEST_F(AuthSrvTest, notifyWithoutRecipient) {
     updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE, false);
 
-    server.setXfrinSession(NULL);
+    // Emulate the case where msgq tells auth there's no Zonemgr module.
+    notify_session.setMessage(isc::config::createAnswer(CC_REPLY_NO_RECPT,
+                                                        "no recipient"));
 
     UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                        default_qid, Name("example"),
@@ -883,6 +887,9 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
     // happens.
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
+    // want_answer should have been set to true so auth can catch it if zonemgr
+    // is not running.
+    EXPECT_TRUE(notify_session.wasAnswerWanted());
     EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
@@ -1759,7 +1766,7 @@ public:
              data_sources_.push_back(
                  DataSourceInfo(client.get(),
                                 isc::datasrc::DataSourceClientContainerPtr(),
-                                false, RRClass::IN(), ztable_segment_));
+                                false, RRClass::IN(), ztable_segment_, ""));
         }
     }
 private:
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index 5ae82a2..03b5d6b 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -148,9 +148,9 @@ class BindCmdInterpreter(Cmd):
         # is processed by a script that expects a specific format.
         if my_readline == sys.stdin.readline and sys.stdin.isatty():
             sys.stdout.write("""\
-WARNING: Python readline module isn't available, so the command line editor
-         (including command history management) does not work.  See BIND 10
-         guide for more details.\n\n""")
+WARNING: The Python readline module isn't available, so some command line
+         editing features (including command history management) will not
+         work.  See the BIND 10 guide for more details.\n\n""")
 
         try:
             if not self.login_to_cmdctl():
diff --git a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
index 6d5bd77..e7bc6ae 100644
--- a/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
+++ b/src/bin/cfgmgr/plugins/datasrc.spec.pre.in
@@ -63,6 +63,11 @@
                                     "item_optional": false,
                                     "item_default": ""
                                 }
+                            },
+                            {
+                                "item_name": "name",
+                                "item_type": "string",
+                                "item_optional": true
                             }
                         ]
                     }
diff --git a/src/bin/cfgmgr/plugins/tests/datasrc_test.py b/src/bin/cfgmgr/plugins/tests/datasrc_test.py
index 83ec4ca..3c714c6 100644
--- a/src/bin/cfgmgr/plugins/tests/datasrc_test.py
+++ b/src/bin/cfgmgr/plugins/tests/datasrc_test.py
@@ -153,6 +153,101 @@ class DatasrcTest(unittest.TestCase):
             }
         }]})
 
+    def test_names_present(self):
+        """
+        Test we don't choke on configuration with the "name" being present on
+        some items.
+        """
+        self.accept({"IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "Whatever"
+        }]})
+
+    def test_names_default_classes(self):
+        """
+        Test we can have a client of the same type in different classes
+        without specified name. The defaults should be derived both from
+        the type and the class.
+        """
+        self.accept({
+        "IN": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }],
+        "CH": [{
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+
+    def test_names_collision(self):
+        """
+        Reject when two names are the same.
+
+        Cases are:
+        - Explicit names.
+        - Two default names turn out to be the same (same type and class).
+        - One explicit is set to the same as the default one.
+        """
+        self.reject({"IN": [
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "Whatever"
+        },
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "Whatever"
+        }]})
+        # The same, but across different classes is allowed (we would
+        # identify the data source by class+name tuple)
+        self.accept({
+        "IN": [
+            {
+                "type": "MasterFiles",
+                "cache-enable": True,
+                "params": {},
+                "name": "Whatever"
+            }
+        ],
+        "CH": [
+            {
+                "type": "MasterFiles",
+                "cache-enable": True,
+                "params": {},
+                "name": "Whatever"
+            }
+        ]})
+        self.reject({"IN": [
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        },
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+        self.reject({"IN": [
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {},
+            "name": "MasterFiles"
+        },
+        {
+            "type": "MasterFiles",
+            "cache-enable": True,
+            "params": {}
+        }]})
+
 if __name__ == '__main__':
     isc.log.init("bind10")
     isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index b742a13..03b2c0c 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -79,20 +79,12 @@ public:
 
 static const char* DUID_FILE = "server-id-test.txt";
 
-class Dhcpv6SrvTest : public ::testing::Test {
+// test fixture for any tests requiring blank/empty configuration
+// serves as base class for additional tests 
+class NakedDhcpv6SrvTest : public ::testing::Test {
 public:
-    /// Name of the server-id file (used in server-id tests)
-
-    // these are empty for now, but let's keep them around
-    Dhcpv6SrvTest() : rcode_(-1) {
-        subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
-                                         2000, 3000, 4000));
-        pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
-        subnet_->addPool(pool_);
-
-        CfgMgr::instance().deleteSubnets6();
-        CfgMgr::instance().addSubnet6(subnet_);
 
+    NakedDhcpv6SrvTest() : rcode_(-1) {
         // it's ok if that fails. There should not be such a file anyway
         unlink(DUID_FILE);
     }
@@ -142,25 +134,22 @@ public:
         EXPECT_TRUE(expected_clientid->getData() == tmp->getData());
     }
 
-    // Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
-    // It returns IAADDR option for each chaining with checkIAAddr method.
-    boost::shared_ptr<Option6IAAddr> checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
-                                         uint32_t expected_t1, uint32_t expected_t2) {
-        OptionPtr tmp = rsp->getOption(D6O_IA_NA);
-        // Can't use ASSERT_TRUE() in method that returns something
-        if (!tmp) {
-            ADD_FAILURE() << "IA_NA option not present in response";
-            return (boost::shared_ptr<Option6IAAddr>());
-        }
+    // Checks if server response is a NAK
+    void checkNakResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
+                          uint32_t expected_transid, 
+                          uint16_t expected_status_code) {
+        // Check if we get response at all
+        checkResponse(rsp, expected_message_type, expected_transid);
 
-        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-        EXPECT_EQ(expected_iaid, ia->getIAID() );
-        EXPECT_EQ(expected_t1, ia->getT1());
-        EXPECT_EQ(expected_t2, ia->getT2());
+        // Check that IA_NA was returned 
+        OptionPtr option_ia_na = rsp->getOption(D6O_IA_NA);
+        ASSERT_TRUE(option_ia_na);
 
-        tmp = ia->getOption(D6O_IAADDR);
-        boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
-        return (addr);
+        // check that the status is no address available
+        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(option_ia_na);
+        ASSERT_TRUE(ia);
+
+        checkIA_NAStatusCode(ia, expected_status_code);
     }
 
     // Checks that server rejected IA_NA, i.e. that it has no addresses and
@@ -199,7 +188,6 @@ public:
         }
     }
 
-
     void checkMsgStatusCode(const Pkt6Ptr& msg, uint16_t expected_status) {
         boost::shared_ptr<OptionCustom> status =
             boost::dynamic_pointer_cast<OptionCustom>(msg->getOption(D6O_STATUS_CODE));
@@ -219,7 +207,71 @@ public:
         }
     }
 
-    // Check that generated IAADDR option contains expected address.
+    // Basic checks for generated response (message type and transaction-id).
+    void checkResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
+                       uint32_t expected_transid) {
+        ASSERT_TRUE(rsp);
+        EXPECT_EQ(expected_message_type, rsp->getType());
+        EXPECT_EQ(expected_transid, rsp->getTransid());
+    }
+
+    virtual ~NakedDhcpv6SrvTest() {
+        // Let's clean up if there is such a file.
+        unlink(DUID_FILE);
+    };
+
+    // A DUID used in most tests (typically as client-id)
+    DuidPtr duid_;
+
+    int rcode_;
+    ConstElementPtr comment_;
+};
+
+// Provides suport for tests against a preconfigured subnet6                       
+// extends upon NakedDhcp6SrvTest
+class Dhcpv6SrvTest : public NakedDhcpv6SrvTest {
+public:
+    /// Name of the server-id file (used in server-id tests)
+
+    // these are empty for now, but let's keep them around
+    Dhcpv6SrvTest() {
+        subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
+                                         2000, 3000, 4000));
+        pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+        subnet_->addPool(pool_);
+
+        CfgMgr::instance().deleteSubnets6();
+        CfgMgr::instance().addSubnet6(subnet_);
+    }
+
+    // Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
+    // It returns IAADDR option for each chaining with checkIAAddr method.
+    boost::shared_ptr<Option6IAAddr> checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                                            uint32_t expected_t1, uint32_t expected_t2) {
+        OptionPtr tmp = rsp->getOption(D6O_IA_NA);
+        // Can't use ASSERT_TRUE() in method that returns something
+        if (!tmp) {
+            ADD_FAILURE() << "IA_NA option not present in response";
+            return (boost::shared_ptr<Option6IAAddr>());
+        }
+ 
+        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+        if (!ia) {
+            ADD_FAILURE() << "IA_NA cannot convert option ptr to Option6";
+            return (boost::shared_ptr<Option6IAAddr>());
+        }
+
+        EXPECT_EQ(expected_iaid, ia->getIAID());
+        EXPECT_EQ(expected_t1, ia->getT1());
+        EXPECT_EQ(expected_t2, ia->getT2());
+ 
+        tmp = ia->getOption(D6O_IAADDR);
+        boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
+        return (addr);
+    }
+
+    // Check that generated IAADDR option contains expected address
+    // and lifetime values match the configured subnet
     void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
                      const IOAddress& expected_addr,
                      uint32_t /* expected_preferred */,
@@ -235,15 +287,8 @@ public:
         EXPECT_EQ(addr->getValid(), subnet_->getValid());
     }
 
-    // Basic checks for generated response (message type and transaction-id).
-    void checkResponse(const Pkt6Ptr& rsp, uint8_t expected_message_type,
-                       uint32_t expected_transid) {
-        ASSERT_TRUE(rsp);
-        EXPECT_EQ(expected_message_type, rsp->getType());
-        EXPECT_EQ(expected_transid, rsp->getTransid());
-    }
-
     // Checks if the lease sent to client is present in the database
+    // and is valid when checked agasint the configured subnet
     Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
                          boost::shared_ptr<Option6IAAddr> addr) {
         boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
@@ -265,9 +310,6 @@ public:
 
     ~Dhcpv6SrvTest() {
         CfgMgr::instance().deleteSubnets6();
-
-        // Let's clean up if there is such a file.
-        unlink(DUID_FILE);
     };
 
     // A subnet used in most tests
@@ -275,13 +317,132 @@ public:
 
     // A pool used in most tests
     Pool6Ptr pool_;
+};
 
-    // A DUID used in most tests (typically as client-id)
-    DuidPtr duid_;
+// This test verifies that incoming SOLICIT can be handled properly when
+// there are no subnets configured. 
+//
+// This test sends a SOLICIT and the expected response 
+// is an ADVERTISE with STATUS_NoAddrsAvail and no address provided in the 
+// response
+TEST_F(NakedDhcpv6SrvTest, SolicitNoSubnet) {
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->addOption(generateIA(234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt6Ptr reply = srv.processSolicit(sol);
+
+    // check that we get the right NAK
+    checkNakResponse (reply, DHCPV6_ADVERTISE, 1234, STATUS_NoAddrsAvail);
+}
+
+// This test verifies that incoming REQUEST can be handled properly when
+// there are no subnets configured. 
+//
+// This test sends a REQUEST and the expected response 
+// is an REPLY with STATUS_NoAddrsAvail and no address provided in the 
+// response
+TEST_F(NakedDhcpv6SrvTest, RequestNoSubnet) {
+    NakedDhcpv6Srv srv(0);
+
+    // Let's create a REQUEST
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+
+    // with a hint
+    IOAddress hint("2001:db8:1:1::dead:beef");
+    OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
+    ia->addOption(hint_opt);
+    req->addOption(ia);
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+
+    // server-id is mandatory in REQUEST
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRequest(req);
+
+    // check that we get the right NAK
+    checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoAddrsAvail);
+}
+
+// This test verifies that incoming RENEW can be handled properly, even when
+// no subnets are configured.
+//
+// This test sends a RENEW and the expected response 
+// is an REPLY with STATUS_NoBinding and no address provided in the 
+// response
+TEST_F(NakedDhcpv6SrvTest, RenewNoSubnet) {
+    NakedDhcpv6Srv srv(0);
+
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Let's create a RENEW
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+
+    OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(renewed_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
+
+    // Server-id is mandatory in RENEW
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRenew(req);
+
+    // check that we get the right NAK
+    checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
+}
+
+// This test verifies that incoming RELEASE can be handled properly, even when
+// no subnets are configured.
+//
+// This test sends a RELEASE and the expected response 
+// is an REPLY with STATUS_NoBinding and no address provided in the 
+// response
+TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
+    NakedDhcpv6Srv srv(0);
+
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Let's create a RELEASE
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
+
+    // Server-id is mandatory in RELEASE
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(req);
+
+    // check that we get the right NAK
+    checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
+}
 
-    int rcode_;
-    ConstElementPtr comment_;
-};
 
 // Test verifies that the Dhcpv6_srv class can be instantiated. It checks a mode
 // without open sockets and with sockets opened on a high port (to not require
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index 6a718ba..0a67718 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -622,7 +622,7 @@ class XfrinConnection(asyncore.dispatcher):
         it if the constructor raises an exception after opening the socket.
         '''
         self.create_socket(self._master_addrinfo[0], self._master_addrinfo[1])
-        self.setblocking(1)
+        self.socket.setblocking(1)
 
     def _get_zone_soa(self):
         '''Retrieve the current SOA RR of the zone to be transferred.
diff --git a/src/lib/asiodns/tcp_server.h b/src/lib/asiodns/tcp_server.h
index ad99446..f2faed3 100644
--- a/src/lib/asiodns/tcp_server.h
+++ b/src/lib/asiodns/tcp_server.h
@@ -23,7 +23,6 @@
 #include <boost/shared_ptr.hpp>
 
 #include <asiolink/asiolink.h>
-#include <asiolink/asiolink.h>
 #include <coroutine.h>
 #include "dns_server.h"
 #include "dns_lookup.h"
diff --git a/src/lib/cc/data.h b/src/lib/cc/data.h
index 74e5c12..d0e0d03 100644
--- a/src/lib/cc/data.h
+++ b/src/lib/cc/data.h
@@ -216,7 +216,7 @@ public:
     //@{
     /// Returns the ElementPtr at the given key
     /// \param name The key of the Element to return
-    /// \return The ElementPtr at the given key
+    /// \return The ElementPtr at the given key, or null if not present
     virtual ConstElementPtr get(const std::string& name) const;
 
     /// Sets the ElementPtr at the given key
diff --git a/src/lib/config/ccsession.h b/src/lib/config/ccsession.h
index bae25bc..15290e6 100644
--- a/src/lib/config/ccsession.h
+++ b/src/lib/config/ccsession.h
@@ -375,25 +375,29 @@ public:
         return (session_.group_sendmsg(msg, group, instance, to, want_answer));
     };
 
-    /**
-     * Receive a message from the underlying CC session.
-     * This has the same interface as isc::cc::Session::group_recvmsg()
-     *
-     * \param envelope see isc::cc::Session::group_recvmsg()
-     * \param msg see isc::cc::Session::group_recvmsg()
-     * \param nonblock see isc::cc::Session::group_recvmsg()
-     * \param seq see isc::cc::Session::group_recvmsg()
-     * \return see isc::cc::Session::group_recvmsg()
-     */
+    /// \brief Receive a message from the underlying CC session.
+    /// This has the same interface as isc::cc::Session::group_recvmsg()
+    ///
+    /// NOTE: until #2804 is resolved this method wouldn't work except in
+    /// very limited cases; don't try to use it until then.
+    ///
+    /// \param envelope see isc::cc::Session::group_recvmsg()
+    /// \param msg see isc::cc::Session::group_recvmsg()
+    /// \param nonblock see isc::cc::Session::group_recvmsg()
+    /// \param seq see isc::cc::Session::group_recvmsg()
+    /// \return see isc::cc::Session::group_recvmsg()
     bool groupRecvMsg(isc::data::ConstElementPtr& envelope,
                       isc::data::ConstElementPtr& msg,
                       bool nonblock = true,
                       int seq = -1) {
         return (session_.group_recvmsg(envelope, msg, nonblock, seq));
-    };
+    }
 
     /// \brief Send a command message and wait for the answer.
     ///
+    /// NOTE: until #2804 is resolved this method wouldn't work except in
+    /// very limited cases; don't try to use it until then.
+    ///
     /// This is mostly a convenience wrapper around groupSendMsg
     /// and groupRecvMsg, with some error handling.
     ///
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 0750fb6..eb60a96 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -27,6 +27,7 @@
 #include <util/memory_segment_local.h>
 
 #include <memory>
+#include <set>
 #include <boost/foreach.hpp>
 #include <boost/bind.hpp>
 
@@ -47,9 +48,11 @@ namespace datasrc {
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
     DataSourceClient* data_src_client,
     const DataSourceClientContainerPtr& container, bool has_cache,
-    const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment) :
+    const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment,
+    const string& name) :
     data_src_client_(data_src_client),
-    container_(container)
+    container_(container),
+    name_(name)
 {
     if (has_cache) {
         cache_.reset(new InMemoryClient(segment, rrclass));
@@ -59,8 +62,9 @@ ConfigurableClientList::DataSourceInfo::DataSourceInfo(
 
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
     const RRClass& rrclass, const shared_ptr<ZoneTableSegment>& segment,
-    bool has_cache) :
-    data_src_client_(NULL)
+    bool has_cache, const string& name) :
+    data_src_client_(NULL),
+    name_(name)
 {
     if (has_cache) {
         cache_.reset(new InMemoryClient(segment, rrclass));
@@ -92,6 +96,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
         vector<DataSourceInfo> new_data_sources;
         shared_ptr<ZoneTableSegment> ztable_segment(
             ZoneTableSegment::create(*config, rrclass_));
+        set<string> used_names;
         for (; i < config->size(); ++i) {
             // Extract the parameters
             const ConstElementPtr dconf(config->get(i));
@@ -108,6 +113,13 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
             const bool want_cache(allow_cache &&
                                   dconf->contains("cache-enable") &&
                                   dconf->get("cache-enable")->boolValue());
+            // Get the name (either explicit, or guess)
+            const ConstElementPtr name_elem(dconf->get("name"));
+            const string name(name_elem ? name_elem->stringValue() : type);
+            if (!used_names.insert(name).second) {
+                isc_throw(ConfigurationError, "Duplicit name in client list: "
+                          << name);
+            }
 
             if (type == "MasterFiles") {
                 // In case the cache is not allowed, we just skip the master
@@ -130,7 +142,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                 }
                 new_data_sources.push_back(DataSourceInfo(rrclass_,
                                                           ztable_segment,
-                                                          true));
+                                                          true, name));
             } else {
                 // Ask the factory to create the data source for us
                 const DataSourcePair ds(this->getDataSourceClient(type,
@@ -138,7 +150,8 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                 // And put it into the vector
                 new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
                                                           want_cache, rrclass_,
-                                                          ztable_segment));
+                                                          ztable_segment,
+                                                          name));
             }
 
             if (want_cache) {
@@ -462,5 +475,18 @@ ConfigurableClientList::getDataSourceClient(const string& type,
     return (DataSourcePair(&container->getInstance(), container));
 }
 
+vector<DataSourceStatus>
+ConfigurableClientList::getStatus() const {
+    vector<DataSourceStatus> result;
+    BOOST_FOREACH(const DataSourceInfo& info, data_sources_) {
+        // TODO: Once we support mapped cache, decide when we need the
+        // SEGMENT_WAITING.
+        result.push_back(DataSourceStatus(info.name_, info.cache_ ?
+                                          SEGMENT_INUSE : SEGMENT_UNUSED,
+                                          "local"));
+    }
+    return (result);
+}
+
 }
 }
diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h
index d1a35b5..256ad36 100644
--- a/src/lib/datasrc/client_list.h
+++ b/src/lib/datasrc/client_list.h
@@ -46,6 +46,76 @@ class InMemoryClient;
 class ZoneWriter;
 }
 
+/// \brief Segment status of the cache
+///
+/// Describes the status in which the memory segment for the in-memory cache of
+// /given data source is.
+enum MemorySegmentState {
+    /// \brief No segment used for this data source.
+    ///
+    /// This is usually a result of the cache being disabled.
+    SEGMENT_UNUSED,
+
+    /// \brief It is a mapped segment and we wait for information how to map
+    ///     it.
+    SEGMENT_WAITING,
+
+    /// \brief The segment is ready to be used.
+    SEGMENT_INUSE
+};
+
+/// \brief Status of one data source.
+///
+/// This indicates the status a data soure is in. It is used with segment
+/// and cache management, to discover the data sources that need external
+/// mapping or local loading.
+///
+/// In future, it may be extended for other purposes, such as performing an
+/// operation on named data source.
+class DataSourceStatus {
+public:
+    /// \brief Constructor
+    ///
+    /// Sets initial values. It doesn't matter what is provided for the type
+    /// if state is SEGMENT_UNUSED, the value is effectively ignored.
+    DataSourceStatus(const std::string& name, MemorySegmentState state,
+                     const std::string& type) :
+        name_(name),
+        type_(type),
+        state_(state)
+    {}
+
+    /// \brief Get the segment state
+    MemorySegmentState getSegmentState() const {
+        return (state_);
+    }
+
+    /// \brief Get the segment type
+    ///
+    /// \note Specific values of the type are only meaningful for the
+    ///     corresponding memory segment implementation and modules that
+    ///     directly manage the segments. Other normal applications should
+    ///     treat them as opaque identifiers.
+    ///
+    /// \throw isc::InvalidOperation if called and state is SEGMENT_UNUSED.
+    const std::string& getSegmentType() const {
+        if (getSegmentState() == SEGMENT_UNUSED) {
+            isc_throw(isc::InvalidOperation,
+                      "No segment used, no type therefore.");
+        }
+        return (type_);
+    }
+
+    /// \brief Get the name.
+    const std::string& getName() const {
+        return (name_);
+    }
+private:
+    std::string name_;
+    std::string type_;
+    MemorySegmentState state_;
+};
+
 /// \brief The list of data source clients.
 ///
 /// The purpose of this class is to hold several data source clients and search
@@ -332,13 +402,14 @@ public:
                        const boost::shared_ptr
                            <isc::datasrc::memory::ZoneTableSegment>&
                                ztable_segment,
-                       bool has_cache = false);
+                       bool has_cache = false,
+                       const std::string& name = std::string());
         DataSourceInfo(DataSourceClient* data_src_client,
                        const DataSourceClientContainerPtr& container,
                        bool has_cache, const dns::RRClass& rrclass,
                        const boost::shared_ptr
                            <isc::datasrc::memory::ZoneTableSegment>&
-                               ztable_segment);
+                               ztable_segment, const std::string& name);
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
 
@@ -350,6 +421,7 @@ public:
         const DataSourceClient* getCacheClient() const;
         boost::shared_ptr<memory::InMemoryClient> cache_;
         boost::shared_ptr<memory::ZoneTableSegment> ztable_segment_;
+        std::string name_;
     };
 
     /// \brief The collection of data sources.
@@ -379,6 +451,15 @@ public:
     virtual DataSourcePair getDataSourceClient(const std::string& type,
                                                const data::ConstElementPtr&
                                                configuration);
+
+    /// \brief Get status information of all internal data sources.
+    ///
+    /// Get a DataSourceStatus for current state of each data source client
+    /// in this list.
+    ///
+    /// This may throw standard exceptions, such as std::bad_alloc. Otherwise,
+    /// it is exception free.
+    std::vector<DataSourceStatus> getStatus() const;
 public:
     /// \brief Access to the data source clients.
     ///
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index 3d78138..bc2d4f1 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -278,7 +278,7 @@ public:
             ds_.push_back(ds);
             ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
                                    ds.get(), DataSourceClientContainerPtr(),
-                                   false, rrclass_, ztable_segment_));
+                                   false, rrclass_, ztable_segment_, ""));
         }
     }
 
@@ -512,12 +512,12 @@ TEST_F(ListTest, configureMulti) {
     const ConstElementPtr elem(Element::fromJSON("["
         "{"
         "   \"type\": \"type1\","
-        "   \"cache\": \"off\","
+        "   \"cache-enable\": false,"
         "   \"params\": {}"
         "},"
         "{"
         "   \"type\": \"type2\","
-        "   \"cache\": \"off\","
+        "   \"cache-enable\": false,"
         "   \"params\": {}"
         "}]"
     ));
@@ -546,7 +546,7 @@ TEST_F(ListTest, configureParams) {
         ConstElementPtr elem(Element::fromJSON(string("["
             "{"
             "   \"type\": \"t\","
-            "   \"cache\": \"off\","
+            "   \"cache-enable\": false,"
             "   \"params\": ") + *param +
             "}]"));
         list_->configure(elem, true);
@@ -555,6 +555,33 @@ TEST_F(ListTest, configureParams) {
     }
 }
 
+TEST_F(ListTest, status) {
+    EXPECT_TRUE(list_->getStatus().empty());
+    const ConstElementPtr elem(Element::fromJSON("["
+        "{"
+        "   \"type\": \"type1\","
+        "   \"cache-enable\": false,"
+        "   \"params\": {}"
+        "},"
+        "{"
+        "   \"type\": \"type2\","
+        "   \"cache-enable\": true,"
+        "   \"cache-zones\": [],"
+        "   \"name\": \"Test name\","
+        "   \"params\": {}"
+        "}]"
+    ));
+    list_->configure(elem, true);
+    const vector<DataSourceStatus> statuses(list_->getStatus());
+    ASSERT_EQ(2, statuses.size());
+    EXPECT_EQ("type1", statuses[0].getName());
+    EXPECT_EQ(SEGMENT_UNUSED, statuses[0].getSegmentState());
+    EXPECT_THROW(statuses[0].getSegmentType(), isc::InvalidOperation);
+    EXPECT_EQ("Test name", statuses[1].getName());
+    EXPECT_EQ(SEGMENT_INUSE, statuses[1].getSegmentState());
+    EXPECT_EQ("local", statuses[1].getSegmentType());
+}
+
 TEST_F(ListTest, wrongConfig) {
     const char* configs[] = {
         // A lot of stuff missing from there
@@ -834,6 +861,54 @@ TEST_F(ListTest, masterFiles) {
     EXPECT_EQ(0, list_->getDataSources().size());
 }
 
+// Test the names are set correctly and collission is detected.
+TEST_F(ListTest, names) {
+    // Explicit name
+    const ConstElementPtr elem1(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   },"
+        "   \"name\": \"Whatever\""
+        "}]"));
+    list_->configure(elem1, true);
+    EXPECT_EQ("Whatever", list_->getDataSources()[0].name_);
+
+    // Default name
+    const ConstElementPtr elem2(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   }"
+        "}]"));
+    list_->configure(elem2, true);
+    EXPECT_EQ("MasterFiles", list_->getDataSources()[0].name_);
+
+    // Collission
+    const ConstElementPtr elem3(Element::fromJSON("["
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   }"
+        "},"
+        "{"
+        "   \"type\": \"MasterFiles\","
+        "   \"cache-enable\": true,"
+        "   \"params\": {"
+        "       \".\": \"" TEST_DATA_DIR "/root.zone\""
+        "   },"
+        "   \"name\": \"MasterFiles\""
+        "}]"));
+    EXPECT_THROW(list_->configure(elem3, true),
+                 ConfigurableClientList::ConfigurationError);
+}
+
 TEST_F(ListTest, BadMasterFile) {
     // Configuration should succeed, and the good zones in the list
     // below should be loaded. No bad zones should be loaded.
@@ -1088,4 +1163,16 @@ TYPED_TEST(ReloadTest, reloadMasterFile) {
                                                          RRType::TXT())->code);
 }
 
+// Check the status holds data
+TEST(DataSourceStatus, status) {
+    const DataSourceStatus status("Test", SEGMENT_INUSE, "local");
+    EXPECT_EQ("Test", status.getName());
+    EXPECT_EQ(SEGMENT_INUSE, status.getSegmentState());
+    EXPECT_EQ("local", status.getSegmentType());
+    const DataSourceStatus status_unused("Unused", SEGMENT_UNUSED, "");
+    EXPECT_EQ("Unused", status_unused.getName());
+    EXPECT_EQ(SEGMENT_UNUSED, status_unused.getSegmentState());
+    EXPECT_THROW(status_unused.getSegmentType(), isc::InvalidOperation);
+}
+
 }
diff --git a/src/lib/dhcp/duid.cc b/src/lib/dhcp/duid.cc
index e0df45d..8570d26 100644
--- a/src/lib/dhcp/duid.cc
+++ b/src/lib/dhcp/duid.cc
@@ -28,15 +28,20 @@ namespace dhcp {
 DUID::DUID(const std::vector<uint8_t>& duid) {
     if (duid.size() > MAX_DUID_LEN) {
         isc_throw(OutOfRange, "DUID too large");
-    } else {
-        duid_ = duid;
     }
+    if (duid.empty()) {
+        isc_throw(OutOfRange, "Empty DUIDs are not allowed");
+    }
+    duid_ = duid;
 }
 
 DUID::DUID(const uint8_t* data, size_t len) {
     if (len > MAX_DUID_LEN) {
         isc_throw(OutOfRange, "DUID too large");
     }
+    if (len == 0) {
+        isc_throw(OutOfRange, "Empty DUIDs/Client-ids not allowed");
+    }
 
     duid_ = std::vector<uint8_t>(data, data + len);
 }
@@ -83,11 +88,19 @@ bool DUID::operator!=(const DUID& other) const {
 // Constructor based on vector<uint8_t>
 ClientId::ClientId(const std::vector<uint8_t>& clientid)
     : DUID(clientid) {
+    if (clientid.size() < MIN_CLIENT_ID_LEN) {
+        isc_throw(OutOfRange, "client-id is too short (" << clientid.size()
+                  << "), at least 2 is required");
+    }
 }
 
 // Constructor based on C-style data
 ClientId::ClientId(const uint8_t *clientid, size_t len)
     : DUID(clientid, len) {
+    if (len < MIN_CLIENT_ID_LEN) {
+        isc_throw(OutOfRange, "client-id is too short (" << len
+                  << "), at least 2 is required");
+    }
 }
 
 // Returns a copy of client-id data
diff --git a/src/lib/dhcp/duid.h b/src/lib/dhcp/duid.h
index 21c6b8f..d20a58d 100644
--- a/src/lib/dhcp/duid.h
+++ b/src/lib/dhcp/duid.h
@@ -35,6 +35,11 @@ class DUID {
     /// As defined in RFC3315, section 9.1
     static const size_t MAX_DUID_LEN = 128;
 
+    /// @brief minimum duid size
+    /// There's no explicit minimal DUID size specified in RFC3315,
+    /// so let's use absolute minimum
+    static const size_t MIN_DUID_LEN = 1;
+
     /// @brief specifies DUID type
     typedef enum {
         DUID_UNKNOWN = 0, ///< invalid/unknown type
@@ -88,6 +93,13 @@ typedef boost::shared_ptr<DUID> DuidPtr;
 /// a client-id
 class ClientId : public DUID {
 public:
+
+    /// @brief Minimum size of a client ID
+    ///
+    /// Excerpt from RFC2132, section 9.14.
+    /// The code for this option is 61, and its minimum length is 2.
+    static const size_t MIN_CLIENT_ID_LEN = 2;
+
     /// @brief Maximum size of a client ID
     ///
     /// This is the same as the maximum size of the underlying DUID.
diff --git a/src/lib/dhcp/tests/duid_unittest.cc b/src/lib/dhcp/tests/duid_unittest.cc
index 44aa561..36499c2 100644
--- a/src/lib/dhcp/tests/duid_unittest.cc
+++ b/src/lib/dhcp/tests/duid_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  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
@@ -37,6 +37,15 @@ using boost::scoped_ptr;
 
 namespace {
 
+// This is a workaround for strange linking problems with gtest:
+// libdhcp___unittests-duid_unittest.o: In function `Compare<long unsigned int, long unsigned int>':
+// ~/gtest-1.6.0/include/gtest/gtest.h:1353: undefined reference to `isc::dhcp::ClientId::MAX_CLIENT_ID_LE'N
+// collect2: ld returned 1 exit status
+
+const size_t MAX_DUID_LEN = DUID::MAX_DUID_LEN;
+const size_t MAX_CLIENT_ID_LEN = DUID::MAX_DUID_LEN;
+
+
 // This test verifies if the constructors are working as expected
 // and process passed parameters.
 TEST(DuidTest, constructor) {
@@ -61,21 +70,20 @@ TEST(DuidTest, constructor) {
 // This test verifies if DUID size restrictions are implemented
 // properly.
 TEST(DuidTest, size) {
-    const int MAX_DUID_SIZE = 128;
-    uint8_t data[MAX_DUID_SIZE + 1];
+    uint8_t data[MAX_DUID_LEN + 1];
     vector<uint8_t> data2;
-    for (uint8_t i = 0; i < MAX_DUID_SIZE + 1; ++i) {
+    for (uint8_t i = 0; i < MAX_DUID_LEN + 1; ++i) {
         data[i] = i;
-        if (i < MAX_DUID_SIZE)
+        if (i < MAX_DUID_LEN)
             data2.push_back(i);
     }
-    ASSERT_EQ(data2.size(), MAX_DUID_SIZE);
+    ASSERT_EQ(data2.size(), MAX_DUID_LEN);
 
-    scoped_ptr<DUID> duidmaxsize1(new DUID(data, MAX_DUID_SIZE));
+    scoped_ptr<DUID> duidmaxsize1(new DUID(data, MAX_DUID_LEN));
     scoped_ptr<DUID> duidmaxsize2(new DUID(data2));
 
     EXPECT_THROW(
-        scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_SIZE + 1)),
+        scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_LEN + 1)),
         OutOfRange);
 
     // that's one too much
@@ -84,6 +92,16 @@ TEST(DuidTest, size) {
     EXPECT_THROW(
         scoped_ptr<DUID> toolarge2(new DUID(data2)),
         OutOfRange);
+
+    // empty duids are not allowed
+    vector<uint8_t> empty;
+    EXPECT_THROW(
+        scoped_ptr<DUID> emptyDuid(new DUID(empty)),
+        OutOfRange);
+
+    EXPECT_THROW(
+        scoped_ptr<DUID> emptyDuid2(new DUID(data, 0)),
+        OutOfRange);
 }
 
 // This test verifies if the implementation supports all defined
@@ -157,6 +175,51 @@ TEST(ClientIdTest, constructor) {
     EXPECT_TRUE(data2 == vecdata);
 }
 
+// Check that client-id sizes are reasonable
+TEST(ClientIdTest, size) {
+    uint8_t data[MAX_CLIENT_ID_LEN + 1];
+    vector<uint8_t> data2;
+    for (uint8_t i = 0; i < MAX_CLIENT_ID_LEN + 1; ++i) {
+        data[i] = i;
+        if (i < MAX_CLIENT_ID_LEN)
+            data2.push_back(i);
+    }
+    ASSERT_EQ(data2.size(), MAX_CLIENT_ID_LEN);
+
+    scoped_ptr<ClientId> duidmaxsize1(new ClientId(data, MAX_CLIENT_ID_LEN));
+    scoped_ptr<ClientId> duidmaxsize2(new ClientId(data2));
+
+    EXPECT_THROW(
+        scoped_ptr<ClientId> toolarge1(new ClientId(data, MAX_CLIENT_ID_LEN + 1)),
+        OutOfRange);
+
+    // that's one too much
+    data2.push_back(128);
+
+    EXPECT_THROW(
+        scoped_ptr<ClientId> toolarge2(new ClientId(data2)),
+        OutOfRange);
+
+    // empty client-ids are not allowed
+    vector<uint8_t> empty;
+    EXPECT_THROW(
+        scoped_ptr<ClientId> empty_client_id1(new ClientId(empty)),
+        OutOfRange);
+
+    EXPECT_THROW(
+        scoped_ptr<ClientId> empty_client_id2(new ClientId(data, 0)),
+        OutOfRange);
+
+    // client-id must be at least 2 bytes long
+    vector<uint8_t> shorty(1,17); // just a single byte with value 17
+    EXPECT_THROW(
+        scoped_ptr<ClientId> too_short_client_id1(new ClientId(shorty)),
+        OutOfRange);
+    EXPECT_THROW(
+        scoped_ptr<ClientId> too_short_client_id1(new ClientId(data, 1)),
+        OutOfRange);
+}
+
 // This test checks if the comparison operators are sane.
 TEST(ClientIdTest, operators) {
     uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index a3df8e1..2a1bfc2 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -177,6 +177,14 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             isc_throw(InvalidOperation, "No allocator selected");
         }
 
+        if (!subnet) {
+            isc_throw(InvalidOperation, "Subnet is required for allocation");
+        }
+
+        if (!duid) {
+            isc_throw(InvalidOperation, "DUID is mandatory for allocation");
+        }
+
         // check if there's existing lease for that subnet/duid/iaid combination.
         Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
         if (existing) {
@@ -284,6 +292,14 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
             isc_throw(InvalidOperation, "No allocator selected");
         }
 
+        if (!subnet) {
+            isc_throw(InvalidOperation, "Can't allocate IPv4 address without subnet");
+        }
+
+        if (!hwaddr) {
+            isc_throw(InvalidOperation, "HWAddr must be defined");
+        }
+
         // Check if there's existing lease for that subnet/clientid/hwaddr combination.
         Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet->getID());
         if (existing) {
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index b6f7001..7d328e7 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -249,8 +249,10 @@ struct Lease4 : public Lease {
            const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
            uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id)
         : Lease(addr, t1, t2, valid_lft, subnet_id, cltt),
-        ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len),
-        client_id_(new ClientId(clientid, clientid_len)) {
+        ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len) {
+        if (clientid_len) {
+            client_id_.reset(new ClientId(clientid, clientid_len));
+        }
     }
 
     /// @brief Default constructor
diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc
index 4b5c1e1..b9706e4 100644
--- a/src/lib/dhcpsrv/mysql_lease_mgr.cc
+++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc
@@ -315,6 +315,10 @@ public:
         lease_ = lease;
 
         // Initialize prior to constructing the array of MYSQL_BIND structures.
+        // It sets all fields, including is_null, to zero, so we need to set
+        // is_null only if it should be true. This gives up minor performance
+        // benefit while being safe approach. For improved readability, the
+        // code that explicitly sets is_null is there, but is commented out.
         memset(bind_, 0, sizeof(bind_));
 
         // Set up the structures for the various components of the lease4
@@ -327,6 +331,8 @@ public:
         bind_[0].buffer_type = MYSQL_TYPE_LONG;
         bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
         bind_[0].is_unsigned = MLM_TRUE;
+        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // hwaddr: varbinary(128)
         // For speed, we avoid copying the data into temporary storage and
@@ -336,6 +342,8 @@ public:
         bind_[1].buffer = reinterpret_cast<char*>(&(lease_->hwaddr_[0]));
         bind_[1].buffer_length = hwaddr_length_;
         bind_[1].length = &hwaddr_length_;
+        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // client_id: varbinary(128)
         if (lease_->client_id_) {
@@ -345,6 +353,8 @@ public:
             bind_[2].buffer = reinterpret_cast<char*>(&client_id_[0]);
             bind_[2].buffer_length = client_id_length_;
             bind_[2].length = &client_id_length_;
+            // bind_[2].is_null = &MLM_FALSE; // commented out for performance
+                                              // reasons, see memset() above
         } else {
             bind_[2].buffer_type = MYSQL_TYPE_NULL;
 
@@ -353,15 +363,17 @@ public:
             // fields doesn't matter if type is set to MYSQL_TYPE_NULL,
             // but let's set them to some sane values in case earlier versions
             // didn't have that assumption.
-            static my_bool no_clientid = MLM_TRUE;
+            client_id_null_ = MLM_TRUE;
             bind_[2].buffer = NULL;
-            bind_[2].is_null = &no_clientid;
+            bind_[2].is_null = &client_id_null_;
         }
 
         // valid lifetime: unsigned int
         bind_[3].buffer_type = MYSQL_TYPE_LONG;
         bind_[3].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
         bind_[3].is_unsigned = MLM_TRUE;
+        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // expire: timestamp
         // The lease structure holds the client last transmission time (cltt_)
@@ -377,12 +389,16 @@ public:
         bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
         bind_[4].buffer = reinterpret_cast<char*>(&expire_);
         bind_[4].buffer_length = sizeof(expire_);
+        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // subnet_id: unsigned int
         // Can use lease_->subnet_id_ directly as it is of type uint32_t.
         bind_[5].buffer_type = MYSQL_TYPE_LONG;
         bind_[5].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
         bind_[5].is_unsigned = MLM_TRUE;
+        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
@@ -404,12 +420,18 @@ public:
     std::vector<MYSQL_BIND> createBindForReceive() {
 
         // Initialize MYSQL_BIND array.
+        // It sets all fields, including is_null, to zero, so we need to set
+        // is_null only if it should be true. This gives up minor performance
+        // benefit while being safe approach. For improved readability, the
+        // code that explicitly sets is_null is there, but is commented out.
         memset(bind_, 0, sizeof(bind_));
 
         // address:  uint32_t
         bind_[0].buffer_type = MYSQL_TYPE_LONG;
         bind_[0].buffer = reinterpret_cast<char*>(&addr4_);
         bind_[0].is_unsigned = MLM_TRUE;
+        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // hwaddr: varbinary(20)
         hwaddr_length_ = sizeof(hwaddr_buffer_);
@@ -417,6 +439,8 @@ public:
         bind_[1].buffer = reinterpret_cast<char*>(hwaddr_buffer_);
         bind_[1].buffer_length = hwaddr_length_;
         bind_[1].length = &hwaddr_length_;
+        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // client_id: varbinary(128)
         client_id_length_ = sizeof(client_id_buffer_);
@@ -424,21 +448,30 @@ public:
         bind_[2].buffer = reinterpret_cast<char*>(client_id_buffer_);
         bind_[2].buffer_length = client_id_length_;
         bind_[2].length = &client_id_length_;
+        bind_[2].is_null = &client_id_null_;
+        // bind_[2].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // lease_time: unsigned int
         bind_[3].buffer_type = MYSQL_TYPE_LONG;
         bind_[3].buffer = reinterpret_cast<char*>(&valid_lifetime_);
         bind_[3].is_unsigned = MLM_TRUE;
+        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // expire: timestamp
         bind_[4].buffer_type = MYSQL_TYPE_TIMESTAMP;
         bind_[4].buffer = reinterpret_cast<char*>(&expire_);
         bind_[4].buffer_length = sizeof(expire_);
+        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // subnet_id: unsigned int
         bind_[5].buffer_type = MYSQL_TYPE_LONG;
         bind_[5].buffer = reinterpret_cast<char*>(&subnet_id_);
         bind_[5].is_unsigned = MLM_TRUE;
+        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
@@ -465,6 +498,11 @@ public:
         time_t cltt = 0;
         MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
 
+        if (client_id_null_==MLM_TRUE) {
+            // There's no client-id, so we pass client-id_length_ set to 0
+            client_id_length_ = 0;
+        }
+
         // note that T1 and T2 are not stored
         return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
                                      client_id_buffer_, client_id_length_,
@@ -502,6 +540,8 @@ private:
     uint8_t         client_id_buffer_[ClientId::MAX_CLIENT_ID_LEN];
                                         ///< Client ID buffer
     unsigned long   client_id_length_;  ///< Client ID address length
+    my_bool         client_id_null_;    ///< Is Client ID null?
+
     MYSQL_TIME      expire_;            ///< Lease expiry time
     Lease4Ptr       lease_;             ///< Pointer to lease object
     uint32_t        subnet_id_;         ///< Subnet identification
@@ -564,6 +604,10 @@ public:
 
         // Ensure bind_ array clear for constructing the MYSQL_BIND structures
         // for this lease.
+        // It sets all fields, including is_null, to zero, so we need to set
+        // is_null only if it should be true. This gives up minor performance
+        // benefit while being safe approach. For improved readability, the
+        // code that explicitly sets is_null is there, but is commented out.
         memset(bind_, 0, sizeof(bind_));
 
         // address: varchar(39)
@@ -588,6 +632,8 @@ public:
         bind_[0].buffer = const_cast<char*>(addr6_.c_str());
         bind_[0].buffer_length = addr6_length_;
         bind_[0].length = &addr6_length_;
+        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // duid: varchar(128)
         duid_ = lease_->duid_->getDuid();
@@ -597,11 +643,15 @@ public:
         bind_[1].buffer = reinterpret_cast<char*>(&(duid_[0]));
         bind_[1].buffer_length = duid_length_;
         bind_[1].length = &duid_length_;
+        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // valid lifetime: unsigned int
         bind_[2].buffer_type = MYSQL_TYPE_LONG;
         bind_[2].buffer = reinterpret_cast<char*>(&lease_->valid_lft_);
         bind_[2].is_unsigned = MLM_TRUE;
+        // bind_[2].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // expire: timestamp
         // The lease structure holds the client last transmission time (cltt_)
@@ -616,18 +666,24 @@ public:
         bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
         bind_[3].buffer = reinterpret_cast<char*>(&expire_);
         bind_[3].buffer_length = sizeof(expire_);
+        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // subnet_id: unsigned int
         // Can use lease_->subnet_id_ directly as it is of type uint32_t.
         bind_[4].buffer_type = MYSQL_TYPE_LONG;
         bind_[4].buffer = reinterpret_cast<char*>(&lease_->subnet_id_);
         bind_[4].is_unsigned = MLM_TRUE;
+        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // pref_lifetime: unsigned int
         // Can use lease_->preferred_lft_ directly as it is of type uint32_t.
         bind_[5].buffer_type = MYSQL_TYPE_LONG;
         bind_[5].buffer = reinterpret_cast<char*>(&lease_->preferred_lft_);
         bind_[5].is_unsigned = MLM_TRUE;
+        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // lease_type: tinyint
         // Must convert to uint8_t as lease_->type_ is a LeaseType variable.
@@ -635,18 +691,24 @@ public:
         bind_[6].buffer_type = MYSQL_TYPE_TINY;
         bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
         bind_[6].is_unsigned = MLM_TRUE;
+        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // iaid: unsigned int
         // Can use lease_->iaid_ directly as it is of type uint32_t.
         bind_[7].buffer_type = MYSQL_TYPE_LONG;
         bind_[7].buffer = reinterpret_cast<char*>(&lease_->iaid_);
         bind_[7].is_unsigned = MLM_TRUE;
+        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // prefix_len: unsigned tinyint
         // Can use lease_->prefixlen_ directly as it is uint32_t.
         bind_[8].buffer_type = MYSQL_TYPE_TINY;
         bind_[8].buffer = reinterpret_cast<char*>(&lease_->prefixlen_);
         bind_[8].is_unsigned = MLM_TRUE;
+        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
@@ -670,6 +732,10 @@ public:
     std::vector<MYSQL_BIND> createBindForReceive() {
 
         // Initialize MYSQL_BIND array.
+        // It sets all fields, including is_null, to zero, so we need to set
+        // is_null only if it should be true. This gives up minor performance
+        // benefit while being safe approach. For improved readability, the
+        // code that explicitly sets is_null is there, but is commented out.
         memset(bind_, 0, sizeof(bind_));
 
         // address:  varchar(39)
@@ -681,6 +747,8 @@ public:
         bind_[0].buffer = addr6_buffer_;
         bind_[0].buffer_length = addr6_length_;
         bind_[0].length = &addr6_length_;
+        // bind_[0].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // client_id: varbinary(128)
         duid_length_ = sizeof(duid_buffer_);
@@ -688,41 +756,57 @@ public:
         bind_[1].buffer = reinterpret_cast<char*>(duid_buffer_);
         bind_[1].buffer_length = duid_length_;
         bind_[1].length = &duid_length_;
+        // bind_[1].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // lease_time: unsigned int
         bind_[2].buffer_type = MYSQL_TYPE_LONG;
         bind_[2].buffer = reinterpret_cast<char*>(&valid_lifetime_);
         bind_[2].is_unsigned = MLM_TRUE;
+        // bind_[2].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // expire: timestamp
         bind_[3].buffer_type = MYSQL_TYPE_TIMESTAMP;
         bind_[3].buffer = reinterpret_cast<char*>(&expire_);
         bind_[3].buffer_length = sizeof(expire_);
+        // bind_[3].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // subnet_id: unsigned int
         bind_[4].buffer_type = MYSQL_TYPE_LONG;
         bind_[4].buffer = reinterpret_cast<char*>(&subnet_id_);
         bind_[4].is_unsigned = MLM_TRUE;
+        // bind_[4].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // pref_lifetime: unsigned int
         bind_[5].buffer_type = MYSQL_TYPE_LONG;
         bind_[5].buffer = reinterpret_cast<char*>(&pref_lifetime_);
         bind_[5].is_unsigned = MLM_TRUE;
+        // bind_[5].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // lease_type: tinyint
         bind_[6].buffer_type = MYSQL_TYPE_TINY;
         bind_[6].buffer = reinterpret_cast<char*>(&lease_type_);
         bind_[6].is_unsigned = MLM_TRUE;
+        // bind_[6].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // iaid: unsigned int
         bind_[7].buffer_type = MYSQL_TYPE_LONG;
         bind_[7].buffer = reinterpret_cast<char*>(&iaid_);
         bind_[7].is_unsigned = MLM_TRUE;
+        // bind_[7].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // prefix_len: unsigned tinyint
         bind_[8].buffer_type = MYSQL_TYPE_TINY;
         bind_[8].buffer = reinterpret_cast<char*>(&prefixlen_);
         bind_[8].is_unsigned = MLM_TRUE;
+        // bind_[8].is_null = &MLM_FALSE; // commented out for performance
+                                          // reasons, see memset() above
 
         // Add the error flags
         setErrorIndicators(bind_, error_, LEASE_COLUMNS);
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index ddc0f62..144d7b5 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -160,7 +160,15 @@ public:
         EXPECT_EQ(subnet_->getT2(), lease->t2_);
         EXPECT_TRUE(false == lease->fqdn_fwd_);
         EXPECT_TRUE(false == lease->fqdn_rev_);
-        EXPECT_TRUE(*lease->client_id_ == *clientid_);
+        if (lease->client_id_ && !clientid_) {
+            ADD_FAILURE() << "Lease4 has a client-id, while it should have none.";
+        } else
+        if (!lease->client_id_ && clientid_) {
+            ADD_FAILURE() << "Lease4 has no client-id, but it was expected to have one.";
+        } else
+        if (lease->client_id_ && clientid_) {
+            EXPECT_TRUE(*lease->client_id_ == *clientid_);
+        }
         EXPECT_TRUE(lease->hwaddr_ == hwaddr_->hwaddr_);
         // @todo: check cltt
      }
@@ -329,6 +337,24 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
     detailCompareLease(lease, from_mgr);
 }
 
+// This test checks that NULL values are handled properly
+TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    // Allocations without subnet are not allowed
+    Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
+                                               IOAddress("::"), false);
+    ASSERT_FALSE(lease);
+
+    // Allocations without DUID are not allowed either
+    lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
+                                     IOAddress("::"), false);
+    ASSERT_FALSE(lease);
+}
+
+
 // This test verifies that the allocator picks addresses that belong to the
 // pool
 TEST_F(AllocEngine6Test, IterativeAllocator) {
@@ -702,6 +728,42 @@ TEST_F(AllocEngine4Test, allocBogusHint4) {
 }
 
 
+// This test checks that NULL values are handled properly
+TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    // Allocations without subnet are not allowed
+    Lease4Ptr lease = engine->allocateAddress4(SubnetPtr(), clientid_, hwaddr_,
+                                               IOAddress("0.0.0.0"), false);
+    EXPECT_FALSE(lease);
+
+    // Allocations without HW address are not allowed
+    lease = engine->allocateAddress4(subnet_, clientid_, HWAddrPtr(),
+                                     IOAddress("0.0.0.0"), false);
+    EXPECT_FALSE(lease);
+
+    // Allocations without client-id are allowed
+    clientid_ = ClientIdPtr();
+    lease = engine->allocateAddress4(subnet_, ClientIdPtr(), hwaddr_,
+                                     IOAddress("0.0.0.0"), false);
+    // Check that we got a lease
+    ASSERT_TRUE(lease);
+
+    // Do all checks on the lease
+    checkLease4(lease);
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    detailCompareLease(lease, from_mgr);
+}
+
+
+
 // This test verifies that the allocator picks addresses that belong to the
 // pool
 TEST_F(AllocEngine4Test, IterativeAllocator) {
diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
index 2ae6a49..226afe9 100644
--- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
@@ -307,8 +307,7 @@ public:
 
         } else if (address == straddress4_[7]) {
             lease->hwaddr_ = vector<uint8_t>();             // Empty
-            lease->client_id_ = ClientIdPtr(
-                new ClientId(vector<uint8_t>()));           // Empty
+            lease->client_id_ = ClientIdPtr();              // Empty
             lease->valid_lft_ = 7975;
             lease->cltt_ = 213876;
             lease->subnet_id_ = 19;
@@ -702,6 +701,83 @@ TEST_F(MySqlLeaseMgrTest, basicLease4) {
     detailCompareLease(leases[2], l_returned);
 }
 
+/// @brief Basic Lease4 Checks
+///
+/// Checks that the addLease, getLease4(by address), getLease4(hwaddr,subnet_id),
+/// updateLease4() and deleteLease (IPv4 address) can handle NULL client-id.
+/// (client-id is optional and may not be present)
+TEST_F(MySqlLeaseMgrTest, lease4NullClientId) {
+    // Get the leases to be used for the test.
+    vector<Lease4Ptr> leases = createLeases4();
+
+    // Let's clear client-id pointers
+    leases[1]->client_id_ = ClientIdPtr();
+    leases[2]->client_id_ = ClientIdPtr();
+    leases[3]->client_id_ = ClientIdPtr();
+
+    // Start the tests.  Add three leases to the database, read them back and
+    // check they are what we think they are.
+    EXPECT_TRUE(lmptr_->addLease(leases[1]));
+    EXPECT_TRUE(lmptr_->addLease(leases[2]));
+    EXPECT_TRUE(lmptr_->addLease(leases[3]));
+    lmptr_->commit();
+
+    // Reopen the database to ensure that they actually got stored.
+    reopen();
+
+    Lease4Ptr l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+    l_returned = lmptr_->getLease4(ioaddress4_[3]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[3], l_returned);
+
+    // Check that we can't add a second lease with the same address
+    EXPECT_FALSE(lmptr_->addLease(leases[1]));
+
+    // Check that we can get the lease by HWAddr
+    HWAddr tmp(leases[2]->hwaddr_, HTYPE_ETHER);
+    Lease4Collection returned = lmptr_->getLease4(tmp);
+    ASSERT_EQ(1, returned.size());
+    detailCompareLease(leases[2], *returned.begin());
+
+    l_returned = lmptr_->getLease4(tmp, leases[2]->subnet_id_);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+
+    // Check that we can update the lease
+    // Modify some fields in lease 1 (not the address) and update it.
+    ++leases[1]->subnet_id_;
+    leases[1]->valid_lft_ *= 2;
+    lmptr_->updateLease4(leases[1]);
+
+    // ... and check that the lease is indeed updated
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[1], l_returned);
+
+
+
+    // Delete a lease, check that it's gone, and that we can't delete it
+    // a second time.
+    EXPECT_TRUE(lmptr_->deleteLease(ioaddress4_[1]));
+    l_returned = lmptr_->getLease4(ioaddress4_[1]);
+    EXPECT_FALSE(l_returned);
+    EXPECT_FALSE(lmptr_->deleteLease(ioaddress4_[1]));
+
+    // Check that the second address is still there.
+    l_returned = lmptr_->getLease4(ioaddress4_[2]);
+    ASSERT_TRUE(l_returned);
+    detailCompareLease(leases[2], l_returned);
+
+}
+
 /// @brief Basic Lease6 Checks
 ///
 /// Checks that the addLease, getLease6 (by address) and deleteLease (with an
@@ -781,14 +857,14 @@ TEST_F(MySqlLeaseMgrTest, getLease4Hwaddr) {
     // Repeat test with just one expected match
     // @todo: Simply use HWAddr directly once 2589 is implemented
     returned = lmptr_->getLease4(HWAddr(leases[2]->hwaddr_, HTYPE_ETHER));
-    EXPECT_EQ(1, returned.size());
+    ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[2], *returned.begin());
 
     // Check that an empty vector is valid
     EXPECT_TRUE(leases[7]->hwaddr_.empty());
     // @todo: Simply use HWAddr directly once 2589 is implemented
     returned = lmptr_->getLease4(HWAddr(leases[7]->hwaddr_, HTYPE_ETHER));
-    EXPECT_EQ(1, returned.size());
+    ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[7], *returned.begin());
 
     // Try to get something with invalid hardware address
@@ -958,13 +1034,14 @@ TEST_F(MySqlLeaseMgrTest, getLease4ClientId) {
 
     // Repeat test with just one expected match
     returned = lmptr_->getLease4(*leases[3]->client_id_);
-    EXPECT_EQ(1, returned.size());
+    ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[3], *returned.begin());
 
-    // Check that an empty vector is valid
-    EXPECT_TRUE(leases[7]->client_id_->getClientId().empty());
-    returned = lmptr_->getLease4(leases[7]->hwaddr_);
-    EXPECT_EQ(1, returned.size());
+    // Check that client-id is NULL
+    EXPECT_FALSE(leases[7]->client_id_);
+    HWAddr tmp(leases[7]->hwaddr_, HTYPE_ETHER);
+    returned = lmptr_->getLease4(tmp);
+    ASSERT_EQ(1, returned.size());
     detailCompareLease(leases[7], *returned.begin());
 
     // Try to get something with invalid client ID
@@ -988,7 +1065,11 @@ TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSize) {
     // ClientId::MAX_CLIENT_ID_LEN is used in an EXPECT_EQ.
     int client_id_max = ClientId::MAX_CLIENT_ID_LEN;
     EXPECT_EQ(128, client_id_max);
-    for (uint8_t i = 0; i <= client_id_max; i += 16) {
+
+    int client_id_min = ClientId::MIN_CLIENT_ID_LEN;
+    EXPECT_EQ(2, client_id_min); // See RFC2132, section 9.14
+
+    for (uint8_t i = client_id_min; i <= client_id_max; i += 16) {
         vector<uint8_t> clientid_vec(i, i);
         leases[1]->client_id_.reset(new ClientId(clientid_vec));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
@@ -1097,13 +1178,17 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
     // For speed, go from 0 to 128 is steps of 16.
     int duid_max = DUID::MAX_DUID_LEN;
     EXPECT_EQ(128, duid_max);
-    for (uint8_t i = 0; i <= duid_max; i += 16) {
+
+    int duid_min = DUID::MIN_DUID_LEN;
+    EXPECT_EQ(1, duid_min);
+
+    for (uint8_t i = duid_min; i <= duid_max; i += 16) {
         vector<uint8_t> duid_vec(i, i);
         leases[1]->duid_.reset(new DUID(duid_vec));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
         Lease6Collection returned = lmptr_->getLease6(*leases[1]->duid_,
                                                       leases[1]->iaid_);
-        EXPECT_EQ(1, returned.size());
+        ASSERT_EQ(1, returned.size());
         detailCompareLease(leases[1], *returned.begin());
         (void) lmptr_->deleteLease(leases[1]->addr_);
     }
@@ -1162,7 +1247,11 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
     // For speed, go from 0 to 128 is steps of 16.
     int duid_max = DUID::MAX_DUID_LEN;
     EXPECT_EQ(128, duid_max);
-    for (uint8_t i = 0; i <= duid_max; i += 16) {
+
+    int duid_min = DUID::MIN_DUID_LEN;
+    EXPECT_EQ(1, duid_min);
+
+    for (uint8_t i = duid_min; i <= duid_max; i += 16) {
         vector<uint8_t> duid_vec(i, i);
         leases[1]->duid_.reset(new DUID(duid_vec));
         EXPECT_TRUE(lmptr_->addLease(leases[1]));
diff --git a/src/lib/dhcpsrv/tests/test_utils.cc b/src/lib/dhcpsrv/tests/test_utils.cc
index 3c69dbe..ea62225 100644
--- a/src/lib/dhcpsrv/tests/test_utils.cc
+++ b/src/lib/dhcpsrv/tests/test_utils.cc
@@ -27,7 +27,23 @@ detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) {
     // thrown for IPv6 addresses.
     EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
     EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
-    EXPECT_TRUE(*first->client_id_ == *second->client_id_);
+    if (first->client_id_ && second->client_id_) {
+        EXPECT_TRUE(*first->client_id_ == *second->client_id_);
+    } else {
+        if (first->client_id_ && !second->client_id_) {
+
+            ADD_FAILURE() << "Client-id present in first lease ("
+                          << first->client_id_->getClientId().size()
+                          << " bytes), but missing in second.";
+        }
+        if (!first->client_id_ && second->client_id_) {
+            ADD_FAILURE() << "Client-id missing in first lease, but present in second ("
+                          << second->client_id_->getClientId().size()
+                          << " bytes).";
+        }
+        // else here would mean that both leases do not have client_id_
+        // which makes them equal in that regard. It is ok.
+    }
     EXPECT_EQ(first->valid_lft_, second->valid_lft_);
     EXPECT_EQ(first->cltt_, second->cltt_);
     EXPECT_EQ(first->subnet_id_, second->subnet_id_);
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index d0a2576..0d946b9 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -33,6 +33,7 @@ import sys
 # Example:
 #     new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
 new_rdata_factory_users = [('a', 'in'), ('aaaa', 'in'),
+                           ('afsdb', 'generic'),
                            ('cname', 'generic'),
                            ('dlv', 'generic'),
                            ('dname', 'generic'),
diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc
index ec76ee0..cf6a35c 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.cc
+++ b/src/lib/dns/rdata/generic/afsdb_18.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  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
@@ -25,9 +25,12 @@
 
 #include <boost/lexical_cast.hpp>
 
+#include <dns/rdata/generic/detail/lexer_util.h>
+
 using namespace std;
+using boost::lexical_cast;
 using namespace isc::util;
-using namespace isc::util::str;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -52,24 +55,58 @@ using namespace isc::util::str;
 AFSDB::AFSDB(const std::string& afsdb_str) :
     subtype_(0), server_(Name::ROOT_NAME())
 {
-    istringstream iss(afsdb_str);
-
     try {
-        const uint32_t subtype = tokenToNum<int32_t, 16>(getToken(iss));
-        const Name servername(getToken(iss));
+        std::istringstream ss(afsdb_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
 
-        if (!iss.eof()) {
-            isc_throw(InvalidRdataText, "Unexpected input for AFSDB"
-                    "RDATA: " << afsdb_str);
+        createFromLexer(lexer, NULL);
+
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText, "extra input text for AFSDB: "
+                      << afsdb_str);
         }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText, "Failed to construct AFSDB from '" <<
+                  afsdb_str << "': " << ex.what());
+    }
+}
 
-        subtype_ = subtype;
-        server_ = servername;
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual representation
+/// of an AFSDB RDATA.  The SERVER field can be non-absolute if \c origin
+/// is non-NULL, in which case \c origin is used to make it absolute.
+/// It must not be represented as a quoted string.
+///
+/// The SUBTYPE field must be a valid decimal representation of an
+/// unsigned 16-bit integer.
+///
+/// \throw MasterLexer::LexerError General parsing error such as missing field.
+/// \throw Other Exceptions from the Name and RRTTL constructors if
+/// construction of textual fields as these objects fail.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+/// \param origin If non NULL, specifies the origin of SERVER when it
+/// is non-absolute.
+AFSDB::AFSDB(MasterLexer& lexer, const Name* origin,
+       MasterLoader::Options, MasterLoaderCallbacks&) :
+    subtype_(0), server_(".")
+{
+    createFromLexer(lexer, origin);
+}
 
-    } catch (const StringTokenError& ste) {
-        isc_throw(InvalidRdataText, "Invalid AFSDB text: " <<
-                  ste.what() << ": " << afsdb_str);
+void
+AFSDB::createFromLexer(MasterLexer& lexer, const Name* origin)
+{
+    const uint32_t num = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (num > 65535) {
+        isc_throw(InvalidRdataText, "Invalid AFSDB subtype: " << num);
     }
+    subtype_ = static_cast<uint16_t>(num);
+
+    server_ = createNameFromLexer(lexer, origin);
 }
 
 /// \brief Constructor from wire-format data.
@@ -111,7 +148,7 @@ AFSDB::operator=(const AFSDB& source) {
 /// \return A \c string object that represents the \c AFSDB object.
 string
 AFSDB::toText() const {
-    return (boost::lexical_cast<string>(subtype_) + " " + server_.toText());
+    return (lexical_cast<string>(subtype_) + " " + server_.toText());
 }
 
 /// \brief Render the \c AFSDB in the wire format without name compression.
diff --git a/src/lib/dns/rdata/generic/afsdb_18.h b/src/lib/dns/rdata/generic/afsdb_18.h
index 4a46775..359a6e4 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.h
+++ b/src/lib/dns/rdata/generic/afsdb_18.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  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
@@ -61,6 +61,8 @@ public:
     uint16_t getSubtype() const;
 
 private:
+    void createFromLexer(MasterLexer& lexer, const Name* origin);
+
     uint16_t subtype_;
     Name server_;
 };
diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc
index 9bb64b7..9a628cd 100644
--- a/src/lib/dns/tests/rdata_afsdb_unittest.cc
+++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  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
@@ -34,7 +34,7 @@ using namespace isc::dns::rdata;
 const char* const afsdb_text = "1 afsdb.example.com.";
 const char* const afsdb_text2 = "0 root.example.com.";
 const char* const too_long_label("012345678901234567890123456789"
-        "0123456789012345678901234567890123");
+        "0123456789012345678901234567890123.");
 
 namespace {
 class Rdata_AFSDB_Test : public RdataTest {
@@ -68,9 +68,17 @@ TEST_F(Rdata_AFSDB_Test, badText) {
     // number of fields (must be 2) is incorrect
     EXPECT_THROW(const generic::AFSDB rdata_afsdb("10 afsdb. example.com."),
                  InvalidRdataText);
+    // No origin and relative
+    EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com"),
+                 MissingNameOrigin);
     // bad name
     EXPECT_THROW(const generic::AFSDB rdata_afsdb("1 afsdb.example.com." +
-                string(too_long_label)), TooLongLabel);
+                 string(too_long_label)), TooLongLabel);
+}
+
+TEST_F(Rdata_AFSDB_Test, copy) {
+    const generic::AFSDB rdata_afsdb2(rdata_afsdb);
+    EXPECT_EQ(0, rdata_afsdb.compare(rdata_afsdb2));
 }
 
 TEST_F(Rdata_AFSDB_Test, assignment) {
@@ -119,9 +127,24 @@ TEST_F(Rdata_AFSDB_Test, createFromLexer) {
         *test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
                                      afsdb_text)));
 
+    // test::createRdataUsingLexer() constructs relative to
+    // "example.org." origin.
+    generic::AFSDB tmp = generic::AFSDB("1 afsdb2.example.org.");
+    EXPECT_EQ(0, tmp.compare(
+        *test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
+                                     "1 afsdb2")));
+
     // Exceptions cause NULL to be returned.
     EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
                                              "1root.example.com."));
+
+    // 65536 is larger than maximum possible subtype
+    EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
+                                             "65536 afsdb.example.com."));
+
+    // Extra text at end of line
+    EXPECT_FALSE(test::createRdataUsingLexer(RRType::AFSDB(), RRClass::IN(),
+                                             "1 afsdb.example.com. extra."));
 }
 
 TEST_F(Rdata_AFSDB_Test, toWireBuffer) {
@@ -197,9 +220,9 @@ TEST_F(Rdata_AFSDB_Test, compare) {
     EXPECT_EQ(0, rdata_afsdb.compare(generic::AFSDB("1 "
                                 "AFSDB.example.com.")));
 
-    const generic::AFSDB small1("10 afsdb.example.com");
-    const generic::AFSDB large1("65535 afsdb.example.com");
-    const generic::AFSDB large2("256 afsdb.example.com");
+    const generic::AFSDB small1("10 afsdb.example.com.");
+    const generic::AFSDB large1("65535 afsdb.example.com.");
+    const generic::AFSDB large2("256 afsdb.example.com.");
 
     // confirm these are compared as unsigned values
     EXPECT_GT(0, rdata_afsdb.compare(large1));
@@ -210,7 +233,7 @@ TEST_F(Rdata_AFSDB_Test, compare) {
     EXPECT_LT(0, large2.compare(small1));
 
     // another AFSDB whose server name is larger than that of rdata_afsdb.
-    const generic::AFSDB large3("256 zzzzz.example.com");
+    const generic::AFSDB large3("256 zzzzz.example.com.");
     EXPECT_GT(0, large2.compare(large3));
     EXPECT_LT(0, large3.compare(large2));
 
diff --git a/src/lib/testutils/mockups.h b/src/lib/testutils/mockups.h
index 8ba2287..58b39ee 100644
--- a/src/lib/testutils/mockups.h
+++ b/src/lib/testutils/mockups.h
@@ -40,15 +40,16 @@ public:
     MockSession() :
         // by default we return a simple "success" message.
         msg_(isc::data::Element::fromJSON("{\"result\": [0, \"SUCCESS\"]}")),
-        send_ok_(true), receive_ok_(true)
+        send_ok_(true), receive_ok_(true), answer_wanted_(false)
     {}
 
 
     virtual void establish(const char*) {}
     virtual void disconnect() {}
 
-    virtual int group_sendmsg(isc::data::ConstElementPtr msg, std::string group,
-                              std::string, std::string, bool)
+    virtual int group_sendmsg(isc::data::ConstElementPtr msg,
+                              std::string group,
+                              std::string, std::string, bool want_answer)
     {
         if (!send_ok_) {
             isc_throw(isc::cc::SessionError,
@@ -57,6 +58,7 @@ public:
 
         sent_msg_ = msg;
         msg_dest_ = group;
+        answer_wanted_ = want_answer;
         return (0);
     }
 
@@ -93,8 +95,12 @@ public:
     void disableSend() { send_ok_ = false; }
     void disableReceive() { receive_ok_ = false; }
 
-    isc::data::ConstElementPtr getSentMessage() { return (sent_msg_); }
-    std::string getMessageDest() { return (msg_dest_); }
+    isc::data::ConstElementPtr getSentMessage() const { return (sent_msg_); }
+    std::string getMessageDest() const { return (msg_dest_); }
+
+    /// \brief Return the value of want_answer parameter of the previous call
+    /// to group_sendmsg().
+    bool wasAnswerWanted() const { return (answer_wanted_); }
 
 private:
     isc::data::ConstElementPtr sent_msg_;
@@ -102,6 +108,7 @@ private:
     isc::data::ConstElementPtr msg_;
     bool send_ok_;
     bool receive_ok_;
+    bool answer_wanted_;
 };
 
 // This mock object does nothing except for recording passed parameters
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index 81378b9..a17ce42 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -334,3 +334,42 @@ Feature: Xfrin incoming notify handling
     Then wait for new master stderr message NOTIFY_OUT_REPLY_RECEIVED
 
     A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+
+    #
+    # Test for NOTIFY when zonemgr is not running
+    #
+    Scenario: Handle incoming notify while zonemgr is not running
+    Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+    And wait for master stderr message BIND10_STARTED_CC
+    And wait for master stderr message CMDCTL_STARTED
+    And wait for master stderr message AUTH_SERVER_STARTED
+    And wait for master stderr message XFROUT_STARTED
+    And wait for master stderr message ZONEMGR_STARTED
+    And wait for master stderr message STATS_STARTING
+
+    And I have bind10 running with configuration xfrin/retransfer_slave_notify.conf
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message AUTH_SERVER_STARTED
+    And wait for bind10 stderr message XFRIN_STARTED
+    And wait for bind10 stderr message ZONEMGR_STARTED
+
+    # remove zonemgr from the system.  a subsequent notify is ignored, but
+    # an error message shouldn't be logged at auth.
+    When I send bind10 the following commands with cmdctl
+    """
+    config remove Init/components b10-zonemgr
+    config commit
+    """
+    last bindctl output should not contain "error"
+    And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+
+    A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN
+
+    When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
+    Then wait for master stderr message XFROUT_NOTIFY_COMMAND
+    Then wait for new bind10 stderr message AUTH_RECEIVED_NOTIFY
+    Then wait for new bind10 stderr message AUTH_ZONEMGR_NOTEXIST not AUTH_ZONEMGR_ERROR
+    Then wait for master stderr message NOTIFY_OUT_TIMEOUT not NOTIFY_OUT_REPLY_RECEIVED
+
+    A query for www.example.org to [::1]:47806 should have rcode NXDOMAIN



More information about the bind10-changes mailing list