BIND 10 master, updated. ee0950bcf935175ebed77a407ad6881e2b482bef Merge branch 'master' into trac2108_3

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Sep 17 12:26:14 UTC 2012


The branch, master has been updated
       via  ee0950bcf935175ebed77a407ad6881e2b482bef (commit)
       via  70f25f4ac5d30d17d87c8be3b138d29c081eaee4 (commit)
       via  6919cf078b8864752a438346b662ace8d4acaa59 (commit)
       via  65ce434abe243852100803da6f0ba086f55fda5b (commit)
       via  7f4c06a3fcbbff054ff5227b7b3b60f865b8ed46 (commit)
       via  2de58c844cbf6ab10fa9bbe2e45b09badcd3101b (commit)
       via  514cf93463cd28391d257672726077717580a9c3 (commit)
       via  56afaa4d876b6af0814e396c95d9375305878737 (commit)
       via  e3f72ac2caea7a75deb1b772d9681d6be3aad203 (commit)
       via  d976012056c29d07ad3ca563b7870a0ea07ca531 (commit)
       via  f1f2184dc4023a2cdd4157aefd3bdafdb164d586 (commit)
       via  dd5485e6973a8ebc6fc99727ac3bc8903c8fce3b (commit)
       via  4171ba94fead572dbc4de66f9a35380680d55fa7 (commit)
       via  8716a098a9205bf1aeae4dd61d7c2e4fb4956c47 (commit)
       via  eb66b3c19f083d918a536b422376217851896d0f (commit)
       via  5bd8df4b94383665c2c6de44436b3eeedae7f2d4 (commit)
       via  a727e666876aa6ec02cc9d736b2735c4d021fca4 (commit)
       via  f60ea8d945a1c503670418c7e3f6cc57e5b82ee1 (commit)
       via  c531154142d7466163731429af8050ede78bc423 (commit)
       via  72f8b8a0db78bab56f8efb0f344fc8961f9adadc (commit)
       via  a376d4a632d9510ba8f4175b4221b245b812d65b (commit)
       via  f4ceb192ddc140409230eef5ecc55a13f04059f5 (commit)
       via  e898d07a4540cc9783779f0f39ddcf8264419068 (commit)
       via  083e6836264daff5da8aa95dff7927d0f37f0e43 (commit)
       via  3f0eb8fec78587398baa9964c344330b77a37f9e (commit)
       via  41eff487aee5315bdb63b805d1e3e9efe13118f4 (commit)
       via  2d47ae95847e1088b1458f7d099093d2f5c8d9ef (commit)
       via  46286a6732d6adf6f1640ad4ad6b7360a279fea4 (commit)
       via  15bdb89898addbb7149479a083af674c545755d7 (commit)
       via  86d8c6421097faeb05a2abc1638c61c44913bd27 (commit)
       via  5a70502ad2679d135e26db9f3eb0a523eefd83e6 (commit)
       via  0a9b4b2a90c9021bb875ace73a82b5507d92f08a (commit)
       via  caf572ed4757d33ee4b849de2a4745dcbe04dc66 (commit)
       via  f476f78a1e47392cb1b2b95c7d1d424b099bea95 (commit)
       via  22add5b6c6441fab79e0fd43b44d0caade159f69 (commit)
       via  f0be7a0795ab7a93bf7aa8e1c6b48a738441e179 (commit)
       via  26abb2819f66a4a247ce1ebf77831d3979c85018 (commit)
       via  b9c899265d584b9399afc061e431c9a35af276b6 (commit)
       via  d4026adcbe1cf9c969b2cff4064b2ebc5ff85b54 (commit)
       via  cf9ebfb24e25d6a166f1773ce000b93623b937f1 (commit)
       via  6be43cd5166ec9429bac140ced8405c368301262 (commit)
       via  cc5d46563e11e7c987265f1be51c908e3bca4fb7 (commit)
       via  0f8bc280b95e71f7b5095dea586c84868c852d7c (commit)
       via  11fbb68418d52e4ac94ebbb8297013336e54779c (commit)
       via  35057f3b488579a7ad147c18ae20581ca5866eda (commit)
       via  18141332274dfbe3cdaa941d47b02bf0ca89fbb6 (commit)
       via  1fe06c9434a24cc93f6c9301c9c3f0a512c40ffc (commit)
       via  330f8ec209cc50bd19da3f2548370f24e6a3641f (commit)
       via  2dce3cb62a8f3c39bf9b4f364bdf6ae42a0e3433 (commit)
       via  79e4f78c803a819ebe940c2b0ae6916b6e7b5933 (commit)
       via  e1f7b3f3c9fa167a285257f5452e44684d1e9c3c (commit)
       via  97cd696da6a3fbfbd4a395ee67ef4f5958e63099 (commit)
       via  c080ae50b5cdb5d5d0840c3828cdcb64c53b15a2 (commit)
       via  02c01e5d1d6ff331405f9622e26df72cd9651864 (commit)
       via  6ed1c019aa98e461b4cd2860a1e206750891e92b (commit)
       via  cf353d1ae86e495f335d46f8153bac81f00b90c9 (commit)
       via  c326d49966e90613660925224e957ad586574145 (commit)
       via  fb45c7ee68b9354991fb0c5244b2b3535dad73af (commit)
       via  bff38dc7a6eae26d1b6178088197bb1467746377 (commit)
       via  fb5704f3df5b34c1a8630650257ccd2317d784dc (commit)
       via  7a3b73ce16268558c72244b91f08d4768a55133c (commit)
       via  105b19f75e8d0158a1370faae8ac4dd99ed0752c (commit)
       via  37ef5a70b967ee810f9082bfa596f0f806ca4fd0 (commit)
       via  723fcfb3c12c9cb0abd5d5354fb083f6d1aaa853 (commit)
       via  365c64286ae8724ed8af98af64228db8e605b30d (commit)
       via  1af7d120c3e58a3f503902d50ab5d3201c5cd2f9 (commit)
       via  b5efb671ff4bf5a570e00a626464eb7b97f9c8ae (commit)
       via  222eefb4bd062a21bfac115a2d058655593836bc (commit)
       via  1e1882f13e8e2739f394c5642878b32397cfd4f8 (commit)
       via  4f132f47001567b8b9f48147546bc714dd28739f (commit)
       via  94670cafdbca56d6bf2708e83fa9aa07295f579c (commit)
       via  4da836d3f6d756fe91ae42fd5cb83ec282f3dc33 (commit)
       via  e9d6cf4587af29f3120b7b41a8174a19334719c0 (commit)
       via  92850a8f2cbcd9dd08882fc92c3db5fe11f8a32f (commit)
       via  d06f56460ccb91743e5e09a22b5051e6c2c7de8c (commit)
       via  f8b817744f6207f0aa8d997c7faf35b520383adc (commit)
       via  91ab8fe87a310dd610a38c8ff7813963baf5d576 (commit)
       via  64b3b91a2685a351317a43999fc26316099f70b3 (commit)
       via  753931a085b6df6b0e1e2a7a7530e3e8f6152459 (commit)
       via  32c4d7fa5d90e76a0e81b12be856f83563f20039 (commit)
       via  ea0b94856e40284e6a91005963f26999c7c0d86c (commit)
       via  9158d6d575838ace538c7cb23318a525c1b2732e (commit)
       via  90b10c0f2a7b7843ecc74961e1d8ee491df69d33 (commit)
       via  21ff0c5ddf62e42a6221a716ae0f79023301b71d (commit)
       via  42cd408242643ec58c3865fb1ad80a2c9340a874 (commit)
       via  b3310cab0f5e156a7cde66921b5151099346a366 (commit)
       via  0523c84f2a8a2e32fa5d831b32eec5a59325c119 (commit)
       via  a879e6b86253e1179cf95b2a2d65a3c08e29eb93 (commit)
       via  0f42cb55b2bfdefcea88ed14bfa1c21f2e4b4cc5 (commit)
       via  d5e7b615047b7e7d32b6ea9da3db1d3fb0573154 (commit)
       via  d475b8b1a6cd56cdc049cafc1e17311b41baffb2 (commit)
       via  12a6f524849941a162bc814ea9797e0d15eafdac (commit)
       via  63eba8528685e5c19e54503ef6ab6ed1c1fbbe25 (commit)
       via  7e46f0a77c389e5af64cc5fe7f7a8ca50e79957c (commit)
       via  38db4f895183880f827ae0c24f2eb602860b2ca6 (commit)
       via  9d1a6c855efa69033fce89cd1d9c640757324696 (commit)
       via  31a1014180ca193b7bf27c754974fc6166ad648e (commit)
       via  cc90d4ea448ab2297fb80ef8880601020ad4a885 (commit)
       via  f573cd7f9148766399605dec1b6b42852095bc63 (commit)
       via  8b1a6f28fc6238001d66d8aa16348efb7dfd14e1 (commit)
       via  d074626ee7d65dee4011443bbf0be2b16bb44411 (commit)
       via  372c18dac614fd8cfbe0e55bcf3aaf1aa6749ef4 (commit)
       via  0aa28c0f46b3882585fbb739a322053fa14f68b6 (commit)
       via  558aff98969b401f7505bcc69fd8a1356e157084 (commit)
       via  0c4fd3055fe7714ded8851090c4b7b2490136f32 (commit)
       via  c13e1fbaea4f33b0385ffb5bdb23a3fcefda7391 (commit)
       via  65282c50effd9e5a6c846ac50707b73df97381c3 (commit)
       via  1628d5fc238c4d72c43bf7e7c4a535ecfb7c2777 (commit)
       via  a10c87fb37c4383640a47b358b80211a81dad742 (commit)
       via  36acee537796e3a57b2dc96600b5ae55e4acf169 (commit)
       via  1c1627f4887f854c26a2ac00122718bdd7a0016c (commit)
       via  ad45b4e2de7386663a828889d3d09db547b74cb5 (commit)
       via  c396c0b44213c1f1c236e254fcd34708498498fe (commit)
       via  99db6f4f616fb7ab7d66147c661f0efd544e69f1 (commit)
       via  9be7fe08ea3b0833049fea4e34a96a992f174329 (commit)
       via  120561d3bf2290360dd5e2b341cfc306017c2db4 (commit)
       via  429f2e7c9a622ee39ee964b469bd9946f63c1c13 (commit)
       via  896c0de84e16201a4c682bc9d895359f41813422 (commit)
       via  f2623367b1289645a0440faf590ceb8a3e417f55 (commit)
       via  061fbc119bccfb8cf5afe7eb3e8a6228d8956168 (commit)
       via  d1c78d4a09351ccb7f8aab34f74fe5245ba590d6 (commit)
       via  bba5b86a7cbeb0400d5edab44b63a54cc9848a0a (commit)
       via  bd22127826c2286adfc28583c8f14c688391dc2c (commit)
       via  ae9a4acc8822f21b32a89ebf5504e79f2f37d5c8 (commit)
       via  57a9640dbcaeade5e45744c479456489aad353b3 (commit)
      from  68ebb92883a7cd9bb84b3b39cab2049b59119e36 (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 ee0950bcf935175ebed77a407ad6881e2b482bef
Merge: 70f25f4 68ebb92
Author: Mukund Sivaraman <muks at isc.org>
Date:   Mon Sep 17 17:47:13 2012 +0530

    Merge branch 'master' into trac2108_3
    
    Conflicts fixed:
    	src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
    	src/lib/datasrc/memory/treenode_rrset.cc
    	src/lib/datasrc/memory/treenode_rrset.h

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

Summary of changes:
 configure.ac                                       |    1 +
 src/lib/datasrc/memory/.gitignore                  |    2 +
 src/lib/datasrc/memory/Makefile.am                 |   13 +-
 src/lib/datasrc/memory/benchmarks/.gitignore       |    1 +
 src/lib/{asiodns => datasrc/memory}/logger.cc      |   10 +-
 src/lib/datasrc/{ => memory}/logger.h              |   30 +-
 src/lib/datasrc/memory/memory_client.cc            |  911 ++++++++++++++++++++
 .../{memory_datasrc.h => memory/memory_client.h}   |  357 +++-----
 src/lib/datasrc/memory/memory_messages.mes         |   90 ++
 src/lib/datasrc/memory/tests/Makefile.am           |    5 +
 .../datasrc/memory/tests/memory_client_unittest.cc |  740 ++++++++++++++++
 src/lib/datasrc/memory/tests/run_unittests.cc      |    3 +
 src/lib/datasrc/memory/tests/testdata/Makefile.am  |   32 +
 .../lib/datasrc/memory/tests/testdata/empty.zone   |    0
 .../memory/tests/testdata/example.org-broken1.zone |    1 +
 .../memory/tests/testdata/example.org-broken2.zone |    5 +
 .../testdata/example.org-cname-and-not-nsec-1.zone |    4 +
 .../testdata/example.org-cname-and-not-nsec-2.zone |    4 +
 .../testdata/example.org-dname-ns-apex-1.zone      |    4 +
 .../testdata/example.org-dname-ns-apex-2.zone      |    4 +
 .../testdata/example.org-dname-ns-nonapex-1.zone   |    4 +
 .../testdata/example.org-dname-ns-nonapex-2.zone   |    4 +
 .../testdata/example.org-duplicate-type-bad.zone   |    4 +
 .../tests/testdata/example.org-duplicate-type.zone |    4 +
 .../memory/tests/testdata/example.org-empty.zone   |    2 +
 .../tests/testdata/example.org-multiple-cname.zone |    3 +
 .../tests/testdata/example.org-multiple-dname.zone |    3 +
 .../tests/testdata/example.org-multiple-nsec3.zone |    3 +
 .../testdata/example.org-multiple-nsec3param.zone  |    3 +
 .../tests/testdata/example.org-multiple.zone       |    4 +
 .../testdata/example.org-nsec3-fewer-labels.zone   |    3 +
 .../testdata/example.org-nsec3-more-labels.zone    |    3 +
 .../example.org-nsec3-signed-no-param.zone}        |    0
 .../tests/testdata/example.org-nsec3-signed.zone}  |    0
 .../tests/testdata/example.org-out-of-zone.zone    |    5 +
 .../example.org-rrsig-follows-nothing.zone         |    5 +
 .../testdata/example.org-rrsig-name-unmatched.zone |    6 +
 .../testdata/example.org-rrsig-type-unmatched.zone |    6 +
 .../memory/tests/testdata/example.org-rrsigs.zone  |    8 +
 .../tests/testdata/example.org-wildcard-dname.zone |    4 +
 .../tests/testdata/example.org-wildcard-ns.zone    |    4 +
 .../tests/testdata/example.org-wildcard-nsec3.zone |    4 +
 .../tests/testdata/example.org.zone}               |    0
 .../memory/tests/treenode_rrset_unittest.cc        |   17 +-
 src/lib/datasrc/memory/treenode_rrset.cc           |    8 +-
 src/lib/datasrc/memory/treenode_rrset.h            |    7 +-
 src/lib/datasrc/memory/zone_data.h                 |    1 +
 src/lib/datasrc/memory/zone_table.cc               |   14 +
 src/lib/datasrc/memory/zone_table.h                |   14 +-
 49 files changed, 2096 insertions(+), 264 deletions(-)
 create mode 100644 src/lib/datasrc/memory/.gitignore
 copy src/lib/{asiodns => datasrc/memory}/logger.cc (87%)
 copy src/lib/datasrc/{ => memory}/logger.h (66%)
 create mode 100644 src/lib/datasrc/memory/memory_client.cc
 copy src/lib/datasrc/{memory_datasrc.h => memory/memory_client.h} (52%)
 create mode 100644 src/lib/datasrc/memory/memory_messages.mes
 create mode 100644 src/lib/datasrc/memory/tests/memory_client_unittest.cc
 create mode 100644 src/lib/datasrc/memory/tests/testdata/Makefile.am
 copy NEWS => src/lib/datasrc/memory/tests/testdata/empty.zone (100%)
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
 copy src/lib/datasrc/{tests/testdata/example.org.nsec3-signed-noparam => memory/tests/testdata/example.org-nsec3-signed-no-param.zone} (100%)
 copy src/lib/datasrc/{tests/testdata/example.org.nsec3-signed => memory/tests/testdata/example.org-nsec3-signed.zone} (100%)
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
 create mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
 copy src/lib/datasrc/{tests/testdata/contexttest.zone => memory/tests/testdata/example.org.zone} (100%)

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 2ead4b7..9d7b7ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1188,6 +1188,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/memory/Makefile
                  src/lib/datasrc/memory/tests/Makefile
+                 src/lib/datasrc/memory/tests/testdata/Makefile
                  src/lib/datasrc/memory/benchmarks/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
diff --git a/src/lib/datasrc/memory/.gitignore b/src/lib/datasrc/memory/.gitignore
new file mode 100644
index 0000000..fbb6381
--- /dev/null
+++ b/src/lib/datasrc/memory/.gitignore
@@ -0,0 +1,2 @@
+/memory_messages.cc
+/memory_messages.h
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index b04647c..168c2ab 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -6,7 +6,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
-CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
+CLEANFILES = *.gcno *.gcda
 
 noinst_LTLIBRARIES = libdatasrc_memory.la
 
@@ -17,4 +17,15 @@ libdatasrc_memory_la_SOURCES += rdata_serialization.h rdata_serialization.cc
 libdatasrc_memory_la_SOURCES += zone_data.h zone_data.cc
 libdatasrc_memory_la_SOURCES += segment_object_holder.h
 libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
+libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
+libdatasrc_memory_la_SOURCES += logger.h logger.cc
+nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
+
 EXTRA_DIST  = rdata_serialization_priv.cc
+
+BUILT_SOURCES = memory_messages.h memory_messages.cc
+memory_messages.h memory_messages.cc: Makefile memory_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/datasrc/memory/memory_messages.mes
+
+EXTRA_DIST += memory_messages.mes
+CLEANFILES += memory_messages.h memory_messages.cc
diff --git a/src/lib/datasrc/memory/benchmarks/.gitignore b/src/lib/datasrc/memory/benchmarks/.gitignore
index ea243a4..8db9170 100644
--- a/src/lib/datasrc/memory/benchmarks/.gitignore
+++ b/src/lib/datasrc/memory/benchmarks/.gitignore
@@ -1 +1,2 @@
 /rdata_reader_bench
+/rrset_render_bench
diff --git a/src/lib/datasrc/memory/logger.cc b/src/lib/datasrc/memory/logger.cc
new file mode 100644
index 0000000..59c93bf
--- /dev/null
+++ b/src/lib/datasrc/memory/logger.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/logger.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+isc::log::Logger logger("datasrc_memory");
+
+}
+}
+}
diff --git a/src/lib/datasrc/memory/logger.h b/src/lib/datasrc/memory/logger.h
new file mode 100644
index 0000000..554ba7b
--- /dev/null
+++ b/src/lib/datasrc/memory/logger.h
@@ -0,0 +1,52 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_LOGGER_H
+#define DATASRC_MEMORY_LOGGER_H
+
+#include <log/macros.h>
+#include <datasrc/memory/memory_messages.h>
+
+/// \file datasrc/memory/logger.h
+/// \brief Data Source memory library global logger
+///
+/// This holds the logger for the data source memory library. It is a
+/// private header and should not be included in any publicly used
+/// header, only in local cc files.
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief The logger for this library
+extern isc::log::Logger logger;
+
+/// \brief Trace basic operations
+const int DBG_TRACE_BASIC = DBGLVL_TRACE_BASIC;
+
+/// \brief Trace data changes and lookups as well
+const int DBG_TRACE_DATA = DBGLVL_TRACE_BASIC_DATA;
+
+/// \brief Detailed even about how the lookups happen
+const int DBG_TRACE_DETAILED = DBGLVL_TRACE_DETAIL;
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_LOGGER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
new file mode 100644
index 0000000..1674095
--- /dev/null
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -0,0 +1,911 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <datasrc/memory/memory_client.h>
+#include <datasrc/memory/logger.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/domaintree.h>
+#include <datasrc/memory/segment_object_holder.h>
+#include <datasrc/memory/treenode_rrset.h>
+
+#include <util/memory_segment_local.h>
+
+#include <datasrc/data_source.h>
+#include <datasrc/factory.h>
+#include <datasrc/result.h>
+
+#include <dns/name.h>
+#include <dns/nsec3hash.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrsetlist.h>
+#include <dns/masterload.h>
+
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+#include <cctype>
+#include <cassert>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using boost::scoped_ptr;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+using detail::SegmentObjectHolder;
+
+namespace {
+// Some type aliases
+typedef DomainTree<std::string> FileNameTree;
+typedef DomainTreeNode<std::string> FileNameNode;
+
+// A functor type used for loading.
+typedef boost::function<void(ConstRRsetPtr)> LoadCallback;
+
+} // end of anonymous namespace
+
+/// Implementation details for \c InMemoryClient hidden from the public
+/// interface.
+///
+/// For now, \c InMemoryClient only contains a \c ZoneTable object, which
+/// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more
+/// member variables later for new features.
+class InMemoryClient::InMemoryClientImpl {
+private:
+    // The deleter for the filenames stored in the tree.
+    struct FileNameDeleter {
+        FileNameDeleter() {}
+        void operator()(std::string* filename) const {
+            delete filename;
+        }
+    };
+
+public:
+    InMemoryClientImpl(util::MemorySegment& mem_sgmt,
+                       RRClass rrclass) :
+        local_mem_sgmt_(mem_sgmt),
+        rrclass_(rrclass),
+        zone_count_(0),
+        zone_table_(ZoneTable::create(local_mem_sgmt_, rrclass)),
+        file_name_tree_(FileNameTree::create(local_mem_sgmt_, false))
+    {}
+    ~InMemoryClientImpl() {
+        FileNameDeleter deleter;
+        FileNameTree::destroy(local_mem_sgmt_, file_name_tree_, deleter);
+
+        ZoneTable::destroy(local_mem_sgmt_, zone_table_, rrclass_);
+
+        // see above for the assert().
+        assert(local_mem_sgmt_.allMemoryDeallocated());
+    }
+
+    // Memory segment to allocate/deallocate memory for the zone table.
+    // (This will eventually have to be abstract; for now we hardcode the
+    // specific derived segment class).
+    util::MemorySegment& local_mem_sgmt_;
+    const RRClass rrclass_;
+    unsigned int zone_count_;
+    ZoneTable* zone_table_;
+    FileNameTree* file_name_tree_;
+    ConstRRsetPtr last_rrset_;
+
+    // Common process for zone load.
+    // rrset_installer is a functor that takes another functor as an argument,
+    // and expected to call the latter for each RRset of the zone.  How the
+    // sequence of the RRsets is generated depends on the internal
+    // details  of the loader: either from a textual master file or from
+    // another data source.
+    // filename is the file name of the master file or empty if the zone is
+    // loaded from another data source.
+    result::Result load(const Name& zone_name, const string& filename,
+                        boost::function<void(LoadCallback)> rrset_installer);
+
+    // Add the necessary magic for any wildcard contained in 'name'
+    // (including itself) to be found in the zone.
+    //
+    // In order for wildcard matching to work correctly in the zone finder,
+    // we must ensure that a node for the wildcarding level exists in the
+    // backend RBTree.
+    // E.g. if the wildcard name is "*.sub.example." then we must ensure
+    // that "sub.example." exists and is marked as a wildcard level.
+    // Note: the "wildcarding level" is for the parent name of the wildcard
+    // name (such as "sub.example.").
+    //
+    // We also perform the same trick for empty wild card names possibly
+    // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
+    void addWildcards(const Name& zone_name, ZoneData& zone_data,
+                      const Name& name)
+    {
+        Name wname(name);
+        const unsigned int labels(wname.getLabelCount());
+        const unsigned int origin_labels(zone_name.getLabelCount());
+        for (unsigned int l = labels;
+             l > origin_labels;
+             --l, wname = wname.split(1)) {
+            if (wname.isWildcard()) {
+                LOG_DEBUG(logger, DBG_TRACE_DATA,
+                          DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name);
+
+                // Ensure a separate level exists for the "wildcarding" name,
+                // and mark the node as "wild".
+                ZoneNode *node;
+                zone_data.insertName(local_mem_sgmt_, wname.split(1), &node);
+                node->setFlag(ZoneData::WILDCARD_NODE);
+
+                // Ensure a separate level exists for the wildcard name.
+                // Note: for 'name' itself we do this later anyway, but the
+                // overhead should be marginal because wildcard names should
+                // be rare.
+                zone_data.insertName(local_mem_sgmt_, wname, &node);
+            }
+        }
+    }
+
+    /*
+     * Does some checks in context of the data that are already in the zone.
+     * Currently checks for forbidden combinations of RRsets in the same
+     * domain (CNAME+anything, DNAME+NS).
+     *
+     * If such condition is found, it throws AddError.
+     */
+    void contextCheck(const Name& zone_name, const AbstractRRset& rrset,
+                      const RdataSet* set) const {
+        // Ensure CNAME and other type of RR don't coexist for the same
+        // owner name except with NSEC, which is the only RR that can coexist
+        // with CNAME (and also RRSIG, which is handled separately)
+        if (rrset.getType() == RRType::CNAME()) {
+            for (const RdataSet* sp = set; sp != NULL; sp = sp->getNext()) {
+                if (sp->type != RRType::NSEC()) {
+                    LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY).
+                        arg(rrset.getName());
+                    isc_throw(AddError, "CNAME can't be added with "
+                              << sp->type << " RRType for "
+                              << rrset.getName());
+                }
+            }
+        } else if ((rrset.getType() != RRType::NSEC()) &&
+                   (RdataSet::find(set, RRType::CNAME()) != NULL)) {
+            LOG_ERROR(logger,
+                      DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName());
+            isc_throw(AddError, "CNAME and " << rrset.getType() <<
+                      " can't coexist for " << rrset.getName());
+        }
+
+        /*
+         * Similar with DNAME, but it must not coexist only with NS and only in
+         * non-apex domains.
+         * RFC 2672 section 3 mentions that it is implied from it and RFC 2181
+         */
+        if (rrset.getName() != zone_name &&
+            // Adding DNAME, NS already there
+            ((rrset.getType() == RRType::DNAME() &&
+              RdataSet::find(set, RRType::NS()) != NULL) ||
+            // Adding NS, DNAME already there
+            (rrset.getType() == RRType::NS() &&
+             RdataSet::find(set, RRType::DNAME()) != NULL)))
+        {
+            LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName());
+            isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
+                "domain " << rrset.getName());
+        }
+    }
+
+    // Validate rrset before adding it to the zone.  If something is wrong
+    // it throws an exception.  It doesn't modify the zone, and provides
+    // the strong exception guarantee.
+    void addValidation(const Name& zone_name, const ConstRRsetPtr rrset) {
+        if (!rrset) {
+            isc_throw(NullRRset, "The rrset provided is NULL");
+        }
+        if (rrset->getRdataCount() == 0) {
+            isc_throw(AddError, "The rrset provided is empty: " <<
+                      rrset->getName() << "/" << rrset->getType());
+        }
+        // Check for singleton RRs. It should probably handled at a different
+        // layer in future.
+        if ((rrset->getType() == RRType::CNAME() ||
+            rrset->getType() == RRType::DNAME()) &&
+            rrset->getRdataCount() > 1)
+        {
+            // XXX: this is not only for CNAME or DNAME. We should generalize
+            // this code for all other "singleton RR types" (such as SOA) in a
+            // separate task.
+            LOG_ERROR(logger,
+                      DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()).
+                arg(rrset->getType());
+            isc_throw(AddError, "multiple RRs of singleton type for "
+                      << rrset->getName());
+        }
+        // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
+        // implementation requests it be so at the moment.
+        if ((rrset->getType() == RRType::NSEC3() ||
+             rrset->getType() == RRType::NSEC3PARAM()) &&
+            rrset->getRdataCount() > 1) {
+            isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
+                      << rrset->getName() << " which isn't supported");
+        }
+
+        NameComparisonResult compare(zone_name.compare(rrset->getName()));
+        if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
+            compare.getRelation() != NameComparisonResult::EQUAL)
+        {
+            LOG_ERROR(logger,
+                      DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()).
+                arg(zone_name);
+            isc_throw(OutOfZone, "The name " << rrset->getName() <<
+                " is not contained in zone " << zone_name);
+        }
+
+        // Some RR types do not really work well with a wildcard.
+        // Even though the protocol specifically doesn't completely ban such
+        // usage, we refuse to load a zone containing such RR in order to
+        // keep the lookup logic simpler and more predictable.
+        // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
+        // for more technical background.  Note also that BIND 9 refuses
+        // NS at a wildcard, so in that sense we simply provide compatible
+        // behavior.
+        if (rrset->getName().isWildcard()) {
+            if (rrset->getType() == RRType::NS()) {
+                LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
+                    arg(rrset->getName());
+                isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
+                          rrset->getName());
+            }
+            if (rrset->getType() == RRType::DNAME()) {
+                LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME).
+                    arg(rrset->getName());
+                isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
+                          rrset->getName());
+            }
+        }
+
+        // Owner names of NSEC3 have special format as defined in RFC5155,
+        // and cannot be a wildcard name or must be one label longer than
+        // the zone origin.  While the RFC doesn't prohibit other forms of
+        // names, no sane zone would have such names for NSEC3.
+        // BIND 9 also refuses NSEC3 at wildcard.
+        if (rrset->getType() == RRType::NSEC3() &&
+            (rrset->getName().isWildcard() ||
+             rrset->getName().getLabelCount() !=
+             zone_name.getLabelCount() + 1)) {
+            LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME).
+                arg(rrset->getName());
+            isc_throw(AddError, "Invalid NSEC3 owner name: " <<
+                      rrset->getName());
+        }
+    }
+
+    void addNSEC3(const ConstRRsetPtr rrset,
+                  const ConstRRsetPtr rrsig,
+                  ZoneData& zone_data) {
+        // We know rrset has exactly one RDATA
+        const generic::NSEC3& nsec3_rdata =
+            dynamic_cast<const generic::NSEC3&>(
+                rrset->getRdataIterator()->getCurrent());
+
+        NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
+        if (nsec3_data == NULL) {
+            nsec3_data = NSEC3Data::create(local_mem_sgmt_, nsec3_rdata);
+            zone_data.setNSEC3Data(nsec3_data);
+        } else {
+            size_t salt_len = nsec3_data->getSaltLen();
+            const uint8_t* salt_data = nsec3_data->getSaltData();
+            const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
+
+            if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
+                (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
+                (salt_data_2.size() != salt_len) ||
+                (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
+                isc_throw(AddError,
+                          "NSEC3 with inconsistent parameters: " <<
+                          rrset->toText());
+            }
+        }
+
+        string fst_label = rrset->getName().split(0, 1).toText(true);
+        transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
+                  ::toupper);
+
+        ZoneNode *node;
+        nsec3_data->insertName(local_mem_sgmt_, Name(fst_label), &node);
+
+        RdataEncoder encoder;
+
+        // We assume that rrsig has already been checked to match rrset
+        // by the caller.
+        RdataSet *set = RdataSet::create(local_mem_sgmt_, encoder,
+                                         rrset, rrsig);
+        RdataSet *old_set = node->setData(set);
+        if (old_set != NULL) {
+            RdataSet::destroy(local_mem_sgmt_, rrclass_, old_set);
+        }
+    }
+
+    void addRdataSet(const Name& zone_name, ZoneData& zone_data,
+                     const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig) {
+        // Only one of these can be passed at a time.
+        assert(!(rrset && rrsig));
+
+        // If rrsig is passed, validate it against the last-saved rrset.
+        if (rrsig) {
+            // The covered RRset should have been saved by now.
+            if (!last_rrset_) {
+                isc_throw(AddError,
+                          "RRSIG is being added, "
+                          "but doesn't follow its covered RR: "
+                          << rrsig->getName());
+            }
+
+            if (rrsig->getName() != last_rrset_->getName()) {
+                isc_throw(AddError,
+                          "RRSIG is being added, "
+                          "but doesn't match the last RR's name: "
+                          << last_rrset_->getName() << " vs. "
+                          << rrsig->getName());
+            }
+
+            // Consistency of other types in rrsig are checked in addRRsig().
+            RdataIteratorPtr rit = rrsig->getRdataIterator();
+            const RRType covered = dynamic_cast<const generic::RRSIG&>(
+                rit->getCurrent()).typeCovered();
+
+            if (covered != last_rrset_->getType()) {
+                isc_throw(AddError,
+                          "RRSIG is being added, "
+                          "but doesn't match the last RR's type: "
+                          << last_rrset_->getType() << " vs. "
+                          << covered);
+            }
+        }
+
+        if (!last_rrset_) {
+            last_rrset_ = rrset;
+            return;
+        }
+
+        if (last_rrset_->getType() == RRType::NSEC3()) {
+            addNSEC3(last_rrset_, rrsig, zone_data);
+        } else {
+            ZoneNode* node;
+            zone_data.insertName(local_mem_sgmt_,
+                                 last_rrset_->getName(), &node);
+
+            RdataSet* set = node->getData();
+
+            // Checks related to the surrounding data.
+            // Note: when the check fails and the exception is thrown,
+            // it may break strong exception guarantee.  At the moment
+            // we prefer code simplicity and don't bother to introduce
+            // complicated recovery code.
+            contextCheck(zone_name, *last_rrset_, set);
+
+            if (RdataSet::find(set, last_rrset_->getType()) != NULL) {
+                isc_throw(AddError,
+                          "RRset of the type already exists: "
+                          << last_rrset_->getName() << " (type: "
+                          << last_rrset_->getType() << ")");
+            }
+
+            RdataEncoder encoder;
+            RdataSet *new_set = RdataSet::create(local_mem_sgmt_, encoder,
+                                                 last_rrset_, rrsig);
+            new_set->next = set;
+            node->setData(new_set);
+
+            // Ok, we just put it in
+
+            // If this RRset creates a zone cut at this node, mark the
+            // node indicating the need for callback in find().
+            if (last_rrset_->getType() == RRType::NS() &&
+                last_rrset_->getName() != zone_name) {
+                node->setFlag(ZoneNode::FLAG_CALLBACK);
+                // If it is DNAME, we have a callback as well here
+            } else if (last_rrset_->getType() == RRType::DNAME()) {
+                node->setFlag(ZoneNode::FLAG_CALLBACK);
+            }
+
+            // If we've added NSEC3PARAM at zone origin, set up NSEC3
+            // specific data or check consistency with already set up
+            // parameters.
+            if (last_rrset_->getType() == RRType::NSEC3PARAM() &&
+                last_rrset_->getName() == zone_name) {
+                // We know rrset has exactly one RDATA
+                const generic::NSEC3PARAM& param =
+                    dynamic_cast<const generic::NSEC3PARAM&>
+                      (last_rrset_->getRdataIterator()->getCurrent());
+
+                NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
+                if (nsec3_data == NULL) {
+                    nsec3_data = NSEC3Data::create(local_mem_sgmt_, param);
+                    zone_data.setNSEC3Data(nsec3_data);
+                } else {
+                    size_t salt_len = nsec3_data->getSaltLen();
+                    const uint8_t* salt_data = nsec3_data->getSaltData();
+                    const vector<uint8_t>& salt_data_2 = param.getSalt();
+
+                    if ((param.getHashalg() != nsec3_data->hashalg) ||
+                        (param.getIterations() != nsec3_data->iterations) ||
+                        (salt_data_2.size() != salt_len) ||
+                        (std::memcmp(&salt_data_2[0],
+                                     salt_data, salt_len) != 0)) {
+                        isc_throw(AddError,
+                                  "NSEC3PARAM with inconsistent parameters: "
+                                  << last_rrset_->toText());
+                    }
+                }
+            } else if (last_rrset_->getType() == RRType::NSEC()) {
+                // If it is NSEC signed zone, so we put a flag there
+                // (flag is enough)
+                zone_data.setSigned(true);
+            }
+        }
+
+        last_rrset_ = rrset;
+    }
+
+    result::Result addRRsig(const ConstRRsetPtr sig_rrset,
+                            const Name& zone_name, ZoneData& zone_data)
+    {
+        // Check consistency of the type covered.
+        // We know the RRset isn't empty, so the following check is safe.
+        RdataIteratorPtr rit = sig_rrset->getRdataIterator();
+        const RRType covered = dynamic_cast<const generic::RRSIG&>(
+            rit->getCurrent()).typeCovered();
+        for (rit->next(); !rit->isLast(); rit->next()) {
+            if (dynamic_cast<const generic::RRSIG&>(
+                    rit->getCurrent()).typeCovered() != covered) {
+                isc_throw(AddError, "RRSIG contains mixed covered types: "
+                          << sig_rrset->toText());
+            }
+        }
+
+        addRdataSet(zone_name, zone_data, ConstRRsetPtr(), sig_rrset);
+        return (result::SUCCESS);
+    }
+
+    /*
+     * Implementation of longer methods. We put them here, because the
+     * access is without the impl_-> and it will get inlined anyway.
+     */
+
+    // Implementation of InMemoryClient::add()
+    result::Result add(const ConstRRsetPtr& rrset,
+                       const Name& zone_name, ZoneData& zone_data)
+    {
+        // Sanitize input.  This will cause an exception to be thrown
+        // if the input RRset is empty.
+        addValidation(zone_name, rrset);
+
+        // OK, can add the RRset.
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
+            arg(rrset->getName()).arg(rrset->getType()).arg(zone_name);
+
+        if (rrset->getType() == RRType::NSEC3()) {
+            addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
+            return (result::SUCCESS);
+        }
+
+        // RRSIGs are special in various points, so we handle it in a
+        // separate dedicated method.
+        if (rrset->getType() == RRType::RRSIG()) {
+            return (addRRsig(rrset, zone_name, zone_data));
+        }
+
+        // Add wildcards possibly contained in the owner name to the domain
+        // tree.
+        // Note: this can throw an exception, breaking strong exception
+        // guarantee.  (see also the note for contextCheck() below).
+        addWildcards(zone_name, zone_data, rrset->getName());
+
+        addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
+
+        return (result::SUCCESS);
+    }
+
+    /*
+     * Wrapper around above.
+     */
+    void addFromLoad(const ConstRRsetPtr& set,
+                     const Name& zone_name, ZoneData* zone_data)
+    {
+        switch (add(set, zone_name, *zone_data)) {
+        case result::SUCCESS:
+            return;
+        default:
+            assert(0);
+        }
+    }
+};
+
+result::Result
+InMemoryClient::InMemoryClientImpl::load(
+    const Name& zone_name,
+    const string& filename,
+    boost::function<void(LoadCallback)> rrset_installer)
+{
+    SegmentObjectHolder<ZoneData, RRClass> holder(
+        local_mem_sgmt_, ZoneData::create(local_mem_sgmt_, zone_name),
+        rrclass_);
+
+    assert(!last_rrset_);
+
+    try {
+        rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this,
+                                    _1, zone_name, holder.get()));
+        // Add any last RRset that was left
+        addRdataSet(zone_name, *holder.get(),
+                    ConstRRsetPtr(), ConstRRsetPtr());
+    } catch (...) {
+        last_rrset_ = ConstRRsetPtr();
+        throw;
+    }
+
+    assert(!last_rrset_);
+
+    const ZoneNode* origin_node = holder.get()->getOriginNode();
+    const RdataSet* set = origin_node->getData();
+    // If the zone is NSEC3-signed, check if it has NSEC3PARAM
+    if (holder.get()->isNSEC3Signed()) {
+        // Note: origin_data_ is set on creation of ZoneData, and the load
+        // process only adds new nodes (and their data), so this assertion
+        // should hold.
+        if (RdataSet::find(set, RRType::NSEC3PARAM()) == NULL) {
+            LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM).
+                arg(zone_name).arg(rrclass_);
+        }
+    }
+
+    // When an empty zone file is loaded, the origin doesn't even have
+    // an SOA RR. This condition should be avoided, and hence load()
+    // should throw when an empty zone is loaded.
+    if (RdataSet::find(set, RRType::SOA()) == NULL) {
+        isc_throw(EmptyZone,
+                  "Won't create an empty zone for: " << zone_name);
+    }
+
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
+        arg(zone_name).arg(rrclass_.toText());
+
+    // Set the filename in file_name_tree_ now, so that getFileName()
+    // can use it (during zone reloading).
+    FileNameNode* node(NULL);
+    switch (file_name_tree_->insert(local_mem_sgmt_,
+                                    zone_name, &node)) {
+    case FileNameTree::SUCCESS:
+    case FileNameTree::ALREADYEXISTS:
+        // These are OK
+        break;
+    default:
+        // Can Not Happen
+        assert(false);
+    }
+    // node must point to a valid node now
+    assert(node != NULL);
+
+    std::string* tstr = node->setData(new std::string(filename));
+    delete tstr;
+
+    ZoneTable::AddResult result(zone_table_->addZone(local_mem_sgmt_,
+                                                     rrclass_, zone_name));
+    if (result.code == result::SUCCESS) {
+        // Only increment the zone count if the zone doesn't already
+        // exist.
+        ++zone_count_;
+    }
+
+    ZoneTable::FindResult fr(zone_table_->setZoneData(zone_name,
+                                                      holder.release()));
+    assert(fr.code == result::SUCCESS);
+    if (fr.zone_data != NULL) {
+        ZoneData::destroy(local_mem_sgmt_, fr.zone_data, rrclass_);
+    }
+
+    return (result.code);
+}
+
+namespace {
+// A wrapper for dns::masterLoad used by load() below.  Essentially it
+// converts the two callback types.  Note the mostly redundant wrapper of
+// boost::bind.  It converts function<void(ConstRRsetPtr)> to
+// function<void(RRsetPtr)> (masterLoad() expects the latter).  SunStudio
+// doesn't seem to do this conversion if we just pass 'callback'.
+void
+masterLoadWrapper(const char* const filename, const Name& origin,
+                  const RRClass& zone_class, LoadCallback callback)
+{
+    masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
+}
+
+// The installer called from Impl::load() for the iterator version of load().
+void
+generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
+    ConstRRsetPtr rrset;
+    vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
+
+    // The current internal implementation assumes an RRSIG is always added
+    // after the RRset they cover.  So we store any RRSIGs in 'rrsigs' until
+    // it's safe to add them; based on our assumption if the owner name
+    // changes, all covered RRsets of the previous name should have been
+    // installed and any pending RRSIGs can be added at that point.  RRSIGs
+    // of the last name from the iterator must be added separately.
+    while ((rrset = iterator->getNextRRset()) != NULL) {
+        if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
+            BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
+                callback(sig_rrset);
+            }
+            rrsigs.clear();
+        }
+        if (rrset->getType() == RRType::RRSIG()) {
+            rrsigs.push_back(rrset);
+        } else {
+            callback(rrset);
+        }
+    }
+
+    BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
+        callback(sig_rrset);
+    }
+}
+}
+
+InMemoryClient::InMemoryClient(util::MemorySegment& mem_sgmt,
+                               RRClass rrclass) :
+    impl_(new InMemoryClientImpl(mem_sgmt, rrclass))
+{}
+
+InMemoryClient::~InMemoryClient() {
+    delete impl_;
+}
+
+RRClass
+InMemoryClient::getClass() const {
+    return (impl_->rrclass_);
+}
+
+unsigned int
+InMemoryClient::getZoneCount() const {
+    return (impl_->zone_count_);
+}
+
+isc::datasrc::memory::ZoneTable::FindResult
+InMemoryClient::findZone2(const isc::dns::Name& zone_name) const {
+    LOG_DEBUG(logger, DBG_TRACE_DATA,
+              DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
+    ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+    return (result);
+}
+
+isc::datasrc::DataSourceClient::FindResult
+InMemoryClient::findZone(const isc::dns::Name&) const {
+    // This variant of findZone() is not implemented and should be
+    // removed eventually. It currently throws an exception. It is
+    // required right now to derive from DataSourceClient.
+    isc_throw(isc::NotImplemented,
+              "This variant of findZone() is not implemented.");
+}
+
+result::Result
+InMemoryClient::load(const isc::dns::Name& zone_name,
+                     const std::string& filename) {
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_LOAD).arg(zone_name).
+        arg(filename);
+
+    return (impl_->load(zone_name, filename,
+                        boost::bind(masterLoadWrapper, filename.c_str(),
+                                    zone_name, getClass(), _1)));
+}
+
+result::Result
+InMemoryClient::load(const isc::dns::Name& zone_name,
+                     ZoneIterator& iterator) {
+    return (impl_->load(zone_name, string(),
+                        boost::bind(generateRRsetFromIterator,
+                                    &iterator, _1)));
+}
+
+const std::string
+InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
+    FileNameNode* node(NULL);
+    FileNameTree::Result result = impl_->file_name_tree_->find(zone_name,
+                                                               &node);
+    if (result == FileNameTree::EXACTMATCH) {
+        return (*node->getData());
+    } else {
+        return (std::string());
+    }
+}
+
+result::Result
+InMemoryClient::add(const isc::dns::Name& zone_name,
+                    const ConstRRsetPtr& rrset) {
+    assert(!impl_->last_rrset_);
+
+    ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+    if (result.code != result::SUCCESS) {
+        isc_throw(DataSourceError, "No such zone: " + zone_name.toText());
+    }
+
+    result::Result ret(impl_->add(rrset, zone_name, *result.zone_data));
+    // Add any associated RRSIG too. This has to be done here, as both
+    // the RRset and its RRSIG have to be passed when constructing an
+    // RdataSet.
+    if ((ret == result::SUCCESS) && rrset->getRRsig()) {
+        impl_->add(rrset->getRRsig(), zone_name, *result.zone_data);
+    }
+
+    // Add any last RRset that was left
+    impl_->addRdataSet(zone_name, *result.zone_data,
+                       ConstRRsetPtr(), ConstRRsetPtr());
+
+    assert(!impl_->last_rrset_);
+
+    return (ret);
+}
+
+namespace {
+
+class MemoryIterator : public ZoneIterator {
+private:
+    ZoneChain chain_;
+    const RdataSet* set_node_;
+    const RRClass rrclass_;
+    const ZoneTree& tree_;
+    const ZoneNode* node_;
+    // Only used when separate_rrs_ is true
+    ConstRRsetPtr rrset_;
+    RdataIteratorPtr rdata_iterator_;
+    bool separate_rrs_;
+    bool ready_;
+public:
+    MemoryIterator(const RRClass rrclass,
+                   const ZoneTree& tree, const Name& origin,
+                   bool separate_rrs) :
+        rrclass_(rrclass),
+        tree_(tree),
+        separate_rrs_(separate_rrs),
+        ready_(true)
+    {
+        // Find the first node (origin) and preserve the node chain for future
+        // searches
+        ZoneTree::Result result(tree_.find(origin, &node_, chain_));
+        // It can't happen that the origin is not in there
+        if (result != ZoneTree::EXACTMATCH) {
+            isc_throw(Unexpected,
+                      "In-memory zone corrupted, missing origin node");
+        }
+        // Initialize the iterator if there's somewhere to point to
+        if (node_ != NULL && node_->getData() != NULL) {
+            set_node_ = node_->getData();
+            if (separate_rrs_ && set_node_ != NULL) {
+                rrset_.reset(new TreeNodeRRset(rrclass_,
+                                               node_, set_node_, true));
+                rdata_iterator_ = rrset_->getRdataIterator();
+            }
+        }
+    }
+
+    virtual ConstRRsetPtr getNextRRset() {
+        if (!ready_) {
+            isc_throw(Unexpected, "Iterating past the zone end");
+        }
+        /*
+         * This cycle finds the first nonempty node with yet unused
+         * RdataSset.  If it is NULL, we run out of nodes. If it is
+         * empty, it doesn't contain any RdataSets. If we are at the
+         * end, just get to next one.
+         */
+        while (node_ != NULL &&
+               (node_->getData() == NULL || set_node_ == NULL)) {
+            node_ = tree_.nextNode(chain_);
+            // If there's a node, initialize the iterator and check next time
+            // if the map is empty or not
+            if (node_ != NULL && node_->getData() != NULL) {
+                set_node_ = node_->getData();
+                // New RRset, so get a new rdata iterator
+                if (separate_rrs_ && set_node_ != NULL) {
+                    rrset_.reset(new TreeNodeRRset(rrclass_,
+                                                   node_, set_node_, true));
+                    rdata_iterator_ = rrset_->getRdataIterator();
+                }
+            }
+        }
+        if (node_ == NULL) {
+            // That's all, folks
+            ready_ = false;
+            return (ConstRRsetPtr());
+        }
+
+        if (separate_rrs_) {
+            // For separate rrs, reconstruct a new RRset with just the
+            // 'current' rdata
+            RRsetPtr result(new RRset(rrset_->getName(),
+                                      rrset_->getClass(),
+                                      rrset_->getType(),
+                                      rrset_->getTTL()));
+            result->addRdata(rdata_iterator_->getCurrent());
+            rdata_iterator_->next();
+            if (rdata_iterator_->isLast()) {
+                // all used up, next.
+                set_node_ = set_node_->getNext();
+                // New RRset, so get a new rdata iterator, but only if this
+                // was not the final RRset in the chain
+                if (set_node_ != NULL) {
+                    rrset_.reset(new TreeNodeRRset(rrclass_,
+                                                   node_, set_node_, true));
+                    rdata_iterator_ = rrset_->getRdataIterator();
+                }
+            }
+            return (result);
+        } else {
+            ConstRRsetPtr result(new TreeNodeRRset(rrclass_,
+                                                   node_, set_node_, true));
+
+            // This one is used, move it to the next time for next call
+            set_node_ = set_node_->getNext();
+
+            return (result);
+        }
+    }
+
+    virtual ConstRRsetPtr getSOA() const {
+        isc_throw(NotImplemented, "Not implemented");
+    }
+};
+
+} // End of anonymous namespace
+
+ZoneIteratorPtr
+InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
+    ZoneTable::FindResult result(impl_->zone_table_->findZone(name));
+    if (result.code != result::SUCCESS) {
+        isc_throw(DataSourceError, "No such zone: " + name.toText());
+    }
+
+    return (ZoneIteratorPtr(new MemoryIterator(
+                                getClass(),
+                                result.zone_data->getZoneTree(), name,
+                                separate_rrs)));
+}
+
+ZoneUpdaterPtr
+InMemoryClient::getUpdater(const isc::dns::Name&, bool, bool) const {
+    isc_throw(isc::NotImplemented, "Update attempt on in memory data source");
+}
+
+pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
+InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
+                                 uint32_t) const
+{
+    isc_throw(isc::NotImplemented, "Journaling isn't supported for "
+              "in memory data source");
+}
+
+} // end of namespace memory
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h
new file mode 100644
index 0000000..d2ec4c3
--- /dev/null
+++ b/src/lib/datasrc/memory/memory_client.h
@@ -0,0 +1,257 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_CLIENT_H
+#define DATASRC_MEMORY_CLIENT_H 1
+
+#include <util/memory_segment.h>
+
+#include <datasrc/iterator.h>
+#include <datasrc/client.h>
+#include <datasrc/memory/zone_table.h>
+
+// for isc::datasrc::ZoneTable::FindResult returned by findZone(). This
+// variant of findZone() is not implemented and should be removed
+// eventually.
+#include <datasrc/zonetable.h>
+
+#include <string>
+
+namespace isc {
+
+namespace dns {
+class Name;
+class RRsetList;
+};
+
+namespace datasrc {
+namespace memory {
+
+/// \brief A data source client that holds all necessary data in memory.
+///
+/// The \c InMemoryClient class provides an access to a conceptual data
+/// source that maintains all necessary data in a memory image, thereby
+/// allowing much faster lookups.  The in memory data is a copy of some
+/// real physical source - in the current implementation a list of zones
+/// are populated as a result of \c load() calls; zone data is given in
+/// a standard master file, or as an iterator of some other datasource
+/// including database backed ones.
+///
+/// The InMemoryClient enforces through its interface that all data
+/// loaded to the data source is of the same RR class.  For example, the
+/// \c load() method assumes that the zone being loaded belongs to the
+/// same RR class as the memory::Client instance.
+class InMemoryClient : public DataSourceClient {
+public:
+    ///
+    /// \name Constructors and Destructor.
+    ///
+    //@{
+
+    /// Default constructor.
+    ///
+    /// This constructor internally involves resource allocation, and if
+    /// it fails, a corresponding standard exception will be thrown.
+    /// It never throws an exception otherwise.
+    InMemoryClient(util::MemorySegment& mem_sgmt,
+                   isc::dns::RRClass rrclass);
+
+    /// The destructor.
+    ~InMemoryClient();
+    //@}
+
+    /// \brief Returns the class of the data source client.
+    virtual isc::dns::RRClass getClass() const;
+
+    /// Return the number of zones stored in the client.
+    ///
+    /// This method never throws an exception.
+    ///
+    /// \return The number of zones stored in the client.
+    virtual unsigned int getZoneCount() const;
+
+    /// \brief Load zone from masterfile.
+    ///
+    /// This loads data from masterfile specified by filename. It replaces
+    /// current content. The masterfile parsing ability is kind of limited,
+    /// see isc::dns::masterLoad.
+    ///
+    /// This throws isc::dns::MasterLoadError if there is problem with loading
+    /// (missing file, malformed, it contains different zone, etc - see
+    /// isc::dns::masterLoad for details).
+    ///
+    /// In case of internal problems, OutOfZone, NullRRset or AssertError could
+    /// be thrown, but they should not be expected. Exceptions caused by
+    /// allocation may be thrown as well.
+    ///
+    /// If anything is thrown, the previous content is preserved (so it can
+    /// be used to update the data, but if user makes a typo, the old one
+    /// is kept).
+    ///
+    /// \param filename The master file to load.
+    ///
+    /// \todo We may need to split it to some kind of build and commit/abort.
+    ///     This will probably be needed when a better implementation of
+    ///     configuration reloading is written.
+    result::Result load(const isc::dns::Name& zone_name,
+                        const std::string& filename);
+
+    /// \brief Load zone from another data source.
+    ///
+    /// This is similar to the other version, but zone's RRsets are provided
+    /// by an iterator of another data source.  On successful load, the
+    /// internal filename will be cleared.
+    ///
+    /// This implementation assumes the iterator produces combined RRsets,
+    /// that is, there should exactly one RRset for the same owner name and
+    /// RR type.  This means the caller is expected to create the iterator
+    /// with \c separate_rrs being \c false.  This implementation also assumes
+    /// RRsets of different names are not mixed; so if the iterator produces
+    /// an RRset of a different name than that of the previous RRset, that
+    /// previous name must never appear in the subsequent sequence of RRsets.
+    /// Note that the iterator API does not ensure this.  If the underlying
+    /// implementation does not follow it, load() will fail.  Note, however,
+    /// that this whole interface is tentative.  in-memory zone loading will
+    /// have to be revisited fundamentally, and at that point this restriction
+    /// probably won't matter.
+    result::Result load(const isc::dns::Name& zone_name,
+                        ZoneIterator& iterator);
+
+    /// Return the master file name of the zone
+    ///
+    /// This method returns the name of the zone's master file to be loaded.
+    /// The returned string will be an empty unless the data source client has
+    /// successfully loaded the \c zone_name zone from a file before.
+    ///
+    /// This method should normally not throw an exception.  But the creation
+    /// of the return string may involve a resource allocation, and if it
+    /// fails, the corresponding standard exception will be thrown.
+    ///
+    /// \return The name of the zone file corresponding to the zone, or
+    /// an empty string if the client hasn't loaded the \c zone_name
+    /// zone from a file before.
+    const std::string getFileName(const isc::dns::Name& zone_name) const;
+
+    /// \brief Inserts an rrset into the zone.
+    ///
+    /// It puts another RRset into the zone.
+    ///
+    /// In the current implementation, this method doesn't allow an existing
+    /// RRset to be updated or overridden.  So the caller must make sure that
+    /// all RRs of the same type and name must be given in the form of a
+    /// single RRset.  The current implementation will also require that
+    /// when an RRSIG is added, the RRset to be covered has already been
+    /// added.  These restrictions are probably too strict when this data
+    /// source accepts various forms of input, so they should be revisited
+    /// later.
+    ///
+    /// Except for NullRRset and OutOfZone, this method does not guarantee
+    /// strong exception safety (it is currently not needed, if it is needed
+    /// in future, it should be implemented).
+    ///
+    /// \throw NullRRset \c rrset is a NULL pointer.
+    /// \throw OutOfZone The owner name of \c rrset is outside of the
+    /// origin of the zone.
+    /// \throw AddError Other general errors.
+    /// \throw Others This method might throw standard allocation exceptions.
+    ///
+    /// \param rrset The set to add.
+    /// \return SUCCESS or EXIST (if an rrset for given name and type already
+    ///    exists).
+    result::Result add(const isc::dns::Name& zone_name,
+                       const isc::dns::ConstRRsetPtr& rrset);
+
+    /// \brief RRset is NULL exception.
+    ///
+    /// This is thrown if the provided RRset parameter is NULL.
+    struct NullRRset : public InvalidParameter {
+        NullRRset(const char* file, size_t line, const char* what) :
+            InvalidParameter(file, line, what)
+        { }
+    };
+
+    /// \brief Zone is empty exception.
+    ///
+    /// This is thrown if we have an empty zone created as a result of
+    /// load().
+    struct EmptyZone : public InvalidParameter {
+        EmptyZone(const char* file, size_t line, const char* what) :
+            InvalidParameter(file, line, what)
+        { }
+    };
+
+    /// \brief General failure exception for \c add().
+    ///
+    /// This is thrown against general error cases in adding an RRset
+    /// to the zone.
+    ///
+    /// Note: this exception would cover cases for \c OutOfZone or
+    /// \c NullRRset.  We'll need to clarify and unify the granularity
+    /// of exceptions eventually.  For now, exceptions are added as
+    /// developers see the need for it.
+    struct AddError : public InvalidParameter {
+        AddError(const char* file, size_t line, const char* what) :
+            InvalidParameter(file, line, what)
+        { }
+    };
+
+    /// Returns a \c ZoneTable result that best matches the given name.
+    ///
+    /// This derived version of the method never throws an exception.
+    /// For other details see \c DataSourceClient::findZone().
+    virtual isc::datasrc::memory::ZoneTable::FindResult
+    findZone2(const isc::dns::Name& name) const;
+
+    // This variant of findZone() is not implemented and should be
+    // removed eventually. It currently throws an exception. It is
+    // required right now to derive from DataSourceClient.
+    virtual isc::datasrc::DataSourceClient::FindResult
+    findZone(const isc::dns::Name& name) const;
+
+    /// \brief Implementation of the getIterator method
+    virtual isc::datasrc::ZoneIteratorPtr
+    getIterator(const isc::dns::Name& name, bool separate_rrs = false) const;
+
+    /// In-memory data source doesn't write back persistently, so this
+    /// derived method will result in a NotImplemented exception.
+    ///
+    /// \note We plan to use a database-based data source as a backend
+    /// persistent storage for an in-memory data source.  When it's
+    /// implemented we may also want to allow the user of the in-memory client
+    /// to update via its updater (this may or may not be a good idea and
+    /// is subject to further discussions).
+    virtual ZoneUpdaterPtr getUpdater(const isc::dns::Name& name,
+                                      bool replace, bool journaling = false)
+        const;
+
+    virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
+    getJournalReader(const isc::dns::Name& zone, uint32_t begin_serial,
+                     uint32_t end_serial) const;
+
+private:
+    // TODO: Do we still need the PImpl if nobody should manipulate this class
+    // directly any more (it should be handled through DataSourceClient)?
+    class InMemoryClientImpl;
+    InMemoryClientImpl* impl_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_CLIENT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/memory/memory_messages.mes b/src/lib/datasrc/memory/memory_messages.mes
new file mode 100644
index 0000000..1b67093
--- /dev/null
+++ b/src/lib/datasrc/memory/memory_messages.mes
@@ -0,0 +1,90 @@
+# Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::datasrc::memory
+
+# \brief Messages for the data source memory library
+
+% DATASRC_MEMORY_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'
+The software refuses to load NSEC3 records into a wildcard domain or
+the owner name has two or more labels below the zone origin.
+It isn't explicitly forbidden, but no sane zone wouldn have such names
+for NSEC3.  BIND 9 also refuses NSEC3 at wildcard, so this behavior is
+compatible with BIND 9.
+
+% DATASRC_MEMORY_MEM_ADD_RRSET adding RRset '%1/%2' into zone '%3'
+Debug information. An RRset is being added to the in-memory data source.
+
+% DATASRC_MEMORY_MEM_ADD_WILDCARD adding wildcards for '%1'
+This is a debug message issued during the processing of a wildcard
+name. The internal domain name tree is scanned and some nodes are
+specially marked to allow the wildcard lookup to succeed.
+
+% DATASRC_MEMORY_MEM_ADD_ZONE adding zone '%1/%2'
+Debug information. A zone is being added into the in-memory data source.
+
+% DATASRC_MEMORY_MEM_CNAME_COEXIST can't add data to CNAME in domain '%1'
+This is the same problem as in MEM_CNAME_TO_NONEMPTY, but it happened the
+other way around -- adding some other data to CNAME.
+
+% DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY can't add CNAME to domain with other data in '%1'
+Someone or something tried to add a CNAME into a domain that already contains
+some other data. But the protocol forbids coexistence of CNAME with anything
+(RFC 1034, section 3.6.2). This indicates a problem with provided data.
+
+% DATASRC_MEMORY_MEM_DNAME_NS DNAME and NS can't coexist in non-apex domain '%1'
+A request was made for DNAME and NS records to be put into the same
+domain which is not the apex (the top of the zone). This is forbidden
+by RFC 2672 (section 3) and indicates a problem with provided data.
+
+% DATASRC_MEMORY_MEM_DUP_RRSET duplicate RRset '%1/%2'
+An RRset is being inserted into in-memory data source for a second time.  The
+original version must be removed first. Note that loading master files where an
+RRset is split into multiple locations is not supported yet.
+
+% DATASRC_MEMORY_MEM_FIND_ZONE looking for zone '%1'
+Debug information. A zone object for this zone is being searched for in the
+in-memory data source.
+
+% DATASRC_MEMORY_MEM_LOAD loading zone '%1' from file '%2'
+Debug information. The content of master file is being loaded into the memory.
+
+% DATASRC_MEMORY_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2
+The in-memory data source has loaded a zone signed with NSEC3 RRs,
+but it doesn't have a NSEC3PARAM RR at the zone origin.  It's likely that
+the zone is somehow broken, but this RR is not necessarily needed for
+handling lookups with NSEC3 in this data source, so it accepts the given
+content of the zone.  Nevertheless the administrator should look into
+the integrity of the zone data.
+
+% DATASRC_MEMORY_MEM_OUT_OF_ZONE domain '%1' doesn't belong to zone '%2'
+It was attempted to add the domain into a zone that shouldn't have it
+(eg. the domain is not subdomain of the zone origin). This indicates a
+problem with provided data.
+
+% DATASRC_MEMORY_MEM_SINGLETON trying to add multiple RRs for domain '%1' and type '%2'
+Some resource types are singletons -- only one is allowed in a domain
+(for example CNAME or SOA). This indicates a problem with provided data.
+
+% DATASRC_MEMORY_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1'
+The software refuses to load DNAME records into a wildcard domain.  It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
+
+% DATASRC_MEMORY_MEM_WILDCARD_NS NS record in wildcard domain '%1'
+The software refuses to load NS records into a wildcard domain.  It isn't
+explicitly forbidden, but the protocol is ambiguous about how this should
+behave and BIND 9 refuses that as well. Please describe your intention using
+different tools.
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
index 334094d..0895b5c 100644
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ b/src/lib/datasrc/memory/tests/Makefile.am
@@ -1,6 +1,9 @@
+SUBDIRS = testdata .
+
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
@@ -26,6 +29,7 @@ run_unittests_SOURCES += zone_table_unittest.cc
 run_unittests_SOURCES += zone_data_unittest.cc
 run_unittests_SOURCES += memory_segment_test.h
 run_unittests_SOURCES += segment_object_holder_unittest.cc
+run_unittests_SOURCES += memory_client_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
@@ -36,6 +40,7 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 run_unittests_LDADD += $(GTEST_LDADD)
 endif
 
diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc
new file mode 100644
index 0000000..6952a71
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/memory_client_unittest.cc
@@ -0,0 +1,740 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+#include <dns/nsec3hash.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrsetlist.h>
+#include <dns/rrttl.h>
+#include <dns/masterload.h>
+
+#include <datasrc/result.h>
+#include <datasrc/data_source.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/memory_client.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+
+namespace {
+// Memory segment specified for tests.  It normally behaves like a "local"
+// memory segment.  If "throw count" is set to non 0 via setThrowCount(),
+// it continues the normal behavior up to the specified number of calls to
+// allocate(), and throws an exception at the next call.
+class TestMemorySegment : public isc::util::MemorySegmentLocal {
+public:
+    TestMemorySegment() : throw_count_(0) {}
+    virtual void* allocate(size_t size) {
+        if (throw_count_ > 0) {
+            if (--throw_count_ == 0) {
+                throw std::bad_alloc();
+            }
+        }
+        return (isc::util::MemorySegmentLocal::allocate(size));
+    }
+    void setThrowCount(size_t count) { throw_count_ = count; }
+
+private:
+    size_t throw_count_;
+};
+
+static const char* rrset_data[] = {
+    "example.org. 3600 IN SOA   ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600",
+    "a.example.org.		   	 3600 IN A	192.168.0.1",
+    "a.example.org.		   	 3600 IN MX	10 mail.example.org.",
+    NULL
+};
+
+class MockIterator : public ZoneIterator {
+private:
+    MockIterator() :
+        rrset_data_ptr_(rrset_data)
+    {
+    }
+
+    const char** rrset_data_ptr_;
+
+public:
+    virtual ConstRRsetPtr getNextRRset() {
+        if (*rrset_data_ptr_ == NULL) {
+             return (ConstRRsetPtr());
+        }
+
+        RRsetPtr result(textToRRset(*rrset_data_ptr_,
+                                    RRClass::IN(), Name("example.org")));
+        rrset_data_ptr_++;
+
+        return (result);
+    }
+
+    virtual ConstRRsetPtr getSOA() const {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+
+    static ZoneIteratorPtr makeIterator(void) {
+        return (ZoneIteratorPtr(new MockIterator()));
+    }
+};
+
+class MemoryClientTest : public ::testing::Test {
+protected:
+    MemoryClientTest() : zclass_(RRClass::IN()),
+                         client_(new InMemoryClient(mem_sgmt_, zclass_))
+    {}
+    ~MemoryClientTest() {
+        if (client_ != NULL) {
+            delete client_;
+        }
+    }
+    void TearDown() {
+        delete client_;
+        client_ = NULL;
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+    }
+    const RRClass zclass_;
+    TestMemorySegment mem_sgmt_;
+    InMemoryClient* client_;
+};
+
+TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) {
+    // Attempting to load example.org to example.com zone should result
+    // in an exception.
+    EXPECT_THROW(client_->load(Name("example.com"),
+                               TEST_DATA_DIR "/example.org-empty.zone"),
+                 MasterLoadError);
+}
+
+TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak1) {
+    // Attempting to load broken example.org zone should result in an
+    // exception. This should not leak ZoneData and other such
+    // allocations.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR "/example.org-broken1.zone"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak2) {
+    // Attempting to load broken example.org zone should result in an
+    // exception. This should not leak ZoneData and other such
+    // allocations.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR "/example.org-broken2.zone"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNonExistentZoneFile) {
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR "/somerandomfilename"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
+    // When an empty zone file is loaded, the origin doesn't even have
+    // an SOA RR. This condition should be avoided, and hence load()
+    // should throw when an empty zone is loaded.
+
+    EXPECT_EQ(0, client_->getZoneCount());
+
+    EXPECT_THROW(client_->load(Name("."),
+                               TEST_DATA_DIR "/empty.zone"),
+                 InMemoryClient::EmptyZone);
+
+    EXPECT_EQ(0, client_->getZoneCount());
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, load) {
+    // This is a simple load check for a "full" and correct zone that
+    // should not result in any exceptions.
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org.zone");
+}
+
+TEST_F(MemoryClientTest, loadFromIterator) {
+    client_->load(Name("example.org"),
+                  *MockIterator::makeIterator());
+
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset(iterator->getNextRRset());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // RRType::MX() RRset
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::MX(), rrset->getType());
+
+    // RRType::A() RRset
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // There's nothing else in this iterator
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+    // Iterating past the end should result in an exception
+    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+}
+
+TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
+    // Just to check that things get cleaned up
+
+    for (int i = 1; i < 16; i++) {
+        mem_sgmt_.setThrowCount(i);
+        EXPECT_THROW(client_->load(Name("example.org"),
+                                   TEST_DATA_DIR "/example.org.zone"),
+                     std::bad_alloc);
+    }
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3Signed) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-nsec3-signed.zone");
+}
+
+TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
+}
+
+TEST_F(MemoryClientTest, loadReloadZone) {
+    // Because we reload the same zone, also check that the zone count
+    // doesn't increase.
+    EXPECT_EQ(0, client_->getZoneCount());
+
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-empty.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+
+    // Reload zone with same data
+
+    client_->load(Name("example.org"),
+                  client_->getFileName(Name("example.org")));
+    EXPECT_EQ(1, client_->getZoneCount());
+
+    isc::datasrc::memory::ZoneTable::FindResult
+        result(client_->findZone2(Name("example.org")));
+    EXPECT_EQ(result::SUCCESS, result.code);
+    EXPECT_NE(static_cast<ZoneData*>(NULL),
+              result.zone_data);
+
+    /* Check SOA */
+    const ZoneNode* node = result.zone_data->getOriginNode();
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    const RdataSet* set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::SOA(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    /* Check ns1.example.org */
+    const ZoneTree& tree = result.zone_data->getZoneTree();
+    ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
+    EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
+
+    // Reload zone with different data
+
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+
+    isc::datasrc::memory::ZoneTable::FindResult
+        result2(client_->findZone2(Name("example.org")));
+    EXPECT_EQ(result::SUCCESS, result2.code);
+    EXPECT_NE(static_cast<ZoneData*>(NULL),
+              result2.zone_data);
+
+    /* Check SOA */
+    node = result2.zone_data->getOriginNode();
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::SOA(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    /* Check ns1.example.org */
+    const ZoneTree& tree2 = result2.zone_data->getZoneTree();
+    ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
+    EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::AAAA(), set->type);
+
+    set = set->getNext();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::A(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDuplicateType) {
+    // This should not result in any exceptions:
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-duplicate-type.zone");
+
+    // This should throw:
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-duplicate-type-bad.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
+    // Multiple CNAME RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-cname.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
+    // Multiple DNAME RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-dname.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
+    // Multiple NSEC3 RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-nsec3.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
+    // Multiple NSEC3PARAM RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-nsec3param.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadOutOfZoneThrows) {
+    // Out of zone names should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-out-of-zone.zone"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardNSThrows) {
+    // Wildcard NS names should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-wildcard-ns.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
+    // Wildcard DNAME names should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-wildcard-dname.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
+    // Wildcard NSEC3 names should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-wildcard-nsec3.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
+    // NSEC3 names with labels != (origin_labels + 1) should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-nsec3-fewer-labels.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
+    // NSEC3 names with labels != (origin_labels + 1) should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-nsec3-more-labels.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
+    // CNAME and not NSEC should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-cname-and-not-nsec-1.zone"),
+                 InMemoryClient::AddError);
+
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-cname-and-not-nsec-2.zone"),
+                 InMemoryClient::AddError);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) {
+    // DNAME + NS (apex) is OK
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR
+                  "/example.org-dname-ns-apex-1.zone");
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) {
+    // DNAME + NS (apex) is OK (reverse order)
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR
+                  "/example.org-dname-ns-apex-2.zone");
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
+    // DNAME + NS (non-apex) must throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-dname-ns-nonapex-1.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
+    // DNAME + NS (non-apex) must throw (reverse order)
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-dname-ns-nonapex-2.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-rrsig-follows-nothing.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGNameUnmatched) {
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-rrsig-name-unmatched.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGTypeUnmatched) {
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-rrsig-type-unmatched.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGs) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+}
+
+TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    RRsetPtr rrset(new RRset(Name("example.org"),
+                             RRClass::IN(), RRType::A(), RRTTL(3600)));
+    rrset->addRdata(in::A("192.0.2.1"));
+    rrset->addRdata(in::A("192.0.2.2"));
+
+    RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
+                             RRType::RRSIG(), RRTTL(300)));
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
+                                   "12345 example.org. FAKEFAKEFAKE"));
+    rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
+                                   "54321 example.org. FAKEFAKEFAKEFAKE"));
+    rrset->addRRsig(rrsig);
+
+    EXPECT_THROW(client_->add(Name("example.org"), rrset),
+                 InMemoryClient::AddError);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, getZoneCount) {
+    EXPECT_EQ(0, client_->getZoneCount());
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+}
+
+TEST_F(MemoryClientTest, getFileNameForNonExistentZone) {
+    // Zone "example.org." doesn't exist
+    EXPECT_TRUE(client_->getFileName(Name("example.org.")).empty());
+}
+
+TEST_F(MemoryClientTest, getFileName) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    EXPECT_EQ(TEST_DATA_DIR "/example.org-empty.zone",
+              client_->getFileName(Name("example.org")));
+}
+
+TEST_F(MemoryClientTest, getIteratorForNonExistentZone) {
+    // Zone "." doesn't exist
+    EXPECT_THROW(client_->getIterator(Name(".")), DataSourceError);
+}
+
+TEST_F(MemoryClientTest, getIterator) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset_soa(iterator->getNextRRset());
+    EXPECT_TRUE(rrset_soa);
+    EXPECT_EQ(RRType::SOA(), rrset_soa->getType());
+
+    // There's nothing else in this iterator
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+    // Iterating past the end should result in an exception
+    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+}
+
+TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-multiple.zone");
+
+    // separate_rrs = false
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset(iterator->getNextRRset());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // Only one RRType::A() RRset
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // There's nothing else in this zone
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+
+    // separate_rrs = true
+    ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true));
+
+    // First we have the SOA
+    rrset = iterator2->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // First RRType::A() RRset
+    rrset = iterator2->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // Second RRType::A() RRset
+    rrset = iterator2->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // There's nothing else in this iterator
+    EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
+}
+
+TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // This method is not implemented.
+    EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
+    // The zone "example.org" doesn't exist, so we can't add an RRset to
+    // it.
+    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+                               RRTTL(300)));
+    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+    EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
+}
+
+TEST_F(MemoryClientTest, addOutOfZoneThrows) {
+    // Out of zone names should throw.
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-empty.zone");
+
+    RRsetPtr rrset_a(new RRset(Name("a.example.com"),
+                               RRClass::IN(), RRType::A(), RRTTL(300)));
+    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+
+    EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
+                 OutOfZone);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, addNullRRsetThrows) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
+                 InMemoryClient::NullRRset);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+                               RRTTL(300)));
+    EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
+                 InMemoryClient::AddError);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, add) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+
+    // Add another RRset
+    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+                               RRTTL(300)));
+    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+    client_->add(Name("example.org"), rrset_a);
+
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset(iterator->getNextRRset());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // There's nothing else in this zone
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+}
+
+TEST_F(MemoryClientTest, findZoneThrowsNotImplemented) {
+    // This method is not implemented.
+    EXPECT_THROW(client_->findZone(Name(".")),
+                 isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, findZone2) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    isc::datasrc::memory::ZoneTable::FindResult
+        result(client_->findZone2(Name("example.com")));
+    EXPECT_EQ(result::NOTFOUND, result.code);
+    EXPECT_EQ(static_cast<ZoneData*>(NULL),
+              result.zone_data);
+
+    isc::datasrc::memory::ZoneTable::FindResult
+        result2(client_->findZone2(Name("example.org")));
+    EXPECT_EQ(result::SUCCESS, result2.code);
+    EXPECT_NE(static_cast<ZoneData*>(NULL),
+              result2.zone_data);
+
+    /* Check SOA */
+    const ZoneNode* node = result2.zone_data->getOriginNode();
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    const RdataSet* set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::SOA(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    /* Check ns1.example.org */
+    const ZoneTree& tree = result2.zone_data->getZoneTree();
+    ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
+    EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::AAAA(), set->type);
+
+    set = set->getNext();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::A(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+}
+
+TEST_F(MemoryClientTest, getUpdaterThrowsNotImplemented) {
+    // This method is not implemented.
+    EXPECT_THROW(client_->getUpdater(Name("."), false, false),
+                 isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
+    // This method is not implemented.
+    EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
+                 isc::NotImplemented);
+}
+}
diff --git a/src/lib/datasrc/memory/tests/run_unittests.cc b/src/lib/datasrc/memory/tests/run_unittests.cc
index 998667c..6321976 100644
--- a/src/lib/datasrc/memory/tests/run_unittests.cc
+++ b/src/lib/datasrc/memory/tests/run_unittests.cc
@@ -14,10 +14,13 @@
 
 #include <gtest/gtest.h>
 #include <util/unittests/run_all.h>
+#include <log/logger_support.h>
 
 int
 main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);
 
+    isc::log::initLogger();
+
     return (isc::util::unittests::run_all());
 }
diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am
new file mode 100644
index 0000000..9fb9986
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/Makefile.am
@@ -0,0 +1,32 @@
+CLEANFILES = *.copied
+
+EXTRA_DIST =  empty.zone
+EXTRA_DIST += example.org.zone
+EXTRA_DIST += example.org-empty.zone
+
+EXTRA_DIST += example.org-broken1.zone
+EXTRA_DIST += example.org-broken2.zone
+EXTRA_DIST += example.org-cname-and-not-nsec-1.zone
+EXTRA_DIST += example.org-cname-and-not-nsec-2.zone
+EXTRA_DIST += example.org-dname-ns-apex-1.zone
+EXTRA_DIST += example.org-dname-ns-apex-2.zone
+EXTRA_DIST += example.org-dname-ns-nonapex-1.zone
+EXTRA_DIST += example.org-dname-ns-nonapex-2.zone
+EXTRA_DIST += example.org-duplicate-type-bad.zone
+EXTRA_DIST += example.org-duplicate-type.zone
+EXTRA_DIST += example.org-multiple-cname.zone
+EXTRA_DIST += example.org-multiple-dname.zone
+EXTRA_DIST += example.org-multiple-nsec3.zone
+EXTRA_DIST += example.org-multiple-nsec3param.zone
+EXTRA_DIST += example.org-multiple.zone
+EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
+EXTRA_DIST += example.org-nsec3-signed-no-param.zone
+EXTRA_DIST += example.org-nsec3-signed.zone
+EXTRA_DIST += example.org-out-of-zone.zone
+EXTRA_DIST += example.org-rrsig-follows-nothing.zone
+EXTRA_DIST += example.org-rrsig-name-unmatched.zone
+EXTRA_DIST += example.org-rrsig-type-unmatched.zone
+EXTRA_DIST += example.org-rrsigs.zone
+EXTRA_DIST += example.org-wildcard-dname.zone
+EXTRA_DIST += example.org-wildcard-ns.zone
+EXTRA_DIST += example.org-wildcard-nsec3.zone
diff --git a/src/lib/datasrc/memory/tests/testdata/empty.zone b/src/lib/datasrc/memory/tests/testdata/empty.zone
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
new file mode 100644
index 0000000..317095d
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
@@ -0,0 +1 @@
+This is a broken zone that should not parse.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
new file mode 100644
index 0000000..2f9fd98
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
@@ -0,0 +1,5 @@
+;; broken example.org zone, where some RRs are OK, but others aren't
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 73 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A		192.0.2.1
+ns2.example.org.		      3600 IN A		192.0.2.2
+ns2.a.example.com.		      3600 IN AAAA
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
new file mode 100644
index 0000000..5533663
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
@@ -0,0 +1,4 @@
+;; CNAME + other is an error
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200
+a.example.org.				      7200  IN A	192.168.0.1
+a.example.org.				      3600  IN CNAME	foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
new file mode 100644
index 0000000..966aeeb
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
@@ -0,0 +1,4 @@
+;; CNAME + other is an error
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200
+a.example.org.				      3600  IN CNAME	foo.example.com.
+a.example.org.				      7200  IN A	192.168.0.1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
new file mode 100644
index 0000000..f57c25d
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
+example.org.				      3600  IN DNAME	foo.example.com.
+example.org.				      3600  IN NS	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
new file mode 100644
index 0000000..bb3f191
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200
+example.org.				      3600  IN NS	bar.example.com.
+example.org.				      3600  IN DNAME	foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
new file mode 100644
index 0000000..68a9805
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (non-apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200
+ns1.example.org.			      3600  IN DNAME	foo.example.com.
+ns1.example.org.			      3600  IN NS	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
new file mode 100644
index 0000000..1b671dd
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (non-apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
+ns1.example.org.			      3600  IN NS	bar.example.com.
+ns1.example.org.			      3600  IN DNAME	foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
new file mode 100644
index 0000000..06c7dff
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
@@ -0,0 +1,4 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 77 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A	 	192.168.0.1
+ns1.example.org.		      3600 IN AAAA 	::1
+ns1.example.org.		      3600 IN A	 	192.168.0.2
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
new file mode 100644
index 0000000..6e5c1b8
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
@@ -0,0 +1,4 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 76 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A	 	192.168.0.1
+ns1.example.org.		      3600 IN A	 	192.168.0.2
+ns1.example.org.		      3600 IN AAAA 	::1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone b/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
new file mode 100644
index 0000000..f11b9b8
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
@@ -0,0 +1,2 @@
+;; empty example.org zone
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
new file mode 100644
index 0000000..0a0c983
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
@@ -0,0 +1,3 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+ns1.example.org.		      3600 IN CNAME	foo.example.com.
+ns1.example.org.		      3600 IN CNAME	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
new file mode 100644
index 0000000..3d581d0
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
@@ -0,0 +1,3 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+ns1.example.org.		      3600 IN DNAME	foo.example.com.
+ns1.example.org.		      3600 IN DNAME	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
new file mode 100644
index 0000000..874023a
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
@@ -0,0 +1,3 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012090702 7200 3600 2592000 1200
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
new file mode 100644
index 0000000..5e69518
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
@@ -0,0 +1,3 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012090700 7200 3600 2592000 1200
+example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
+example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
new file mode 100644
index 0000000..f473ae6
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
@@ -0,0 +1,4 @@
+;; Multiple RDATA for testing separate RRs iterator
+example.org.  		   	 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+a.example.org.		   	 3600 IN A	192.168.0.1
+a.example.org.		   	 3600 IN A	192.168.0.2
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
new file mode 100644
index 0000000..0221269
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
@@ -0,0 +1,3 @@
+;; NSEC3 names with labels != (origin_labels + 1)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200
+example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
new file mode 100644
index 0000000..efebcfb
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
@@ -0,0 +1,3 @@
+;; NSEC3 names with labels != (origin_labels + 1)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200
+a.b.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
new file mode 100644
index 0000000..5caa308
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
@@ -0,0 +1,15 @@
+;; This file intentionally removes NSEC3PARAM from example.org.nsec3-signed
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
+example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org.				      86400 IN NS	ns.example.org.
+example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+;; example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
+;; example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org.				      86400 IN A	192.0.2.1
+ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
new file mode 100644
index 0000000..9c1195f
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
@@ -0,0 +1,14 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
+example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org.				      86400 IN NS	ns.example.org.
+example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
+example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org.				      86400 IN A	192.0.2.1
+ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone b/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
new file mode 100644
index 0000000..e3afb74
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
@@ -0,0 +1,5 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 75 3600 300 3600000 3600
+a.example.com.	  		      3600 IN A	 	192.168.0.1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
new file mode 100644
index 0000000..ef5f887
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
@@ -0,0 +1,5 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
new file mode 100644
index 0000000..dc1d728
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
@@ -0,0 +1,6 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 70 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A		192.0.2.1
+ns2.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
new file mode 100644
index 0000000..300e124
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
@@ -0,0 +1,6 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 72 3600 300 3600000 3600
+ns1.example.org.		      3600 IN AAAA	2001:db8::1
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
new file mode 100644
index 0000000..1c780b1
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
@@ -0,0 +1,8 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 74 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A	 	192.168.0.1
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN AAAA 	::1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
new file mode 100644
index 0000000..0d03b0d
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard DNAME names
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+*.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
new file mode 100644
index 0000000..2933515
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard NS names
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+*.example.org.			      3600 IN NS	ns1.example.org.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
new file mode 100644
index 0000000..feee116
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard NS names
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+*.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org.zone b/src/lib/datasrc/memory/tests/testdata/example.org.zone
new file mode 100644
index 0000000..e499e0d
--- /dev/null
+++ b/src/lib/datasrc/memory/tests/testdata/example.org.zone
@@ -0,0 +1,81 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 67 3600 300 3600000 3600
+example.org.			      3600 IN NS	ns1.example.org.
+example.org.			      3600 IN NS	ns2.example.org.
+example.org.			      3600 IN MX	1 mx1.example.org.
+example.org.			      3600 IN MX	2 mx2.example.org.
+example.org.			      3600 IN MX	3 mx.a.example.org.
+
+ns1.example.org.		      3600 IN A		192.0.2.1
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN AAAA	2001:db8::1
+ns1.example.org.		      3600 IN RRSIG	AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
+ns2.example.org.		      3600 IN A		192.0.2.2
+ns2.example.org.		      3600 IN TXT	"text data"
+
+mx1.example.org.		      3600 IN A		192.0.2.10
+mx2.example.org.		      3600 IN AAAA	2001:db8::10
+
+;; delegation
+a.example.org.			      3600 IN NS	ns1.a.example.org.
+a.example.org.			      3600 IN NS	ns2.a.example.org.
+a.example.org.			      3600 IN NS	ns.example.com.
+
+ns1.a.example.org.		      3600 IN A		192.0.2.5
+ns2.a.example.org.		      3600 IN A		192.0.2.6
+ns2.a.example.org.		      3600 IN AAAA	2001:db8::6
+mx.a.example.org.		      3600 IN A		192.0.2.7
+
+;; delegation, one of its NS names is at zone cut.
+b.example.org.			      3600 IN NS	ns.b.example.org.
+b.example.org.			      3600 IN NS	b.example.org.
+b.example.org.			      3600 IN AAAA	2001:db8::8
+
+ns.b.example.org.		      3600 IN A		192.0.2.9
+
+;; The MX name is at a zone cut.  shouldn't be included in the
+;; additional section.
+mxatcut.example.org.		      3600 IN MX	1 b.example.org.
+
+;; delegation, one of its NS names is under a DNAME delegation point;
+;; another is at that point; and yet another is under DNAME below a
+;; zone cut.
+c.example.org. 	      	      3600 IN NS	ns.dname.example.org.
+c.example.org. 	      	      3600 IN NS	dname.example.org.
+c.example.org.      	      3600 IN NS	ns.deepdname.example.org.
+ns.dname.example.org.		      3600 IN A		192.0.2.11
+dname.example.org.		      3600 IN A		192.0.2.12
+ns.deepdname.example.org.	      3600 IN AAAA	2001:db8::9
+
+;; delegation, one of its NS name is at an empty non terminal.
+d.example.org. 	      	      3600 IN NS	ns.empty.example.org.
+d.example.org. 	      	      3600 IN NS	ns1.example.org.
+;; by adding these two we can create an empty RB node for
+;; ns.empty.example.org in the in-memory zone
+foo.ns.empty.example.org.     3600 IN A		192.0.2.13
+bar.ns.empty.example.org.     3600 IN A		192.0.2.14
+
+;; delegation; the NS name matches a wildcard (and there's no exact
+;; match).  One of the NS names matches an empty wildcard node, for
+;; which no additional record should be provided (or any other
+;; disruption should happen).
+e.example.org. 	      	      3600 IN NS	ns.wild.example.org.
+e.example.org. 	      	      3600 IN NS	ns.emptywild.example.org.
+e.example.org. 	      	      3600 IN NS	ns2.example.org.
+*.wild.example.org.	      3600 IN A		192.0.2.15
+a.*.emptywild.example.org.    3600 IN AAAA	2001:db8::2
+
+;; additional for an answer RRset (MX) as a result of wildcard
+;; expansion
+*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
+
+;; CNAME
+alias.example.org. 3600 IN CNAME cname.example.org.
+
+;; DNAME
+dname.example.org. 3600 IN DNAME dname.example.com.
+
+;; DNAME under a NS (strange one)
+deepdname.c.example.org. 3600 IN DNAME deepdname.example.com.
diff --git a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
index 41f3690..182cca6 100644
--- a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
+++ b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
@@ -143,11 +143,13 @@ protected:
 void
 checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
                  const RRClass& expected_class, const RRType& expected_type,
+                 const uint32_t expected_ttl,
                  size_t expected_rdatacount, size_t expected_sigcount)
 {
     EXPECT_EQ(expected_name, actual_rrset.getName());
     EXPECT_EQ(expected_class, actual_rrset.getClass());
     EXPECT_EQ(expected_type, actual_rrset.getType());
+    EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
     EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
     EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
 }
@@ -176,30 +178,30 @@ createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
 TEST_F(TreeNodeRRsetTest, create) {
     // Constructed with RRSIG, and it should be visible.
     checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
-                     www_name_, rrclass_, RRType::A(), 2, 1);
+                     www_name_, rrclass_, RRType::A(), 3600, 2, 1);
     // Constructed with RRSIG, and it should be invisible.
     checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
-                     www_name_, rrclass_, RRType::A(), 2, 0);
+                     www_name_, rrclass_, RRType::A(), 3600, 2, 0);
     // Constructed without RRSIG, and it would be visible (but of course won't)
     checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
-                     origin_name_, rrclass_, RRType::NS(), 1, 0);
+                     origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
     // Constructed without RRSIG, and it should be visible
     checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
-                     origin_name_, rrclass_, RRType::NS(), 1, 0);
+                     origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
     // RRSIG-only case (note the RRset's type is covered type)
     checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
                                   true),
-                     www_name_, rrclass_, RRType::TXT(), 0, 1);
+                     www_name_, rrclass_, RRType::TXT(), 3600, 0, 1);
     // RRSIG-only case (note the RRset's type is covered type), but it's
     // invisible
     checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
                                   false),
-                     www_name_, rrclass_, RRType::TXT(), 0, 0);
+                     www_name_, rrclass_, RRType::TXT(), 3600, 0, 0);
     // Wildcard substitution
     checkBasicFields(*createRRset(match_name_, rrclass_,
                                   wildcard_node_, wildcard_rdataset_,
                                   true),
-                     match_name_, rrclass_, RRType::A(), 2, 1);
+                     match_name_, rrclass_, RRType::A(), 3600, 2, 1);
 }
 
 // The following two templated functions are helper to encapsulate the
@@ -572,7 +574,6 @@ TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
 
     TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
 
-    EXPECT_THROW(rrset.getTTL(), isc::Unexpected);
     EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
     EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
     EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
diff --git a/src/lib/datasrc/memory/treenode_rrset.cc b/src/lib/datasrc/memory/treenode_rrset.cc
index 5c333ff..fb7cafc 100644
--- a/src/lib/datasrc/memory/treenode_rrset.cc
+++ b/src/lib/datasrc/memory/treenode_rrset.cc
@@ -58,7 +58,13 @@ TreeNodeRRset::getName() const {
 
 const RRTTL&
 TreeNodeRRset::getTTL() const {
-    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+    if (ttl_ == NULL) {
+        util::InputBuffer ttl_buffer(rdataset_->getTTLData(),
+                                     sizeof(uint32_t));
+        ttl_ = new RRTTL(ttl_buffer);
+    }
+
+    return (*ttl_);
 }
 
 void
diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h
index dd94245..bbaeff5 100644
--- a/src/lib/datasrc/memory/treenode_rrset.h
+++ b/src/lib/datasrc/memory/treenode_rrset.h
@@ -112,7 +112,7 @@ public:
                   const RdataSet* rdataset, bool dnssec_ok) :
         node_(node), rdataset_(rdataset),
         rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
-        dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL)
+        dnssec_ok_(dnssec_ok), name_(NULL), realname_(NULL), ttl_(NULL)
     {}
 
     /// \brief Constructor for wildcard-expanded owner name.
@@ -132,11 +132,13 @@ public:
                   bool dnssec_ok) :
         node_(node), rdataset_(rdataset),
         rrsig_count_(rdataset_->getSigRdataCount()), rrclass_(rrclass),
-        dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname))
+        dnssec_ok_(dnssec_ok), name_(NULL), realname_(new dns::Name(realname)),
+	ttl_(NULL)
     {}
 
     virtual ~TreeNodeRRset() {
         delete realname_;
+        delete ttl_;
         delete name_;
     }
 
@@ -257,6 +259,7 @@ private:
     const bool dnssec_ok_;
     mutable dns::Name* name_;
     const dns::Name* const realname_;
+    mutable dns::RRTTL* ttl_;
 };
 
 } // namespace memory
diff --git a/src/lib/datasrc/memory/zone_data.h b/src/lib/datasrc/memory/zone_data.h
index f592680..09306d7 100644
--- a/src/lib/datasrc/memory/zone_data.h
+++ b/src/lib/datasrc/memory/zone_data.h
@@ -43,6 +43,7 @@ namespace memory {
 
 typedef DomainTree<RdataSet> ZoneTree;
 typedef DomainTreeNode<RdataSet> ZoneNode;
+typedef DomainTreeNodeChain<RdataSet> ZoneChain;
 
 /// \brief NSEC3 data for a DNS zone.
 ///
diff --git a/src/lib/datasrc/memory/zone_table.cc b/src/lib/datasrc/memory/zone_table.cc
index f9b2768..a74a61d 100644
--- a/src/lib/datasrc/memory/zone_table.cc
+++ b/src/lib/datasrc/memory/zone_table.cc
@@ -132,6 +132,20 @@ ZoneTable::findZone(const Name& name) const {
     return (FindResult(my_result, node->getData()));
 }
 
+ZoneTable::FindResult
+ZoneTable::setZoneData(const Name& name, ZoneData* data)
+{
+    ZoneTableNode* node(NULL);
+
+    ZoneTableTree::Result result(zones_->find(name, &node));
+
+    if (result != ZoneTableTree::EXACTMATCH) {
+        return (FindResult(result::NOTFOUND, NULL));
+    } else {
+        return (FindResult(result::SUCCESS, node->setData(data)));
+    }
+}
+
 } // end of namespace memory
 } // end of namespace datasrc
 } // end of namespace isc
diff --git a/src/lib/datasrc/memory/zone_table.h b/src/lib/datasrc/memory/zone_table.h
index 2faf606..8ad6213 100644
--- a/src/lib/datasrc/memory/zone_table.h
+++ b/src/lib/datasrc/memory/zone_table.h
@@ -86,11 +86,11 @@ public:
     /// \brief Result data of findZone() method.
     struct FindResult {
         FindResult(result::Result param_code,
-                   const ZoneData* param_zone_data) :
+                   ZoneData* param_zone_data) :
             code(param_code), zone_data(param_zone_data)
         {}
         const result::Result code;
-        const ZoneData* const zone_data;
+        ZoneData* const zone_data;
     };
 
 private:
@@ -185,6 +185,16 @@ public:
     /// \return A \c FindResult object enclosing the search result (see above).
     FindResult findZone(const isc::dns::Name& name) const;
 
+    /// Override the ZoneData for a node (zone) in the zone tree.
+    ///
+    /// \throw none
+    ///
+    /// \param name A domain name for which the zone data is set.
+    /// \param data The new zone data to set.
+    /// \return A \c FindResult object containing the old data if the
+    /// zone was found.
+    FindResult setZoneData(const isc::dns::Name& name, ZoneData* data);
+
 private:
     boost::interprocess::offset_ptr<ZoneTableTree> zones_;
 };



More information about the bind10-changes mailing list