BIND 10 trac2414, updated. aa1643faad572dfc6c91d0fac707f4cac4d5b7a6 [2414] Merge branch 'master' into trac2414

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Nov 7 17:50:27 UTC 2012


The branch, trac2414 has been updated
       via  aa1643faad572dfc6c91d0fac707f4cac4d5b7a6 (commit)
       via  b4921859312474c4cd3f1c3d3f78392f85698c41 (commit)
       via  560a71919bda37ad20e54efed07183acc5413a8a (commit)
       via  dce5a5557d44d52455e9cfbafe2ad882e6b930db (commit)
       via  d5deb4caf172b2d8109b7bfab40b1d3e49526cdb (commit)
       via  5017e9a8d6993c8604722b2385c450b0b54b5cf8 (commit)
       via  24862b62d9a01d277ea5c5b99666f04b1951de64 (commit)
       via  31745f2d5978086e2d0cc40524efc2e5a45b832b (commit)
       via  ff7cec44f4ee5c59593278558269bdd41a6e5719 (commit)
       via  265fb98ba1f1daab43779b8eaeb6d7f8775dd772 (commit)
       via  36514ddc884c02a063e166d44319467ce6fb1d8f (commit)
       via  2c70372efd0b0ddb8059d5bbe9eb195f024d4df8 (commit)
       via  f94b033cbf46821f0391b5512a5fdcb70ffbc6e4 (commit)
       via  b966ebcc19a55c4d01825010ee1e707ba95293e8 (commit)
       via  9af419deb6691876904d456faf0da731cb5c96b5 (commit)
       via  ccb0973459b702152f79ea23841269a379580850 (commit)
       via  4d7d67838cb15ce75ceab0664e7b25e4b58d658a (commit)
       via  ed1a2ad7dfa4d0c87e9ab3fa446acdbc29f6cc24 (commit)
       via  9f5232047b3a68299192a7885068430af6f8859b (commit)
       via  38ebe09ee1c3ed6906f6a439e5ac9088b665420e (commit)
       via  18520152ce6097c22024bca864ec53643be3ef84 (commit)
       via  c19c1fff5013cec501f7c36fe272ecf2bc65d695 (commit)
       via  1f2f8e9d497246e335dcbb9604521eb70ccd4524 (commit)
       via  80b227af0bf08ac9e7bdf645b11e8593b717add3 (commit)
       via  a2090f1d5702cbc00da7b52a2c1247bd37334ebc (commit)
       via  f142087f73411145565baf4a3280607413162e06 (commit)
       via  3cafab05e08e6aeb0eac010a3f12ffa3e8e734dc (commit)
       via  81cf2b7ac4c5ee295c0d654c4ce24911dd23c2c4 (commit)
       via  686594e391c645279cc4a95e0e0020d1c01fba7e (commit)
       via  b1b6e109cda0458811ab61be4263a51b5520ce0b (commit)
       via  e32617d592d204e4c5c27ff3ecab77ea4e258cb7 (commit)
       via  eb92bed532b40a0904f8397ba108d96f152abbbf (commit)
       via  5ac6e8b56caad02994fd9352e3427e975e72f44a (commit)
       via  710bac35169ec02b73a82cab2c4ce31874a8e440 (commit)
       via  b27d601b91d09bbaa5c20805331c7efa95eac944 (commit)
       via  6b8bf7d03e221e9a1802bcddda66b2b424930042 (commit)
       via  6847454e58c65b33e61fe03b16b3aa5a63df8d8d (commit)
       via  9a15250b2c74f8b637cf1f32f79cc626cb05d0e5 (commit)
       via  f2b5789e89f48e5a045f8c921f09b5c3caddea3a (commit)
       via  d12d0c3368d26ab81c5a3c63bad696ee2282530f (commit)
       via  8df24b61c35bf2e4963c195e1c927980e16c4a9c (commit)
       via  9f360b39c79ef71edb474fb6fe443ff30b479fb6 (commit)
       via  5e97750478c1a655d85107bbbda556c8e8bcfec0 (commit)
       via  b1f95f0359a425b9d758cfacffc75a4517fb342f (commit)
       via  82c05da76c188848bb0c0fa2e93c0e90178b5008 (commit)
       via  5ee9df691d301f7a6b3c422013336f229292da35 (commit)
       via  240c5dfdc3f777eb49b41423dbaec2f9479c4eb6 (commit)
       via  6ad5fcf5b5bf4ca4f0295585ac8ad1521baf062a (commit)
       via  e8a6aee2464c295e476eff03448e39d386747140 (commit)
       via  42298cee62c386318f248bcae47d99a39094a10a (commit)
       via  88a8de015f4652fd73164457414f72ab43dcf681 (commit)
       via  0192fd73649d14ade8dc284c64f38d5e0d8b0705 (commit)
       via  e75c686cd9c14f4d6c2a242a0a0853314704fee9 (commit)
       via  cb8128c933f47c23596bce0dd4eaaf77c6af2b6e (commit)
       via  0dd898f80907bcb0180858a3a1b2811c803012a9 (commit)
       via  ba71154440e841836726bbc6b9088f095d14f7d1 (commit)
       via  51e26dc96b447d4f60b6aa2c428bfcfe6e6a4d04 (commit)
       via  c8d6f38ab7cea07e3bc75a5b4cec8207c2db61fd (commit)
       via  17749f1ef367d560e5b878e814352e8b8082542e (commit)
       via  08baa5371d0939356ba56f24f2d537834f7e9bac (commit)
       via  8b36f92a0e51a40702a2fa5db7eabe5344adfeaa (commit)
       via  fd51de64c1738d586791060e3855f414f3cdcdc7 (commit)
       via  cfcad4db4f9da2525f7c9cddcdd52660f60e4afa (commit)
       via  127e416cb3b2d08b2cd9356ca7c8bd109dbef95d (commit)
       via  c4159fc5c746ee7df7aae8c590789e675050d75c (commit)
       via  b2d9be92c95432c92d4996bd999960e08edcc9bc (commit)
       via  d3bdb3c1c1dca514322fefdaac18a253364dfd2f (commit)
       via  0be210a2f53811e07a7ff0aad0aa388c3ef6048c (commit)
       via  8d2205ed3fc327862a0e3a7169e0432f221d514b (commit)
       via  67500d3bd8ae816e5ad73cf475d49f34546db639 (commit)
       via  698ad7c5546e6f0e44be3d9ee1ae57e541a9f968 (commit)
       via  61d7c3959eb991b22bc1c0ef8f4ecb96b65d9325 (commit)
       via  9843e33b58ce12f13fc34fe27c7ef0e4042bd506 (commit)
       via  b5ca6ac342e49edb73ab75938de20c8fd3f6e8b8 (commit)
       via  27e863962f01a9dc8ac5c756ff881cb585c00abf (commit)
       via  1c12d2036d9e92d0475f421406bdca6839b04326 (commit)
       via  7735c8e85cb8275ee5bd1918bff809612248c43d (commit)
       via  59a449f506948e2371ffa87dcd19059388bd1657 (commit)
       via  7ec750c09a3828d28362bc557816e24a6b2207cf (commit)
       via  041dbf514a284b9c14f3a803dbaa8e4260b804f2 (commit)
       via  7dff35a176bcf41d83a14eb5b3fbca6a7dcfafb8 (commit)
       via  0187917d0b77b34997c4666d27f916cd8f6b4dd4 (commit)
       via  7187c1bb9c1b8a1d01843b8b3328454a9bce4d4d (commit)
       via  105208c1e8cd0a77cb8960013a391cc7b48f7a22 (commit)
       via  f4390da9f83529b6c3ed4383d7709e9ede1c3d5b (commit)
       via  40ae159a7ae9c1c1049bd8c18e37ed086c1aafae (commit)
       via  9281b972a87becd0f85b747d5d939f8f2e317247 (commit)
       via  8a0af829c62d143ebb80f99131f7bcb2918add82 (commit)
       via  4965591081ad8457f5ff0f3fe3c581f8e1a4da54 (commit)
       via  83a6c4414e87db481e684f8b6757182a5b8a2cae (commit)
       via  7b11fd33ba9b3b0548a1b0e72b337fe5e3843b3b (commit)
       via  7151128386c71f20f2e51a156d1fb1746fcd7bfa (commit)
       via  3271dcc47f185103c208585d1df8a92e50274f7a (commit)
       via  ce0a6a9742cd2c18cb4c33ad0f174690ed1cbfc9 (commit)
       via  2c8d3ac2d8d62ef77c0f888a7c334689ebcd9b5b (commit)
       via  a6093a8ef88a556bb0c6094d11863e700ec8242f (commit)
       via  cb9e761c578cc1de9421eb5e5c1a45c3d9145239 (commit)
       via  7fb91131cdfc5778e241cb247e3c2713dfe2ca3a (commit)
       via  8aa5e22a0fc048058c3b45d1c2fc76065e0ac8bd (commit)
       via  05dbb62986e1b0260156658896c051cde3c6d53f (commit)
       via  e62b6417336ebdaeda560e47b177e617ee4551e5 (commit)
       via  a88ab93f5a8722d16f96a4e58952042ff2564751 (commit)
       via  33fd1fb0ed8ebfceb01e8a84b1196af29dffde92 (commit)
       via  844348f6a9c5d4670f96750a2c94ade6778891d9 (commit)
       via  57b907eddc75241956102dd4de70d882c1b1fe63 (commit)
       via  a311ab52fe83d602a2ac3daa12314df7de258bae (commit)
       via  1b63f7d9df1621053c71ec3ef546a8cae024dffb (commit)
       via  cf72f036909c786091f845a736bfd6fcfd15a7ac (commit)
       via  e4850b8e75f2d8fd4924b5060cbf3c4458d3513d (commit)
       via  aade8ac800d58c46dad9ab862cf0d6e85174b17d (commit)
       via  29ff6bebc9eef580849d063cfb58bc8e053a03e6 (commit)
       via  a54864927bc88eebbfb0f9515207ba9a3cacaa3e (commit)
       via  462af9f5a374d7c60c8ac2b38e6d663f2af41cc3 (commit)
       via  f48cb0979513b642ab8cea30be5a3caa10fff958 (commit)
       via  30873bc3691ff9be1d9ce56dfb4c3dc30e4dabbc (commit)
       via  ab6215d2e93f0bc0206f4503a9e5b0ea62bf65f3 (commit)
       via  fd2e13361be9f4e4fb4e1098bae2058de33432f7 (commit)
       via  f6af1e6965348482deb45174ba7b4dcb6e9cef61 (commit)
       via  d8b60408e4843a88d15e3cd95dfeb898cea2a8fd (commit)
       via  99bdc7de647ee1abb8401cc1fd8337462173e7f8 (commit)
       via  a6ca17646b6bdf23f36d523bb56f3bb91e1dbf39 (commit)
       via  4b1c96c8a9bba522a512d07b23e36feebaec10bd (commit)
       via  d12b460fbb26cfbd968bf0db25c1d0d3a8d5fb4a (commit)
       via  3b3b42295d636901e0647d0bba3a4f3677af50a0 (commit)
       via  0bc630b2e4ab6efd3f837f2dd6f5e771e6a71b3c (commit)
       via  c37eaa23d920b79d3f344c8fbafbdcda37231b47 (commit)
       via  a2224a3c4ea8bcfc685190d78e539608e76006f4 (commit)
       via  eac4d4fdbc978c037d83081a3a7d99db4a8135bd (commit)
       via  edcbc935eb6ddc4bb5fac1e76e8eac100e783e74 (commit)
       via  7ff2ddee910f9a53bd1dd47c432c99a280a6d076 (commit)
       via  3519306b1c9d4bcaf8d6758ca65ad4a371296346 (commit)
       via  64b2a61c6d242b974169b72542be45a57630ec8d (commit)
       via  03499b185e1dca3807f4c09cd36ba066f25bc918 (commit)
       via  4475bf75419cba6488628a2eb6f3b19fc795ae05 (commit)
       via  9e5e49a5c857b592cc08ba8e3db6a00c9a7fd179 (commit)
       via  5da59e27b2f165d089a8a2a936b4e9eb21387fcc (commit)
       via  08e5c7f5394f449a3e5b7128eacdf5b7a096aa5e (commit)
       via  566ab87733df04e288749e8ab5fb7c1a2c1156cf (commit)
       via  f687a6f097f17344cdf298e33b6592fd41fc8e43 (commit)
       via  c6f40e97e8cde76793b79c54d3611fe9914bc166 (commit)
       via  88259891ae9cdc24ac5e48b5c3b0cb6615f73d19 (commit)
       via  3f3183672b5d824d03fb494dd0d21eaf5d1241cb (commit)
       via  683f4b7420d46fe6a92d3e40225b427eaca1d9f7 (commit)
       via  d899142df0fe588d8f4fa02f4120993f69cdea31 (commit)
       via  29047b54a17c2bc1d27096ea21522e928b51989f (commit)
       via  dbabdcc3c8c103a56a6da5187a97f2748adfa467 (commit)
       via  7eb5518a386019fd723fb2484eb40f13ff283515 (commit)
       via  0aa3d59397af2c127758616bdd93c22d3176d28b (commit)
       via  a4560fb5f2093d4619fb159d77501dd7e6978d35 (commit)
       via  01c85b2bf83b55be659ebe1526eaa1c5aa55b6aa (commit)
       via  07085635d4c12c77ac979ea9755d38a2a40f77b4 (commit)
       via  7d777cee74391169cd589dab1330eda699d2b8d6 (commit)
       via  54a03955995437e3cb380ef5ffba44d61be04213 (commit)
       via  0dfa29e875e3a668e7b6718b510c12df590faec7 (commit)
       via  b4a991767173f93daa5100909740413530c4445d (commit)
       via  a582a2143369f6848cae1bb82301f5a657293f17 (commit)
       via  41fa47c50d302aa71e9aee629c28ed9cb4fcd065 (commit)
       via  3f6ddec37ff9bb49440ce6ceaf02842c74d0b865 (commit)
       via  c638fb4e2776b24b7ecf3c071086056d5cc9d78f (commit)
       via  43f01176f71c387638ff876b7ce31dd42f6bdb9d (commit)
       via  124780d941405d05a2a2906160a3c479a8417abc (commit)
       via  67a6dccbdd9fe5b08b80224fd395fab676cb704c (commit)
       via  fb4eb0b2229e58f19526247193c427fe7af8b9b2 (commit)
       via  c522619b6b3555c5fa0fc8367b6ebc4e72e9335d (commit)
       via  35eb49f72a3dceb354e300469d0c4f342ddb3c8a (commit)
       via  a8a40b6805377c49a9671bd93cb510bfef946a87 (commit)
       via  55ca986dc44bb8059712619dadb64c359356e00a (commit)
       via  c1d90c602c39b7b49e7c8ab802956118f8846cd7 (commit)
       via  72941a64efbe4843657cbb05f8945f7c6dc0e3a0 (commit)
       via  3c3bea55f38dac4d8fcc9c0cda55c6382d6c2cd7 (commit)
       via  307071b44a3b5e5e3e8d3cc75b00c69d81fefaee (commit)
       via  a7adf4e4fe023903dd4ca362a6cfa6cafc06241d (commit)
       via  98cdad7fa22451ef1ca067ee747c70a31262ee61 (commit)
       via  c76e3fc072e8b1b771b55b6547889863ba263f31 (commit)
       via  50c395e0d786b2330919498c37149edf22808a52 (commit)
      from  f25544eeedb66a4f5bb0e0c41387e2e58555daa9 (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 aa1643faad572dfc6c91d0fac707f4cac4d5b7a6
Merge: f25544e b492185
Author: Stephen Morris <stephen at isc.org>
Date:   Wed Nov 7 17:46:02 2012 +0000

    [2414] Merge branch 'master' into trac2414
    
    Conflicts:
    	src/bin/dhcp6/dhcp6_messages.mes
    	src/bin/dhcp6/dhcp6_srv.cc
    	src/bin/dhcp6/dhcp6_srv.h
    	src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
    	src/lib/dhcp/addr_utilities.cc
    	src/lib/dhcp/tests/cfgmgr_unittest.cc
    
    Files automerged successfully:
        src/lib/dhcp/alloc_engine.cc
        src/lib/dhcp/subnet.h
        src/lib/dhcp/tests/alloc_engine_unittest.cc

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

Summary of changes:
 ChangeLog                                          |   23 +
 Makefile.am                                        |    2 +-
 configure.ac                                       |   32 +-
 doc/Doxyfile                                       |    2 +-
 examples/README                                    |   15 +
 examples/configure.ac                              |    7 +
 examples/m4/ax_boost_include.m4                    |    4 +-
 examples/m4/ax_isc_bind10.m4                       |   40 +-
 examples/m4/ax_isc_rpath.m4                        |   46 ++
 src/bin/auth/Makefile.am                           |    3 +-
 src/bin/auth/auth.spec.pre.in                      |  140 ++---
 src/bin/auth/auth_messages.mes                     |   19 +-
 src/bin/auth/auth_srv.cc                           |  104 ++--
 src/bin/auth/auth_srv.h                            |   46 +-
 src/bin/auth/benchmarks/Makefile.am                |    3 +-
 src/bin/auth/command.cc                            |   46 +-
 src/bin/auth/datasrc_clients_mgr.h                 |  206 ++++++-
 src/bin/auth/statistics.cc                         |  296 ++++++----
 src/bin/auth/statistics.h                          |  223 ++++---
 src/bin/auth/statistics_items.h                    |  609 ++++++++++++++++++++
 src/bin/auth/tests/Makefile.am                     |    3 +-
 src/bin/auth/tests/auth_srv_unittest.cc            |  232 ++++++--
 src/bin/auth/tests/command_unittest.cc             |  275 ---------
 .../auth/tests/datasrc_clients_builder_unittest.cc |  347 ++++++++++-
 src/bin/auth/tests/datasrc_clients_mgr_unittest.cc |   49 ++
 src/bin/auth/tests/statistics_unittest.cc          |  372 ++----------
 src/bin/auth/tests/test_datasrc_clients_mgr.cc     |    2 +
 src/bin/auth/tests/test_datasrc_clients_mgr.h      |    3 +-
 src/bin/bindctl/tests/bindctl_test.py              |    5 +-
 src/bin/dhcp6/Makefile.am                          |    1 +
 src/bin/dhcp6/config_parser.cc                     |  472 ++++++++++++++-
 src/bin/dhcp6/dhcp6.dox                            |    2 +
 src/bin/dhcp6/dhcp6.spec                           |   69 ++-
 src/bin/dhcp6/dhcp6_messages.mes                   |   15 +
 src/bin/dhcp6/dhcp6_srv.cc                         |   83 ++-
 src/bin/dhcp6/dhcp6_srv.h                          |   26 +-
 src/bin/dhcp6/tests/Makefile.am                    |    1 +
 src/bin/dhcp6/tests/config_parser_unittest.cc      |  510 +++++++++++++++-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  206 ++++++-
 src/bin/sysinfo/.gitignore                         |    3 +-
 src/bin/sysinfo/Makefile.am                        |    1 +
 .../run_dbutil.sh.in => sysinfo/run_sysinfo.sh.in} |   24 +-
 src/bin/sysinfo/sysinfo.py.in                      |    6 +-
 src/bin/tests/process_rename_test.py.in            |    7 +
 src/lib/bench/example/search_bench.cc              |    1 -
 .../memory/benchmarks/rdata_reader_bench.cc        |    1 -
 .../memory/benchmarks/rrset_render_bench.cc        |    1 -
 src/lib/datasrc/tests/client_list_unittest.cc      |    8 +-
 src/lib/dhcp/addr_utilities.cc                     |   13 +-
 src/lib/dhcp/alloc_engine.cc                       |    6 +-
 src/lib/dhcp/libdhcp++.cc                          |  102 ++++
 src/lib/dhcp/libdhcp++.h                           |   57 +-
 src/lib/dhcp/option_definition.cc                  |    7 +-
 src/lib/dhcp/option_definition.h                   |   89 ++-
 src/lib/dhcp/subnet.h                              |    7 +-
 src/lib/dhcp/tests/alloc_engine_unittest.cc        |   36 +-
 src/lib/dhcp/tests/cfgmgr_unittest.cc              |    1 +
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |   82 +++
 src/lib/dhcp/tests/option_definition_unittest.cc   |   50 ++
 src/lib/dns/Makefile.am                            |    4 +
 src/lib/dns/benchmarks/message_renderer_bench.cc   |    1 -
 .../unittests/resource.cc => dns/master_lexer.cc}  |   36 +-
 src/lib/dns/master_lexer.h                         |  241 ++++++++
 src/lib/dns/master_lexer_inputsource.cc            |  154 +++++
 src/lib/dns/master_lexer_inputsource.h             |  164 ++++++
 src/lib/dns/name.cc                                |  135 ++++-
 src/lib/dns/name.h                                 |   42 ++
 src/lib/dns/tests/Makefile.am                      |    2 +
 .../dns/tests/master_lexer_inputsource_unittest.cc |  325 +++++++++++
 src/lib/dns/tests/master_lexer_token_unittest.cc   |  156 +++++
 src/lib/dns/tests/name_unittest.cc                 |  124 +++-
 src/lib/python/isc/sysinfo/sysinfo.py              |   37 +-
 src/lib/python/isc/sysinfo/tests/sysinfo_test.py   |   65 ++-
 src/lib/statistics/Makefile.am                     |   11 +-
 src/lib/statistics/counter.cc                      |   82 ---
 src/lib/statistics/counter.h                       |   44 +-
 src/lib/statistics/counter_dict.cc                 |  265 ---------
 src/lib/statistics/counter_dict.h                  |  180 +++---
 src/lib/statistics/tests/Makefile.am               |    1 -
 tests/lettuce/features/ddns_system.feature         |    7 +-
 .../lettuce/features/inmemory_over_sqlite3.feature |    2 +-
 81 files changed, 5292 insertions(+), 1827 deletions(-)
 create mode 100644 examples/m4/ax_isc_rpath.m4
 create mode 100644 src/bin/auth/statistics_items.h
 copy src/bin/{dbutil/run_dbutil.sh.in => sysinfo/run_sysinfo.sh.in} (60%)
 copy src/lib/{util/unittests/resource.cc => dns/master_lexer.cc} (51%)
 create mode 100644 src/lib/dns/master_lexer.h
 create mode 100644 src/lib/dns/master_lexer_inputsource.cc
 create mode 100644 src/lib/dns/master_lexer_inputsource.h
 create mode 100644 src/lib/dns/tests/master_lexer_inputsource_unittest.cc
 create mode 100644 src/lib/dns/tests/master_lexer_token_unittest.cc
 delete mode 100644 src/lib/statistics/counter.cc
 delete mode 100644 src/lib/statistics/counter_dict.cc

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index b7ed58b..2b564e1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+499.	[func]		team
+	The b10-auth 'loadzone' command now uses the internal thread
+	introduced in 495 to (re)load a zone in the background, so that
+	query processing isn't blocked while loading a zone.
+	(Trac #2213, git 686594e391c645279cc4a95e0e0020d1c01fba7e)
+
+498.	[func]		marcin
+	Implemented DHCPv6 option values configuration using configuration
+	manager. In order to set values for data fields carried by the
+	particular option, user specifies the string of hexadecimal digits
+	that is in turn converted to binary data and stored into option buffer.
+	More user friendly way of option content specification is planned.
+	(Trac #2318, git e75c686cd9c14f4d6c2a242a0a0853314704fee9)
+
+497.	[bug]		jinmei
+	Fixed several issues in isc-sysinfo:
+	- make sure it doesn't report a negative value for free memory
+	  size (this happened on FreeBSD, but can possibly occur on other
+	  BSD variants)
+	- correctly identifies the SMP support in kernel on FreeBSD
+	- print more human readable uptime as well as the time in seconds
+	(Trac #2297, git 59a449f506948e2371ffa87dcd19059388bd1657)
+
 496.	[func]		tomek
 	DHCPv6 Allocation Engine implemented. It allows address allocation
 	from the configured subnets/pools. It currently features a single
diff --git a/Makefile.am b/Makefile.am
index 1ed0d63..2f3ce85 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-ACLOCAL_AMFLAGS = -I m4macros ${ACLOCAL_FLAGS}
+ACLOCAL_AMFLAGS = -I m4macros -I examples/m4 ${ACLOCAL_FLAGS}
 # ^^^^^^^^ This has to be the first line and cannot come later in this
 # Makefile.am due to some bork in some versions of autotools.
 
diff --git a/configure.ac b/configure.ac
index a9d5a81..754d667 100644
--- a/configure.ac
+++ b/configure.ac
@@ -64,25 +64,9 @@ AM_CONDITIONAL(USE_CLANGPP, test "X${CLANGPP}" = "Xyes")
 
 # Linker options
 
-# check -R and -Wl,-R rather than gcc specific -rpath to be as portable
-# as possible.
-AC_MSG_CHECKING([whether -R flag is available in linker])
-LDFLAGS_SAVED="$LDFLAGS"
-LDFLAGS="$LDFLAGS -R/usr/lib"
-AC_TRY_LINK([],[],
-    [ AC_MSG_RESULT(yes)
-        rpath_flag=-R
-    ],[ AC_MSG_RESULT(no)
-        AC_MSG_CHECKING([whether -Wl,-R flag is available in linker])
-        LDFLAGS="$LDFLAGS_SAVED -Wl,-R"
-        AC_TRY_LINK([], [],
-            [ AC_MSG_RESULT(yes)
-                rpath_flag=-Wl,-R
-            ],[ AC_MSG_RESULT(no)
-                 rpath_flag=no
-            ])
-    ])
-LDFLAGS=$LDFLAGS_SAVED
+# check -R, "-Wl,-R" or -rpath (we share the AX function defined in
+#  examples/m4)
+AX_ISC_RPATH
 
 # Compiler dependent settings: define some mandatory CXXFLAGS here.
 # We also use a separate variable B10_CXXFLAGS.  This will (and should) be
@@ -332,10 +316,10 @@ fi
 # modules, we embed the path to the modules when possible.  We do this even
 # when the path is known in the common operational environment (e.g. when
 # it's stored in a common "hint" file) for simplicity.
-if test $rpath_flag != no; then
+if test "x$ISC_RPATH_FLAG" != "x"; then
 	python_rpath=
 	for flag in ${PYTHON_LDFLAGS}; do
-		python_rpath="${python_rpath} `echo $flag | sed -ne "s/^\(\-L\)/${rpath_flag}/p"`"
+		python_rpath="${python_rpath} `echo $flag | sed -ne "s/^\(\-L\)/${ISC_RPATH_FLAG}/p"`"
 	done
 	PYTHON_LDFLAGS="${PYTHON_LDFLAGS} ${python_rpath}"
 fi
@@ -701,10 +685,10 @@ for flag in ${BOTAN_LIBS}; do
 done
 
 # See python_rpath for some info on why we do this
-if test $rpath_flag != no; then
+if test "x$ISC_RPATH_FLAG" != "x"; then
     BOTAN_RPATH=
     for flag in ${BOTAN_LIBS}; do
-            BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne "s/^\(\-L\)/${rpath_flag}/p"`"
+            BOTAN_RPATH="${BOTAN_RPATH} `echo $flag | sed -ne "s/^\(\-L\)/${ISC_RPATH_FLAG}/p"`"
     done
 AC_SUBST(BOTAN_RPATH)
 
@@ -1299,6 +1283,7 @@ AC_OUTPUT([doc/version.ent
            src/bin/zonemgr/tests/zonemgr_test
            src/bin/zonemgr/run_b10-zonemgr.sh
            src/bin/sysinfo/sysinfo.py
+           src/bin/sysinfo/run_sysinfo.sh
            src/bin/stats/stats.py
            src/bin/stats/stats_httpd.py
            src/bin/bind10/bind10_src.py
@@ -1377,6 +1362,7 @@ AC_OUTPUT([doc/version.ent
            chmod +x src/bin/loadzone/run_loadzone.sh
            chmod +x src/bin/loadzone/tests/correct/correct_test.sh
            chmod +x src/bin/loadzone/tests/error/error_test.sh
+           chmod +x src/bin/sysinfo/run_sysinfo.sh
            chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            chmod +x src/bin/msgq/run_msgq.sh
            chmod +x src/bin/msgq/tests/msgq_test
diff --git a/doc/Doxyfile b/doc/Doxyfile
index f6b9fa0..cc3b595 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -580,7 +580,7 @@ INPUT                  = ../src/lib/exceptions ../src/lib/cc \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
     ../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
     ../src/lib/util/threads/ ../src/lib/resolve ../src/lib/acl \
-    ../src/bin/dhcp6 ../src/lib/dhcp ../src/bin/dhcp4 \
+    ../src/lib/statistics ../src/bin/dhcp6 ../src/lib/dhcp ../src/bin/dhcp4 \
     ../tests/tools/perfdhcp devel
 
 # This tag can be used to specify the character encoding of the source files
diff --git a/examples/README b/examples/README
index 65f777b..08f53fa 100644
--- a/examples/README
+++ b/examples/README
@@ -30,3 +30,18 @@ to the configure.ac file:
 sinclude(m4/ax_boost_include.m4)
 sinclude(m4/ax_isc_bind10.m4)
 (and same for other m4 files as they are added under m4/)
+
+On some systems, espeically if you have installed the BIND 10
+libraries in an uncommon path, programs linked with the BIND 10
+library may not work at run time due to the "missing" shared library.
+Normally, you should be able to avoid this problem by making sure
+to invoking the program explicitly specifying the path to the library,
+e.g., "LD_LIBRARY_PATH=/usr/local/lib/bind10 ./my_bind10_app", or
+you may not even notice the issue if you have installed BIND 10
+library in a common library path on your system (sometimes you may
+still need to run ldconfig(8) beforehand).  Another option is to embed
+the path to the library in your program.  While this approach is
+controversial, and some people rather choose the alternatives, we
+provide a helper tool in case you want to use this option: see the
+lines using BIND10_RPATH in the sample configure.ac file of this
+directory.
diff --git a/examples/configure.ac b/examples/configure.ac
index 9379687..ef9cce0 100644
--- a/examples/configure.ac
+++ b/examples/configure.ac
@@ -14,6 +14,13 @@ AC_LANG([C++])
 # Checks for BIND 10 headers and libraries
 AX_ISC_BIND10
 
+# We use -R, -rpath etc so the resulting program will be more likekly to
+# "just work" by default.  Embedding a specific library path is a controversial
+# practice, though; if you don't like it you can remove the following setting.
+if test "x$BIND10_RPATH" != "x"; then
+   LDFLAGS="$LDFLAGS $BIND10_RPATH"
+fi
+
 # For the example host program, we require the BIND 10 DNS library
 if test "x$BIND10_DNS_LIB" = "x"; then
    AC_MSG_ERROR([unable to find BIND 10 DNS library needed to build 'host'])
diff --git a/examples/m4/ax_boost_include.m4 b/examples/m4/ax_boost_include.m4
index e41614d..77d19ca 100644
--- a/examples/m4/ax_boost_include.m4
+++ b/examples/m4/ax_boost_include.m4
@@ -34,7 +34,7 @@ if test -z "$with_boost_include"; then
 		fi
 	done
 fi
-CPPFLAGS_SAVES="$CPPFLAGS"
+CPPFLAGS_SAVED="$CPPFLAGS"
 if test "${boost_include_path}" ; then
 	BOOST_CPPFLAGS="-I${boost_include_path}"
 	CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
@@ -57,7 +57,7 @@ AC_TRY_COMPILE([
  CPPFLAGS_BOOST_THREADCONF="-DBOOST_DISABLE_THREADS=1"],
 [AC_MSG_RESULT(yes)])
 
-CPPFLAGS="$CPPFLAGS_SAVES $CPPFLAGS_BOOST_THREADCONF"
+CPPFLAGS="$CPPFLAGS_SAVED $CPPFLAGS_BOOST_THREADCONF"
 AC_SUBST(BOOST_CPPFLAGS)
 
 AC_LANG_RESTORE
diff --git a/examples/m4/ax_isc_bind10.m4 b/examples/m4/ax_isc_bind10.m4
index 63e028c..75c37c5 100644
--- a/examples/m4/ax_isc_bind10.m4
+++ b/examples/m4/ax_isc_bind10.m4
@@ -1,4 +1,4 @@
-dnl @synopsis AX_BIND10
+dnl @synopsis AX_ISC_BIND10
 dnl
 dnl @summary figure out how to build C++ programs using ISC BIND 10 libraries
 dnl
@@ -20,9 +20,18 @@ dnl Checks for other BIND 10 module libraries are option, as not all
 dnl applications need all libraries.  The main configure.ac can handle any
 dnl missing library as fatal by checking whether the corresponding
 dnl BIND10_xxx_LIB is defined.
+dnl
+dnl In addition, it sets the BIND10_RPATH variable to a usable linker option
+dnl to embed the path to the BIND 10 library to the programs that are to be
+dnl linked with the library.  If the developer wants to use the option,
+dnl it can be used as follows:
+dnl if test "x$BIND10_RPATH" != "x"; then
+dnl     LDFLAGS="$LDFLAGS $BIND10_RPATH"
+dnl fi
 
 AC_DEFUN([AX_ISC_BIND10], [
 AC_REQUIRE([AX_BOOST_INCLUDE])
+AC_REQUIRE([AX_ISC_RPATH])
 AC_LANG_SAVE
 AC_LANG([C++])
 
@@ -42,19 +51,20 @@ if test "$bind10_inc_path" = "no"; then
 	fi
    done
 fi
-CPPFLAGS_SAVES="$CPPFLAGS"
+CPPFLAGS_SAVED="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" # boost headers will be used in buffer.h
 if test "${bind10_inc_path}" != "no"; then
    BIND10_CPPFLAGS="-I${bind10_inc_path}"
    CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS"
 fi
 AC_CHECK_HEADERS([util/buffer.h],,
-  AC_MSG_ERROR([Missing a commonly used BIND 10 header files]))
-CPPFLAGS="$CPPFLAGS_SAVES"
+  AC_MSG_ERROR([Missing a commonly used BIND 10 header file]))
+CPPFLAGS="$CPPFLAGS_SAVED"
 AC_SUBST(BIND10_CPPFLAGS)
 
 # Check for BIND10 libraries
 CPPFLAGS_SAVED="$CPPFLAGS"
-CPPFLAGS="$CPPFLAGS $BIND10_CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS $BIND10_CPPFLAGS"
 
 AC_ARG_WITH(bind10-lib,
   AS_HELP_STRING([--with-bind10-lib=PATH],
@@ -70,21 +80,25 @@ fi
 # make sure we have buildable libraries
 AC_MSG_CHECKING([for BIND 10 common library])
 BIND10_COMMON_LIB="-lb10-util -lb10-exceptions"
-LDFLAGS="$LDFLAGS $BIND10_LDFLAGS"
+LDFLAGS_SAVED="$LDFLAGS"
+LDFLAGS_CHECK_COMMON="$LDFLAGS $BIND10_LDFLAGS"
+LIBS_SAVED="$LIBS"
 LIBS="$LIBS $BIND10_COMMON_LIB"
 for d in $bind10_lib_dirs
 do
-  LDFLAGS_SAVED="$LDFLAGS"
-  LDFLAGS="$LDFLAGS -L$d"
+  LDFLAGS="$LDFLAGS_CHECK_COMMON -L$d"
   AC_TRY_LINK([
 #include <util/buffer.h>
 ],[
 isc::util::OutputBuffer buffer(0);
-], [BIND10_LDFLAGS="-L${d}"])
+], [BIND10_LDFLAGS="-L${d}"
+    if test "x$ISC_RPATH_FLAG" != "x"; then
+       BIND10_RPATH="${ISC_RPATH_FLAG}${d}"
+    fi
+    ])
   if test "x$BIND10_LDFLAGS" != "x"; then
      break
   fi
-  LDFLAGS="$LDFLAGS_SAVED"
 done
 if test "x$BIND10_LDFLAGS" != "x"; then
   AC_MSG_RESULT(yes)
@@ -94,7 +108,7 @@ else
 fi
 
 # restore LIBS once at this point
-LIBS="$LIBS_SAVES"
+LIBS="$LIBS_SAVED"
 
 AC_SUBST(BIND10_LDFLAGS)
 AC_SUBST(BIND10_COMMON_LIB)
@@ -111,12 +125,12 @@ isc::dns::RRType rrtype(1);
 ], [BIND10_DNS_LIB="-lb10-dns++"
     AC_MSG_RESULT(yes)],
    [AC_MSG_RESULT(no)])
-LIBS="$LIBS_SAVES"
+LIBS="$LIBS_SAVED"
 AC_SUBST(BIND10_DNS_LIB)
 
 # Restore other flags
 CPPFLAGS="$CPPFLAGS_SAVED"
-LDFLAGS="$LDFLAGS_SAVES"
+LDFLAGS="$LDFLAGS_SAVED"
 
 AC_LANG_RESTORE
 ])dnl AX_ISC_BIND10
diff --git a/examples/m4/ax_isc_rpath.m4 b/examples/m4/ax_isc_rpath.m4
new file mode 100644
index 0000000..91d9b8a
--- /dev/null
+++ b/examples/m4/ax_isc_rpath.m4
@@ -0,0 +1,46 @@
+dnl @synopsis AX_ISC_RPATH
+dnl
+dnl @summary figure out whether and which "rpath" linker option is available
+dnl
+dnl This macro checks if the linker supports an option to embed a path
+dnl to a runtime library (often installed in an uncommon place), such as
+dnl gcc's -rpath option.  If found, it sets the ISC_RPATH_FLAG variable to
+dnl the found option flag.  The main configure.ac can use it as follows:
+dnl if test "x$ISC_RPATH_FLAG" != "x"; then
+dnl     LDFLAGS="$LDFLAGS ${ISC_RPATH_FLAG}/usr/local/lib/some_library"
+dnl fi
+
+AC_DEFUN([AX_ISC_RPATH], [
+
+# We'll tweak both CXXFLAGS and CCFLAGS so this function will work whichever
+# language is used in the main script.  Note also that it's not LDFLAGS;
+# technically this is a linker flag, but we've noticed $LDFLAGS can be placed
+# where the compiler could interpret it as a compiler option, leading to
+# subtle failure mode.  So, in the check below using the compiler flag is
+# safer (in the actual Makefiles the flag should be set in LDFLAGS).
+CXXFLAGS_SAVED="$CXXFLAGS"
+CXXFLAGS="$CXXFLAGS -Wl,-R/usr/lib"
+CCFLAGS_SAVED="$CCFLAGS"
+CCFLAGS="$CCFLAGS -Wl,-R/usr/lib"
+
+# check -Wl,-R and -R rather than gcc specific -rpath to be as portable
+# as possible.  -Wl,-R seems to be safer, so we try it first.  In some cases
+# -R is not actually recognized but AC_TRY_LINK doesn't fail due to that.
+AC_MSG_CHECKING([whether -Wl,-R flag is available in linker])
+AC_TRY_LINK([],[],
+    [ AC_MSG_RESULT(yes)
+        ISC_RPATH_FLAG=-Wl,-R
+    ],[ AC_MSG_RESULT(no)
+        AC_MSG_CHECKING([whether -R flag is available in linker])
+	CXXFLAGS="$CXXFLAGS_SAVED -R"
+	CCFLAGS="$CCFLAGS_SAVED -R"
+        AC_TRY_LINK([], [],
+            [ AC_MSG_RESULT([yes; note that -R is more sensitive about the position in option arguments])
+                ISC_RPATH_FLAG=-R
+            ],[ AC_MSG_RESULT(no) ])
+    ])
+
+CXXFLAGS=$CXXFLAGS_SAVED
+CCFLAGS=$CCFLAGS_SAVED
+
+])dnl AX_ISC_RPATH
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 6ee6677..7d29fcc 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -54,7 +54,7 @@ b10_auth_SOURCES += auth_log.cc auth_log.h
 b10_auth_SOURCES += auth_config.cc auth_config.h
 b10_auth_SOURCES += command.cc command.h
 b10_auth_SOURCES += common.h common.cc
-b10_auth_SOURCES += statistics.cc statistics.h
+b10_auth_SOURCES += statistics.cc statistics.h statistics_items.h
 b10_auth_SOURCES += datasrc_clients_mgr.h
 b10_auth_SOURCES += datasrc_config.h datasrc_config.cc
 b10_auth_SOURCES += main.cc
@@ -74,7 +74,6 @@ b10_auth_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_auth_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_auth_LDADD += $(top_builddir)/src/lib/xfr/libb10-xfr.la
 b10_auth_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
-b10_auth_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
 b10_auth_LDADD += $(top_builddir)/src/lib/util/threads/libb10-threads.la
 b10_auth_LDADD += $(SQLITE_LIBS)
 
diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in
index a471b7a..30a455d 100644
--- a/src/bin/auth/auth.spec.pre.in
+++ b/src/bin/auth/auth.spec.pre.in
@@ -145,7 +145,7 @@
         "item_type": "integer",
         "item_optional": false,
         "item_default": 0,
-        "item_title": "Queries TCP ",
+        "item_title": "Queries TCP",
         "item_description": "A number of total query counts which all auth servers receive over TCP since they started initially"
       },
       {
@@ -181,14 +181,6 @@
         "item_description": "The number of total request counts whose opcode is status"
       },
       {
-        "item_name": "opcode.reserved3",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 3",
-        "item_description": "The number of total request counts whose opcode is 3 (reserved)"
-      },
-      {
         "item_name": "opcode.notify",
         "item_type": "integer",
         "item_optional": true,
@@ -205,84 +197,12 @@
         "item_description": "The number of total request counts whose opcode is update"
       },
       {
-        "item_name": "opcode.reserved6",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 6",
-        "item_description": "The number of total request counts whose opcode is 6 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved7",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 7",
-        "item_description": "The number of total request counts whose opcode is 7 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved8",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 8",
-        "item_description": "The number of total request counts whose opcode is 8 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved9",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 9",
-        "item_description": "The number of total request counts whose opcode is 9 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved10",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 10",
-        "item_description": "The number of total request counts whose opcode is 10 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved11",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 11",
-        "item_description": "The number of total request counts whose opcode is 11 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved12",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 12",
-        "item_description": "The number of total request counts whose opcode is 12 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved13",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 13",
-        "item_description": "The number of total request counts whose opcode is 13 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved14",
-        "item_type": "integer",
-        "item_optional": true,
-        "item_default": 0,
-        "item_title": "Received requests opcode 14",
-        "item_description": "The number of total request counts whose opcode is 14 (reserved)"
-      },
-      {
-        "item_name": "opcode.reserved15",
+        "item_name": "opcode.other",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Received requests opcode 15",
-        "item_description": "The number of total request counts whose opcode is 15 (reserved)"
+        "item_title": "Received requests opcode other",
+        "item_description": "The number of total request counts whose opcode is other (not well-known)"
       },
       {
         "item_name": "rcode.noerror",
@@ -373,52 +293,68 @@
         "item_description": "The number of total responses with rcode 10 (NOTZONE)"
       },
       {
-        "item_name": "rcode.reserved11",
+        "item_name": "rcode.badsigvers",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Sent response with rcode 11",
-        "item_description": "The number of total responses with rcode 11 (reserved)"
+        "item_title": "Sent 'EDNS version not implemented' response",
+        "item_description": "The number of total responses with rcode 16 (BADVERS)"
       },
       {
-        "item_name": "rcode.reserved12",
+        "item_name": "rcode.badkey",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Sent response with rcode 12",
-        "item_description": "The number of total responses with rcode 12 (reserved)"
+        "item_title": "Sent 'Key not recognized' response",
+        "item_description": "The number of total responses with rcode 17 (BADKEY)"
       },
       {
-        "item_name": "rcode.reserved13",
+        "item_name": "rcode.badtime",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Sent response with rcode 13",
-        "item_description": "The number of total responses with rcode 13 (reserved)"
+        "item_title": "Sent 'Signature out of time window' response",
+        "item_description": "The number of total responses with rcode 18 (BADTIME)"
       },
       {
-        "item_name": "rcode.reserved14",
+        "item_name": "rcode.badmode",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Sent response with rcode 14",
-        "item_description": "The number of total responses with rcode 14 (reserved)"
+        "item_title": "Sent 'Bad TKEY Mode' response",
+        "item_description": "The number of total responses with rcode 19 (BADMODE)"
       },
       {
-        "item_name": "rcode.reserved15",
+        "item_name": "rcode.badname",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Sent response with rcode 15",
-        "item_description": "The number of total responses with rcode 15 (reserved)"
+        "item_title": "Sent 'Duplicate key name' response",
+        "item_description": "The number of total responses with rcode 20 (BADNAME)"
       },
       {
-        "item_name": "rcode.badvers",
+        "item_name": "rcode.badalg",
         "item_type": "integer",
         "item_optional": true,
         "item_default": 0,
-        "item_title": "Sent 'EDNS version not implemented' response",
-        "item_description": "The number of total responses with rcode 16 (BADVERS)"
+        "item_title": "Sent 'Algorithm not supported' response",
+        "item_description": "The number of total responses with rcode 21 (BADALG)"
+      },
+      {
+        "item_name": "rcode.badtrunc",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent 'Bad Truncation' response",
+        "item_description": "The number of total responses with rcode 22 (BADTRUNC)"
+      },
+      {
+        "item_name": "rcode.other",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 0,
+        "item_title": "Sent responses with rcode other",
+        "item_description": "The number of total responses with rcode other (not well-known)"
       }
     ]
   }
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index bdbd2fd..221d122 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -61,6 +61,15 @@ the message.
 A debug message, showing when the separate thread for maintaining data
 source clients receives a command from the manager.
 
+% AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR command execution failure: %1
+The separate thread for maintaining data source clients failed to complete a
+command given by the main thread.  In most cases this is some kind of
+configuration or temporary error such as an attempt to load a non-existent
+zone or a temporary DB connection failure.  So the event is just logged and
+the thread keeps running.  In some rare cases, however, this may indicate an
+internal bug and it may be better to restart the entire program, so the log
+message should be carefully examined.
+
 % AUTH_DATASRC_CLIENTS_BUILDER_FAILED data source builder thread stopped due to an exception: %1
 The separate thread for maintaining data source clients has been
 terminated due to some uncaught exception.  When this happens, the
@@ -79,6 +88,11 @@ indicate some run time failure than program errors.  As in the other
 failure case, the thread terminates the entire process immediately
 after logging this message.
 
+% AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE loaded zone %1/%2
+This debug message is issued when the separate thread for maintaining data
+source clients successfully loaded the named zone of the named class as a
+result of the 'loadzone' command.
+
 % AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_CONFIG_ERROR Error in data source configuration: %1
 The thread for maintaining data source clients has received a command to
 reconfigure, but the parameter data (the new configuration) contains an
@@ -164,11 +178,6 @@ has requested the keyring holding TSIG keys from the configuration
 database. It is issued during server startup is an indication that the
 initialization is proceeding normally.
 
-% AUTH_LOAD_ZONE loaded zone %1/%2
-This debug message is issued during the processing of the 'loadzone' command
-when the authoritative server has successfully loaded the named zone of the
-named class.
-
 % AUTH_MEM_DATASRC_DISABLED memory data source is disabled for class %1
 This is a debug message reporting that the authoritative server has
 discovered that the memory data source is disabled for the given class.
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 157ae03..dca8fd0 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -85,6 +85,7 @@ using namespace isc::xfr;
 using namespace isc::asiolink;
 using namespace isc::asiodns;
 using namespace isc::server_common::portconfig;
+using isc::auth::statistics::Counters;
 
 namespace {
 // A helper class for cleaning up message renderer.
@@ -260,7 +261,7 @@ public:
     AbstractSession* xfrin_session_;
 
     /// Query counters for statistics
-    AuthCounters counters_;
+    Counters counters_;
 
     /// Addresses we listen on
     AddressList listen_addresses_;
@@ -285,27 +286,29 @@ public:
 
     /// \brief Resume the server
     ///
-    /// This is a wrapper call for DNSServer::resume(done), if 'done' is true,
-    /// the Rcode set in the given Message is counted in the statistics
-    /// counter.
+    /// This is a wrapper call for DNSServer::resume(done). Query/Response
+    /// statistics counters are incremented in this method.
     ///
     /// This method is expected to be called by processMessage()
     ///
     /// \param server The DNSServer as passed to processMessage()
     /// \param message The response as constructed by processMessage()
-    /// \param done If true, the Rcode from the given message is counted,
-    ///             this value is then passed to server->resume(bool)
+    /// \param stats_attrs Query/response attributes for statistics which is
+    ///                    not in \p messsage.
+    ///                    Note: This parameter is modified inside this method
+    ///                          to store whether the answer has been sent and
+    ///                          the response is truncated.
+    /// \param done If true, it indicates there is a response.
+    ///             this value will be passed to server->resume(bool)
     void resumeServer(isc::asiodns::DNSServer* server,
                       isc::dns::Message& message,
-                      bool done);
+                      statistics::QRAttributes& stats_attrs,
+                      const bool done);
 
 private:
     bool xfrout_connected_;
     AbstractXfroutClient& xfrout_client_;
 
-    /// Increment query counter
-    void incCounter(const int protocol);
-
     // validateStatistics
     bool validateStatistics(isc::data::ConstElementPtr data) const;
 
@@ -494,6 +497,12 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                         OutputBuffer& buffer, DNSServer* server)
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
+    statistics::QRAttributes stats_attrs;
+
+    // statistics: check transport carrying the message (IP, transport)
+    stats_attrs.setQueryIPVersion(io_message.getRemoteEndpoint().getFamily());
+    stats_attrs.setQueryTransportProtocol(
+        io_message.getRemoteEndpoint().getProtocol());
 
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
@@ -503,13 +512,13 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
         // Ignore all responses.
         if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
             LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
-            impl_->resumeServer(server, message, false);
+            impl_->resumeServer(server, message, stats_attrs, false);
             return;
         }
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_HEADER_PARSE_FAIL)
                   .arg(ex.what());
-        impl_->resumeServer(server, message, false);
+        impl_->resumeServer(server, message, stats_attrs, false);
         return;
     }
 
@@ -520,13 +529,13 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
                   .arg(error.getRcode().toText()).arg(error.what());
         makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
-        impl_->resumeServer(server, message, true);
+        impl_->resumeServer(server, message, stats_attrs, true);
         return;
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
                   .arg(ex.what());
         makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
-        impl_->resumeServer(server, message, true);
+        impl_->resumeServer(server, message, stats_attrs, true);
         return;
     } // other exceptions will be handled at a higher layer.
 
@@ -549,21 +558,35 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
                                            **impl_->keyring_));
         tsig_error = tsig_context->verify(tsig_record, io_message.getData(),
                                           io_message.getDataSize());
+        // statistics: check TSIG attributes
+        // SIG(0) is currently not implemented in Auth, but it is implemented
+        // in BIND 9. At the point we support it, the code to check if the
+        // signature is valid would be around here.
+        stats_attrs.setQuerySig(true, false,
+                                tsig_error == TSIGError::NOERROR());
     }
 
     if (tsig_error != TSIGError::NOERROR()) {
         makeErrorMessage(impl_->renderer_, message, buffer,
                          tsig_error.toRcode(), tsig_context);
-        impl_->resumeServer(server, message, true);
+        impl_->resumeServer(server, message, stats_attrs, true);
         return;
     }
 
     const Opcode opcode = message.getOpcode();
     bool send_answer = true;
     try {
-        // update per opcode statistics counter.  This can only be reliable
-        // after TSIG check succeeds.
-        impl_->counters_.inc(message.getOpcode());
+        // statistics: check EDNS
+        //     note: This can only be reliable after TSIG check succeeds.
+        ConstEDNSPtr edns = message.getEDNS();
+        if (edns != NULL) {
+            stats_attrs.setQueryEDNS(true, edns->getVersion() == 0);
+            stats_attrs.setQueryDO(edns->getDNSSECAwareness());
+        }
+
+        // statistics: check OpCode
+        //     note: This can only be reliable after TSIG check succeeds.
+        stats_attrs.setQueryOpCode(opcode.getCode());
 
         if (opcode == Opcode::NOTIFY()) {
             send_answer = impl_->processNotify(io_message, message, buffer,
@@ -605,7 +628,7 @@ AuthSrv::processMessage(const IOMessage& io_message, Message& message,
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
         makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
     }
-    impl_->resumeServer(server, message, send_answer);
+    impl_->resumeServer(server, message, stats_attrs, send_answer);
 }
 
 bool
@@ -622,9 +645,6 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     message.setRcode(Rcode::NOERROR());
 
-    // Increment query counter.
-    incCounter(io_message.getSocket().getProtocol());
-
     if (remote_edns) {
         EDNSPtr local_edns = EDNSPtr(new EDNS());
         local_edns->setDNSSECAwareness(dnssec_ok);
@@ -675,9 +695,6 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
                              OutputBuffer& buffer,
                              auto_ptr<TSIGContext> tsig_context)
 {
-    // Increment query counter.
-    incCounter(io_message.getSocket().getProtocol());
-
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
         makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
@@ -810,19 +827,6 @@ AuthSrvImpl::processUpdate(const IOMessage& io_message) {
 }
 
 void
-AuthSrvImpl::incCounter(const int protocol) {
-    // Increment query counter.
-    if (protocol == IPPROTO_UDP) {
-        counters_.inc(AuthCounters::SERVER_UDP_QUERY);
-    } else if (protocol == IPPROTO_TCP) {
-        counters_.inc(AuthCounters::SERVER_TCP_QUERY);
-    } else {
-        // unknown protocol
-        isc_throw(Unexpected, "Unknown protocol: " << protocol);
-    }
-}
-
-void
 AuthSrvImpl::registerStatisticsValidator() {
     counters_.registerStatisticsValidator(
         boost::bind(&AuthSrvImpl::validateStatistics, this, _1));
@@ -839,10 +843,15 @@ AuthSrvImpl::validateStatistics(isc::data::ConstElementPtr data) const {
 }
 
 void
-AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
+AuthSrvImpl::resumeServer(DNSServer* server, Message& message,
+                          statistics::QRAttributes& stats_attrs,
+                          const bool done) {
     if (done) {
-        counters_.inc(message.getRcode());
+        stats_attrs.answerWasSent();
+        // isTruncated from MessageRenderer
+        stats_attrs.setResponseTruncated(renderer_.isTruncated());
     }
+    counters_.inc(stats_attrs, message);
     server->resume(done);
 }
 
@@ -865,21 +874,6 @@ ConstElementPtr AuthSrv::getStatistics() const {
     return (impl_->counters_.getStatistics());
 }
 
-uint64_t
-AuthSrv::getCounter(const AuthCounters::ServerCounterType type) const {
-    return (impl_->counters_.getCounter(type));
-}
-
-uint64_t
-AuthSrv::getCounter(const Opcode opcode) const {
-    return (impl_->counters_.getCounter(opcode));
-}
-
-uint64_t
-AuthSrv::getCounter(const Rcode rcode) const {
-    return (impl_->counters_.getCounter(rcode));
-}
-
 const AddressList&
 AuthSrv::getListenAddresses() const {
     return (impl_->listen_addresses_);
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index f06a82a..ebd3034 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -222,55 +222,11 @@ public:
     /// \brief Returns statistics data
     ///
     /// This function can throw an exception from
-    /// AuthCounters::getStatistics().
+    /// Counters::getStatistics().
     ///
     /// \return JSON format statistics data.
     isc::data::ConstElementPtr getStatistics() const;
 
-    /// \brief Get the value of counter in the AuthCounters.
-    ///
-    /// This function calls AuthCounters::getStatistics() and
-    /// returns its return value.
-    ///
-    /// This function never throws an exception as far as
-    /// AuthCounters::getStatistics() doesn't throw.
-    ///
-    /// Note: Currently this function is for testing purpose only.
-    ///
-    /// \param type Type of a counter to get the value of
-    ///
-    /// \return the value of the counter.
-
-    uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
-
-    /// \brief Get the value of per Opcode counter in the Auth Counters.
-    ///
-    /// This function calls AuthCounters::getCounter(isc::dns::Opcode) and
-    /// returns its return value.
-    ///
-    /// \note This is a tentative interface as an attempt of experimentally
-    /// supporting more statistics counters.  This should eventually be more
-    /// generalized.  In any case, this method is mainly for testing.
-    ///
-    /// \throw None
-    /// \param opcode The opcode of the counter to get the value of
-    /// \return the value of the counter.
-    uint64_t getCounter(const isc::dns::Opcode opcode) const;
-
-    /// \brief Get the value of per Rcode counter in the Auth Counters.
-    ///
-    /// This function calls AuthCounters::getCounter(isc::dns::Rcode) and
-    /// returns its return value.
-    ///
-    /// \note This is a tentative interface as an attempt of experimentally
-    /// supporting more statistics counters.  This should eventually be more
-    /// generalized.  In any case, this method is mainly for testing.
-    ///
-    /// \throw None
-    /// \param rcode The rcode of the counter to get the value of
-    /// \return the value of the counter.
-    uint64_t getCounter(const isc::dns::Rcode rcode) const;
-
     /**
      * \brief Set and get the addresses we listen on.
      */
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index c525b66..27ddfc5 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -15,7 +15,7 @@ query_bench_SOURCES = query_bench.cc
 query_bench_SOURCES += ../query.h  ../query.cc
 query_bench_SOURCES += ../auth_srv.h ../auth_srv.cc
 query_bench_SOURCES += ../auth_config.h ../auth_config.cc
-query_bench_SOURCES += ../statistics.h ../statistics.cc
+query_bench_SOURCES += ../statistics.h ../statistics.cc ../statistics_items.h
 query_bench_SOURCES += ../auth_log.h ../auth_log.cc
 query_bench_SOURCES += ../datasrc_config.h ../datasrc_config.cc
 
@@ -34,7 +34,6 @@ query_bench_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 query_bench_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
 query_bench_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
-query_bench_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
 query_bench_LDADD += $(top_builddir)/src/lib/util/threads/libb10-threads.la
 query_bench_LDADD += $(SQLITE_LIBS)
 
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index 606fd3f..9fbfb42 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -176,51 +176,7 @@ public:
     virtual ConstElementPtr exec(AuthSrv& server,
                                  isc::data::ConstElementPtr args)
     {
-        if (args == NULL) {
-            isc_throw(AuthCommandError, "Null argument");
-        }
-
-        ConstElementPtr class_elem = args->get("class");
-        RRClass zone_class(class_elem ? RRClass(class_elem->stringValue()) :
-            RRClass::IN());
-
-        ConstElementPtr origin_elem = args->get("origin");
-        if (!origin_elem) {
-            isc_throw(AuthCommandError, "Zone origin is missing");
-        }
-        const Name origin(origin_elem->stringValue());
-
-        DataSrcClientsMgr::Holder holder(server.getDataSrcClientsMgr());
-        boost::shared_ptr<ConfigurableClientList> list =
-            holder.findClientList(zone_class);
-
-        if (!list) {
-            isc_throw(AuthCommandError, "There's no client list for "
-                      "class " << zone_class);
-        }
-
-        switch (list->reload(origin)) {
-            case ConfigurableClientList::ZONE_SUCCESS:
-                // Everything worked fine.
-                LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
-                    .arg(zone_class).arg(origin);
-                return (createAnswer());
-            case ConfigurableClientList::ZONE_NOT_FOUND:
-                isc_throw(AuthCommandError, "Zone " << origin << "/" <<
-                          zone_class << " was not found in any configured "
-                          "data source. Configure it first.");
-            case ConfigurableClientList::ZONE_NOT_CACHED:
-                isc_throw(AuthCommandError, "Zone " << origin << "/" <<
-                          zone_class << " is not served from memory, but "
-                          "directly from the data source. It is not possible "
-                          "to reload it into memory. Configure it to be cached "
-                          "first.");
-            case ConfigurableClientList::CACHE_DISABLED:
-                // This is an internal error. Auth server must have the cache
-                // enabled.
-                isc_throw(isc::Unexpected, "Cache disabled in client list of "
-                          "class " << zone_class);
-        }
+        server.getDataSrcClientsMgr().loadZone(args);
         return (createAnswer());
     }
 };
diff --git a/src/bin/auth/datasrc_clients_mgr.h b/src/bin/auth/datasrc_clients_mgr.h
index 48bae81..38bf162 100644
--- a/src/bin/auth/datasrc_clients_mgr.h
+++ b/src/bin/auth/datasrc_clients_mgr.h
@@ -27,6 +27,7 @@
 
 #include <datasrc/data_source.h>
 #include <datasrc/client_list.h>
+#include <datasrc/memory/zone_writer.h>
 
 #include <auth/auth_log.h>
 #include <auth/datasrc_config.h>
@@ -37,12 +38,26 @@
 #include <boost/noncopyable.hpp>
 
 #include <exception>
+#include <cassert>
 #include <list>
 #include <utility>
 
 namespace isc {
 namespace auth {
 
+/// \brief An exception that is thrown if initial checks for a command fail
+///
+/// This is raised *before* the command to the thread is constructed and
+/// sent, so the application can still handle them (and therefore it is
+/// public, as opposed to InternalCommandError).
+///
+/// And example of its use is currently in loadZone().
+class CommandError : public isc::Exception {
+public:
+    CommandError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 namespace datasrc_clientmgr_internal {
 // This namespace is essentially private for DataSrcClientsMgr(Base) and
 // DataSrcClientsBuilder(Base).  This is exposed in the public header
@@ -55,6 +70,9 @@ enum CommandID {
     RECONFIGURE,  ///< Reconfigure the datasource client lists,
                   ///  the argument to the command is the full new
                   ///  datasources configuration.
+    LOADZONE,     ///< Load a new version of zone into a memory,
+                  ///  the argument to the command is a map containing 'class'
+                  ///  and 'origin' elements, both should have been validated.
     SHUTDOWN,     ///< Shutdown the builder; no argument
     NUM_COMMANDS
 };
@@ -234,6 +252,60 @@ public:
         clients_map_ = new_lists;
     }
 
+    /// \brief Instruct internal thread to (re)load a zone
+    ///
+    /// \param args Element argument that should be a map of the form
+    /// { "class": "IN", "origin": "example.com" }
+    /// (but class is optional and will default to IN)
+    ///
+    /// \exception CommandError if the args value is null, or not in
+    ///                                 the expected format, or contains
+    ///                                 a bad origin or class string
+    void
+    loadZone(data::ConstElementPtr args) {
+        if (!args) {
+            isc_throw(CommandError, "loadZone argument empty");
+        }
+        if (args->getType() != isc::data::Element::map) {
+            isc_throw(CommandError, "loadZone argument not a map");
+        }
+        if (!args->contains("origin")) {
+            isc_throw(CommandError,
+                      "loadZone argument has no 'origin' value");
+        }
+        // Also check if it really is a valid name
+        try {
+            dns::Name(args->get("origin")->stringValue());
+        } catch (const isc::Exception& exc) {
+            isc_throw(CommandError, "bad origin: " << exc.what());
+        }
+
+        if (args->get("origin")->getType() != data::Element::string) {
+            isc_throw(CommandError,
+                      "loadZone argument 'origin' value not a string");
+        }
+        if (args->contains("class")) {
+            if (args->get("class")->getType() != data::Element::string) {
+                isc_throw(CommandError,
+                          "loadZone argument 'class' value not a string");
+            }
+            // Also check if it is a valid class
+            try {
+                dns::RRClass(args->get("class")->stringValue());
+            } catch (const isc::Exception& exc) {
+                isc_throw(CommandError, "bad class: " << exc.what());
+            }
+        }
+
+        // Note: we could do some more advanced checks here,
+        // e.g. check if the zone is known at all in the configuration.
+        // For now these are skipped, but one obvious way to
+        // implement it would be to factor out the code from
+        // the start of doLoadZone(), and call it here too
+
+        sendCommand(datasrc_clientmgr_internal::LOADZONE, args);
+    }
+
 private:
     // This is expected to be called at the end of the destructor.  It
     // actually does nothing, but provides a customization point for
@@ -290,14 +362,27 @@ namespace datasrc_clientmgr_internal {
 /// threads or locks.
 template <typename MutexType, typename CondVarType>
 class DataSrcClientsBuilderBase : boost::noncopyable {
+private:
+    typedef std::map<dns::RRClass,
+                     boost::shared_ptr<datasrc::ConfigurableClientList> >
+    ClientListsMap;
+
 public:
+    /// \brief Internal errors in handling commands.
+    ///
+    /// This exception is expected to be caught within the
+    /// \c DataSrcClientsBuilder implementation, but is defined as public
+    /// so tests can be checked it.
+    class InternalCommandError : public isc::Exception {
+    public:
+        InternalCommandError(const char* file, size_t line, const char* what) :
+            isc::Exception(file, line, what) {}
+    };
+
     /// \brief Constructor.
     ///
     /// It simply sets up a local copy of shared data with the manager.
     ///
-    /// Note: this will take actual set (map) of data source clients and
-    /// a mutex object for it in #2210 or #2212.
-    ///
     /// \throw None
     DataSrcClientsBuilderBase(std::list<Command>* command_queue,
                               CondVarType* cond, MutexType* queue_mutex,
@@ -365,6 +450,11 @@ private:
         }
     }
 
+    void doLoadZone(const isc::data::ConstElementPtr& arg);
+    boost::shared_ptr<datasrc::memory::ZoneWriter> getZoneWriter(
+        datasrc::ConfigurableClientList& client_list,
+        const dns::RRClass& rrclass, const dns::Name& origin);
+
     // The following are shared with the manager
     std::list<Command>* command_queue_;
     CondVarType* cond_;
@@ -397,7 +487,13 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
             } // the lock is released here.
 
             while (keep_running && !current_commands.empty()) {
-                keep_running = handleCommand(current_commands.front());
+                try {
+                    keep_running = handleCommand(current_commands.front());;
+                } catch (const InternalCommandError& e) {
+                    LOG_ERROR(auth_logger,
+                              AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR).
+                        arg(e.what());
+                }
                 current_commands.pop_front();
             }
         }
@@ -426,7 +522,7 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
     }
 
     const boost::array<const char*, NUM_COMMANDS> command_desc = {
-        {"NOOP", "RECONFIGURE", "SHUTDOWN"}
+        {"NOOP", "RECONFIGURE", "LOADZONE", "SHUTDOWN"}
     };
     LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
               AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
@@ -434,6 +530,9 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
     case RECONFIGURE:
         doReconfigure(command.second);
         break;
+    case LOADZONE:
+        doLoadZone(command.second);
+        break;
     case SHUTDOWN:
         return (false);
     case NOOP:
@@ -444,6 +543,103 @@ DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
     }
     return (true);
 }
+
+template <typename MutexType, typename CondVarType>
+void
+DataSrcClientsBuilderBase<MutexType, CondVarType>::doLoadZone(
+    const isc::data::ConstElementPtr& arg)
+{
+    // We assume some basic level validation as this method can only be
+    // called via the manager in practice.  manager is expected to do the
+    // minimal validation.
+    assert(arg);
+    assert(arg->get("origin"));
+
+    // TODO: currently, we hardcode IN as the default for the optional
+    // 'class' argument. We should really derive this from the specification,
+    // but at the moment the config/command API does not allow that to be
+    // done easily. Once that is in place (tickets have yet to be created,
+    // as we need to do a tiny bit of design work for that), this
+    // code can be replaced with the original part:
+    // assert(arg->get("class"));
+    // const dns::RRClass(arg->get("class")->stringValue());
+    isc::data::ConstElementPtr class_elem = arg->get("class");
+    const dns::RRClass rrclass(class_elem ?
+                                dns::RRClass(class_elem->stringValue()) :
+                                dns::RRClass::IN());
+    const dns::Name origin(arg->get("origin")->stringValue());
+    ClientListsMap::iterator found = (*clients_map_)->find(rrclass);
+    if (found == (*clients_map_)->end()) {
+        isc_throw(InternalCommandError, "failed to load a zone " << origin <<
+                  "/" << rrclass << ": not configured for the class");
+    }
+
+    boost::shared_ptr<datasrc::ConfigurableClientList> client_list =
+        found->second;
+    assert(client_list);
+
+    try {
+        boost::shared_ptr<datasrc::memory::ZoneWriter> zwriter =
+            getZoneWriter(*client_list, rrclass, origin);
+
+        zwriter->load(); // this can take time but doesn't cause a race
+        {   // install() can cause a race and must be in a critical section
+            typename MutexType::Locker locker(*map_mutex_);
+            zwriter->install();
+        }
+        LOG_DEBUG(auth_logger, DBG_AUTH_OPS,
+                  AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE)
+            .arg(origin).arg(rrclass);
+
+        // same as load(). We could let the destructor do it, but do it
+        // ourselves explicitly just in case.
+        zwriter->cleanup();
+    } catch (const InternalCommandError& ex) {
+        throw;     // this comes from getZoneWriter.  just let it go through.
+    } catch (const isc::Exception& ex) {
+        // We catch our internal exceptions (which will be just ignored) and
+        // propagated others (which should generally be considered fatal and
+        // will make the thread terminate)
+        isc_throw(InternalCommandError, "failed to load a zone " << origin <<
+                  "/" << rrclass << ": error occurred in reload: " <<
+                  ex.what());
+    }
+}
+
+// A dedicated subroutine of doLoadZone().  Separated just for keeping the
+// main method concise.
+template <typename MutexType, typename CondVarType>
+boost::shared_ptr<datasrc::memory::ZoneWriter>
+DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
+    datasrc::ConfigurableClientList& client_list,
+    const dns::RRClass& rrclass, const dns::Name& origin)
+{
+    const datasrc::ConfigurableClientList::ZoneWriterPair writerpair =
+        client_list.getCachedZoneWriter(origin);
+
+    switch (writerpair.first) {
+    case datasrc::ConfigurableClientList::ZONE_SUCCESS:
+        assert(writerpair.second);
+        return (writerpair.second);
+    case datasrc::ConfigurableClientList::ZONE_NOT_FOUND:
+        isc_throw(InternalCommandError, "failed to load zone " << origin
+                  << "/" << rrclass << ": not found in any configured "
+                  "data source.");
+    case datasrc::ConfigurableClientList::ZONE_NOT_CACHED:
+        isc_throw(InternalCommandError, "failed to load zone " << origin
+                  << "/" << rrclass << ": not served from memory");
+    case datasrc::ConfigurableClientList::CACHE_DISABLED:
+        // This is an internal error. Auth server must have the cache
+        // enabled.
+        isc_throw(InternalCommandError, "failed to load zone " << origin
+                  << "/" << rrclass << ": internal failure, in-memory cache "
+                  "is somehow disabled");
+    }
+
+    // all cases above should either return or throw, but some compilers
+    // still need a return statement
+    return (boost::shared_ptr<datasrc::memory::ZoneWriter>());
+}
 } // namespace datasrc_clientmgr_internal
 
 /// \brief Shortcut type for normal data source clients manager.
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index 2d5f336..b310b23 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -13,9 +13,11 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <auth/statistics.h>
+#include <auth/statistics_items.h>
 #include <auth/auth_log.h>
 
 #include <dns/opcode.h>
+#include <dns/rcode.h>
 
 #include <cc/data.h>
 #include <cc/session.h>
@@ -32,107 +34,206 @@
 
 #include <boost/noncopyable.hpp>
 
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
 using namespace isc::dns;
 using namespace isc::auth;
 using namespace isc::statistics;
 
-// TODO: We need a namespace ("auth_server"?) to hold
-// AuthSrv and AuthCounters.
+namespace isc {
+namespace auth {
+namespace statistics {
 
 // TODO: Make use of wrappers like isc::dns::Opcode
 // for counter item type.
 
-class AuthCountersImpl : boost::noncopyable {
+class CountersImpl : boost::noncopyable {
 public:
-    AuthCountersImpl();
-    ~AuthCountersImpl();
-    void inc(const AuthCounters::ServerCounterType type);
-    void inc(const Opcode opcode) {
-        opcode_counter_.inc(opcode.getCode());
-    }
-    void inc(const Rcode rcode) {
-        rcode_counter_.inc(rcode.getCode());
-    }
-    void inc(const std::string& zone,
-             const AuthCounters::PerZoneCounterType type);
+    CountersImpl();
+    ~CountersImpl();
+    void inc(const QRAttributes& qrattrs, const Message& response);
     isc::data::ConstElementPtr getStatistics() const;
-    void registerStatisticsValidator
-    (AuthCounters::validator_type validator);
-    // Currently for testing purpose only
-    uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
-    uint64_t getCounter(const Opcode opcode) const {
-        return (opcode_counter_.get(opcode.getCode()));
-    }
-    uint64_t getCounter(const Rcode rcode) const {
-        return (rcode_counter_.get(rcode.getCode()));
-    }
+    void registerStatisticsValidator(Counters::validator_type validator);
 private:
-    Counter server_counter_;
-    Counter opcode_counter_;
-    static const size_t NUM_OPCODES = 16;
-    Counter rcode_counter_;
-    static const size_t NUM_RCODES = 17;
-    CounterDictionary per_zone_counter_;
-    AuthCounters::validator_type validator_;
+    // counter for query/response
+    Counter server_qr_counter_;
+    // set of counters for zones
+    CounterDictionary zone_qr_counters_;
+    // validator
+    Counters::validator_type validator_;
 };
 
-AuthCountersImpl::AuthCountersImpl() :
-    // initialize counter
-    // size of server_counter_: AuthCounters::SERVER_COUNTER_TYPES
-    // size of per_zone_counter_: AuthCounters::PER_ZONE_COUNTER_TYPES
-    server_counter_(AuthCounters::SERVER_COUNTER_TYPES),
-    opcode_counter_(NUM_OPCODES), rcode_counter_(NUM_RCODES),
-    per_zone_counter_(AuthCounters::PER_ZONE_COUNTER_TYPES)
-{
-    per_zone_counter_.addElement("_SERVER_");
-}
+CountersImpl::CountersImpl() :
+    // size of server_qr_counter_, zone_qr_counters_: QR_COUNTER_TYPES
+    server_qr_counter_(QR_COUNTER_TYPES),
+    zone_qr_counters_(QR_COUNTER_TYPES),
+    validator_()
+{}
 
-AuthCountersImpl::~AuthCountersImpl()
+CountersImpl::~CountersImpl()
 {}
 
 void
-AuthCountersImpl::inc(const AuthCounters::ServerCounterType type) {
-    server_counter_.inc(type);
-}
+CountersImpl::inc(const QRAttributes& qrattrs, const Message& response) {
+    // protocols carrying request
+    if (qrattrs.req_ip_version_ == AF_INET) {
+        server_qr_counter_.inc(QR_REQUEST_IPV4);
+    } else if (qrattrs.req_ip_version_ == AF_INET6) {
+        server_qr_counter_.inc(QR_REQUEST_IPV6);
+    }
+    if (qrattrs.req_transport_protocol_ == IPPROTO_UDP) {
+        server_qr_counter_.inc(QR_REQUEST_UDP);
+    } else if (qrattrs.req_transport_protocol_ == IPPROTO_TCP) {
+        server_qr_counter_.inc(QR_REQUEST_TCP);
+    }
 
-void
-AuthCountersImpl::inc(const std::string& zone,
-                      const AuthCounters::PerZoneCounterType type)
-{
-    per_zone_counter_[zone].inc(type);
+    // query TSIG
+    if (qrattrs.req_is_tsig_) {
+        server_qr_counter_.inc(QR_REQUEST_TSIG);
+    }
+    if (qrattrs.req_is_sig0_) {
+        server_qr_counter_.inc(QR_REQUEST_SIG0);
+    }
+    if (qrattrs.req_is_badsig_) {
+        server_qr_counter_.inc(QR_REQUEST_BADSIG);
+        // If signature validation is failed, no other attributes are reliable
+        return;
+    }
+
+    // query EDNS
+    if (qrattrs.req_is_edns_0_) {
+        server_qr_counter_.inc(QR_REQUEST_EDNS0);
+    }
+    if (qrattrs.req_is_edns_badver_) {
+        server_qr_counter_.inc(QR_REQUEST_BADEDNSVER);
+    }
+
+    // query DNSSEC
+    if (qrattrs.req_is_dnssec_ok_) {
+        server_qr_counter_.inc(QR_REQUEST_DNSSEC_OK);
+    }
+
+    // QTYPE
+    unsigned int qtype_type = QR_QTYPE_OTHER;
+    const QuestionIterator qiter = response.beginQuestion();
+    if (qiter != response.endQuestion()) {
+        // get the first and only question section and
+        // get the qtype code
+        const unsigned int qtype = (*qiter)->getType().getCode();
+        if (qtype < 258) {
+            // qtype 0..257: lookup qtype-countertype table
+            qtype_type = QRQTypeToQRCounterType[qtype];
+        } else if (qtype < 32768) {
+            // qtype 258..32767: (Unassigned)
+            qtype_type = QR_QTYPE_OTHER;
+        } else if (qtype < 32770) {
+            // qtype 32768..32769: TA and DLV
+            qtype_type = QR_QTYPE_TA + (qtype - 32768);
+        } else {
+            // qtype 32770..65535: (Unassigned, Private use, Reserved)
+            qtype_type = QR_QTYPE_OTHER;
+        }
+    }
+    server_qr_counter_.inc(qtype_type);
+    // OPCODE
+    server_qr_counter_.inc(QROpCodeToQRCounterType[qrattrs.req_opcode_]);
+
+    // response
+    if (qrattrs.answer_sent_) {
+        // responded
+        server_qr_counter_.inc(QR_RESPONSE);
+
+        // response truncated
+        if (qrattrs.res_is_truncated_) {
+            server_qr_counter_.inc(QR_RESPONSE_TRUNCATED);
+        }
+
+        // response EDNS
+        ConstEDNSPtr response_edns = response.getEDNS();
+        if (response_edns != NULL && response_edns->getVersion() == 0) {
+            server_qr_counter_.inc(QR_RESPONSE_EDNS0);
+        }
+
+        // response TSIG
+        if (qrattrs.req_is_tsig_) {
+            // assume response is TSIG signed if request is TSIG signed
+            server_qr_counter_.inc(QR_RESPONSE_TSIG);
+        }
+
+        // response SIG(0) is currently not implemented
+
+        // RCODE
+        const unsigned int rcode = response.getRcode().getCode();
+        unsigned int rcode_type = QR_RCODE_OTHER;
+        if (rcode < 23) {
+            // rcode 0..22: lookup rcode-countertype table
+            rcode_type = QRRCodeToQRCounterType[rcode];
+        } else {
+            // opcode larger than 22 is reserved or unassigned
+            rcode_type = QR_RCODE_OTHER;
+        }
+        server_qr_counter_.inc(rcode_type);
+
+        // compound attributes
+        const unsigned int answer_rrs =
+            response.getRRCount(Message::SECTION_ANSWER);
+        const bool is_aa_set = response.getHeaderFlag(Message::HEADERFLAG_AA);
+
+        if (is_aa_set) {
+            // QryAuthAns
+            server_qr_counter_.inc(QR_QRYAUTHANS);
+        } else {
+            // QryNoAuthAns
+            server_qr_counter_.inc(QR_QRYNOAUTHANS);
+        }
+
+        if (rcode == Rcode::NOERROR_CODE) {
+            if (answer_rrs > 0) {
+                // QrySuccess
+                server_qr_counter_.inc(QR_QRYSUCCESS);
+            } else {
+                if (is_aa_set) {
+                    // QryNxrrset
+                    server_qr_counter_.inc(QR_QRYNXRRSET);
+                } else {
+                    // QryReferral
+                    server_qr_counter_.inc(QR_QRYREFERRAL);
+                }
+            }
+        } else if (rcode == Rcode::REFUSED_CODE) {
+            // AuthRej
+            server_qr_counter_.inc(QR_QRYREJECT);
+        }
+    }
 }
 
 isc::data::ConstElementPtr
-AuthCountersImpl::getStatistics() const {
+CountersImpl::getStatistics() const {
     std::stringstream statistics_string;
     statistics_string << "{ \"queries.udp\": "
-                      << server_counter_.get(AuthCounters::SERVER_UDP_QUERY)
+                      << server_qr_counter_.get(QR_REQUEST_UDP)
                       << ", \"queries.tcp\": "
-                      << server_counter_.get(AuthCounters::SERVER_TCP_QUERY);
+                      << server_qr_counter_.get(QR_REQUEST_TCP);
     // Insert non 0 Opcode counters.
-    for (int i = 0; i < NUM_OPCODES; ++i) {
-        const Counter::Type counter = opcode_counter_.get(i);
+    for (int i = QR_OPCODE_QUERY; i <= QR_OPCODE_OTHER; ++i) {
+        const Counter::Type counter = server_qr_counter_.get(i);
         if (counter != 0) {
-            // The counter item name should be derived lower-cased textual
-            // representation of the code.
-            std::string opcode_txt = Opcode(i).toText();
-            std::transform(opcode_txt.begin(), opcode_txt.end(),
-                           opcode_txt.begin(), ::tolower);
-            statistics_string << ", \"opcode." << opcode_txt << "\": "
-                              << counter;
+            statistics_string << ", \"" << "opcode." <<
+                                 QRCounterOpcode[i - QR_OPCODE_QUERY].name <<
+                                 "\": " << counter;
         }
     }
     // Insert non 0 Rcode counters.
-    for (int i = 0; i < NUM_RCODES; ++i) {
-        const Counter::Type counter = rcode_counter_.get(i);
+    for (int i = QR_RCODE_NOERROR; i <= QR_RCODE_OTHER; ++i) {
+        const Counter::Type counter = server_qr_counter_.get(i);
         if (counter != 0) {
-            // The counter item name should be derived lower-cased textual
-            // representation of the code.
-            std::string rcode_txt = Rcode(i).toText();
-            std::transform(rcode_txt.begin(), rcode_txt.end(),
-                           rcode_txt.begin(), ::tolower);
-            statistics_string << ", \"rcode." << rcode_txt << "\": "
-                              << counter;
+            statistics_string << ", \"" << "rcode." <<
+                                 QRCounterRcode[i - QR_RCODE_NOERROR].name <<
+                                 "\": " << counter;
         }
     }
     statistics_string << "}";
@@ -150,61 +251,34 @@ AuthCountersImpl::getStatistics() const {
 }
 
 void
-AuthCountersImpl::registerStatisticsValidator
-    (AuthCounters::validator_type validator)
+CountersImpl::registerStatisticsValidator
+    (Counters::validator_type validator)
 {
     validator_ = validator;
 }
 
-// Currently for testing purpose only
-uint64_t
-AuthCountersImpl::getCounter(const AuthCounters::ServerCounterType type) const {
-    return (server_counter_.get(type));
-}
-
-AuthCounters::AuthCounters() : impl_(new AuthCountersImpl())
+Counters::Counters() : impl_(new CountersImpl())
 {}
 
-AuthCounters::~AuthCounters() {}
+Counters::~Counters() {}
 
 void
-AuthCounters::inc(const AuthCounters::ServerCounterType type) {
-    impl_->inc(type);
-}
-
-void
-AuthCounters::inc(const Opcode opcode) {
-    impl_->inc(opcode);
-}
-
-void
-AuthCounters::inc(const Rcode rcode) {
-    impl_->inc(rcode);
+Counters::inc(const QRAttributes& qrattrs, const Message& response) {
+    impl_->inc(qrattrs, response);
 }
 
 isc::data::ConstElementPtr
-AuthCounters::getStatistics() const {
+Counters::getStatistics() const {
     return (impl_->getStatistics());
 }
 
-uint64_t
-AuthCounters::getCounter(const AuthCounters::ServerCounterType type) const {
-    return (impl_->getCounter(type));
-}
-
-uint64_t
-AuthCounters::getCounter(const Opcode opcode) const {
-    return (impl_->getCounter(opcode));
-}
-
-uint64_t
-AuthCounters::getCounter(const Rcode rcode) const {
-    return (impl_->getCounter(rcode));
-}
-
 void
-AuthCounters::registerStatisticsValidator
-    (AuthCounters::validator_type validator) const
+Counters::registerStatisticsValidator
+    (Counters::validator_type validator) const
 {
     return (impl_->registerStatisticsValidator(validator));
 }
+
+} // namespace statistics
+} // namespace auth
+} // namespace isc
diff --git a/src/bin/auth/statistics.h b/src/bin/auth/statistics.h
index d5700ad..d60c681 100644
--- a/src/bin/auth/statistics.h
+++ b/src/bin/auth/statistics.h
@@ -15,19 +15,133 @@
 #ifndef STATISTICS_H
 #define STATISTICS_H 1
 
-#include <dns/opcode.h>
-#include <dns/rcode.h>
 #include <cc/session.h>
 #include <cc/data.h>
 
+#include <dns/message.h>
+
+#include <string>
+
 #include <stdint.h>
 #include <boost/scoped_ptr.hpp>
 
-class AuthCountersImpl;
+namespace isc {
+namespace auth {
+namespace statistics {
+
+class CountersImpl;
+
+class QRAttributes {
+/// \brief Query/Response attributes for statistics.
+///
+/// This class holds some attributes related to a query/response
+/// for statistics data collection.
+///
+/// This class does not have getter methods since it exposes private members
+/// to \c CountersImpl directly.
+friend class CountersImpl;
+private:
+    // request attributes
+    int req_ip_version_;            // IP version
+    int req_transport_protocol_;    // Transport layer protocol
+    int req_opcode_;                // OpCode
+    bool req_is_edns_0_;            // EDNS ver.0
+    bool req_is_edns_badver_;       // other EDNS version
+    bool req_is_dnssec_ok_;         // DO bit
+    bool req_is_tsig_;              // signed with valid TSIG
+    bool req_is_sig0_;              // signed with valid SIG(0)
+    bool req_is_badsig_;            // signed but bad signature
+    // zone origin
+    std::string zone_origin_;       // zone origin
+    // response attributes
+    bool answer_sent_;              // DNS message has sent
+    bool res_is_truncated_;         // DNS message is truncated
+public:
+    /// The constructor.
+    ///
+    /// This constructor is mostly exception free. But it may still throw
+    /// a standard exception if memory allocation fails inside the method.
+    ///
+    QRAttributes() {
+        reset();
+    };
+
+    /// The destructor.
+    ///
+    /// This method never throws an exception.
+    ///
+    ~QRAttributes() {};
+    /// \brief Set query opcode.
+    /// \throw None
+    void setQueryOpCode(const int opcode) {
+        req_opcode_ = opcode;
+    };
+    /// \brief Set IP version carrying a query.
+    /// \throw None
+    void setQueryIPVersion(const int ip_version) {
+        req_ip_version_ = ip_version;
+    };
+    /// \brief Set transport protocol carrying a query.
+    /// \throw None
+    void setQueryTransportProtocol(const int transport_protocol) {
+        req_transport_protocol_ = transport_protocol;
+    };
+    /// \brief Set query EDNS attributes.
+    /// \throw None
+    void setQueryEDNS(const bool is_edns_0, const bool is_edns_badver) {
+        req_is_edns_0_ = is_edns_0;
+        req_is_edns_badver_ = is_edns_badver;
+    };
+    /// \brief Set query DO bit.
+    /// \throw None
+    void setQueryDO(const bool is_dnssec_ok) {
+        req_is_dnssec_ok_ = is_dnssec_ok;
+    };
+    /// \brief Set query TSIG attributes.
+    /// \throw None
+    void setQuerySig(const bool is_tsig, const bool is_sig0,
+                            const bool is_badsig)
+    {
+        req_is_tsig_ = is_tsig;
+        req_is_sig0_ = is_sig0;
+        req_is_badsig_ = is_badsig;
+    };
+    /// \brief Set zone origin.
+    /// \throw None
+    void setOrigin(const std::string& origin) {
+        zone_origin_ = origin;
+    };
+    /// \brief Set if the answer was sent.
+    /// \throw None
+    void answerWasSent() {
+        answer_sent_ = true;
+    };
+    /// \brief Set if the response is truncated.
+    /// \throw None
+    void setResponseTruncated(const bool is_truncated) {
+        res_is_truncated_ = is_truncated;
+    };
+    /// \brief Reset attributes.
+    /// \throw None
+    void reset() {
+        req_ip_version_ = 0;
+        req_transport_protocol_ = 0;
+        req_opcode_ = 0;
+        req_is_edns_0_ = false;
+        req_is_edns_badver_ = false;
+        req_is_dnssec_ok_ = false;
+        req_is_tsig_ = false;
+        req_is_sig0_ = false;
+        req_is_badsig_ = false;
+        zone_origin_.clear();
+        answer_sent_ = false;
+        res_is_truncated_ = false;
+    };
+};
 
 /// \brief Set of query counters.
 ///
-/// \c AuthCounters is set of query counters class. It holds query counters
+/// \c Counters is set of query counters class. It holds query counters
 /// and provides an interface to increment the counter of specified type
 /// (e.g. UDP query, TCP query).
 ///
@@ -35,9 +149,7 @@ class AuthCountersImpl;
 /// statistics module.
 ///
 /// This class is designed to be a part of \c AuthSrv.
-/// Call \c inc() to increment a counter for specific type of query in
-/// the query processing function. use \c enum \c CounterType to specify
-/// the type of query.
+/// Call \c inc() to increment a counter for the query.
 /// Call \c getStatistics() to answer statistics information to statistics
 /// module with statistics_session, when the command \c getstats is received.
 ///
@@ -50,61 +162,31 @@ class AuthCountersImpl;
 /// construction overhead of this approach should be acceptable.
 ///
 /// \todo Hold counters for each query types (Notify, Axfr, Ixfr, Normal)
-/// \todo Consider overhead of \c AuthCounters::inc()
-class AuthCounters {
+/// \todo Consider overhead of \c Counters::inc()
+class Counters {
 private:
-    boost::scoped_ptr<AuthCountersImpl> impl_;
+    boost::scoped_ptr<CountersImpl> impl_;
 public:
-    // Enum for the type of counter
-    enum ServerCounterType {
-        SERVER_UDP_QUERY,       ///< SERVER_UDP_QUERY: counter for UDP queries
-        SERVER_TCP_QUERY,       ///< SERVER_TCP_QUERY: counter for TCP queries
-        SERVER_COUNTER_TYPES    ///< The number of defined counters
-    };
-    enum PerZoneCounterType {
-        ZONE_UDP_QUERY,         ///< ZONE_UDP_QUERY: counter for UDP queries
-        ZONE_TCP_QUERY,         ///< ZONE_TCP_QUERY: counter for TCP queries
-        PER_ZONE_COUNTER_TYPES  ///< The number of defined counters
-    };
     /// The constructor.
     ///
     /// This constructor is mostly exception free. But it may still throw
     /// a standard exception if memory allocation fails inside the method.
     ///
-    AuthCounters();
+    Counters();
     /// The destructor.
     ///
     /// This method never throws an exception.
     ///
-    ~AuthCounters();
+    ~Counters();
 
-    /// \brief Increment the counter specified by the parameter.
-    ///
-    /// \param type Type of a counter to increment.
+    /// \brief Increment counters according to the parameters.
     ///
-    /// \throw std::out_of_range \a type is unknown.
-    ///
-    /// usage: counter.inc(AuthCounters::SERVER_UDP_QUERY);
-    /// 
-    void inc(const ServerCounterType type);
-
-    /// \brief Increment the counter of a per opcode counter.
-    ///
-    /// \note This is a tentative interface.  See \c getCounter().
-    ///
-    /// \param opcode The opcode of the counter to increment.
+    /// \param qrattrs Query/Response attributes.
+    /// \param response DNS response message.
     ///
     /// \throw None
-    void inc(const isc::dns::Opcode opcode);
-
-    /// \brief Increment the counter of a per rcode counter.
-    ///
-    /// \note This is a tentative interface.  See \c getCounter().
     ///
-    /// \param rcode The rcode of the counter to increment.
-    ///
-    /// \throw None
-    void inc(const isc::dns::Rcode rcode);
+    void inc(const QRAttributes& qrattrs, const isc::dns::Message& response);
 
     /// \brief Answers statistics counters to statistics module.
     ///
@@ -116,47 +198,6 @@ public:
     ///
     isc::data::ConstElementPtr getStatistics() const;
 
-    /// \brief Get the value of a counter in the AuthCounters.
-    ///
-    /// This function returns a value of the counter specified by \a type.
-    /// This method never throws an exception.
-    ///
-    /// Note: Currently this function is for testing purpose only.
-    ///
-    /// \param type Type of a counter to get the value of
-    ///
-    /// \return the value of the counter specified by \a type.
-    uint64_t getCounter(const AuthCounters::ServerCounterType type) const;
-
-    /// \brief Get the value of a per opcode counter.
-    ///
-    /// This method returns the value of the per opcode counter for the
-    /// specified \c opcode.
-    ///
-    /// \note This is a tentative interface as an attempt of experimentally
-    /// supporting more statistics counters.  This should eventually be more
-    /// generalized.  In any case, this method is mainly for testing.
-    ///
-    /// \throw None
-    /// \param opcode The opcode of the counter to get the value of
-    /// \return the value of the counter.
-    uint64_t getCounter(const isc::dns::Opcode opcode) const;
-
-    /// \brief Get the value of a per rcode counter.
-    ///
-    /// This method returns the value of the per rcode counter for the
-    /// specified \c rcode.
-    ///
-    /// \note As mentioned in getCounter(const isc::dns::Opcode opcode),
-    /// This is a tentative interface as an attempt of experimentally
-    /// supporting more statistics counters.  This should eventually be more
-    /// generalized.  In any case, this method is mainly for testing.
-    ///
-    /// \throw None
-    /// \param rcode The rcode of the counter to get the value of
-    /// \return the value of the counter.
-    uint64_t getCounter(const isc::dns::Rcode rcode) const;
-
     /// \brief A type of validation function for the specification in
     /// isc::config::ModuleSpec.
     ///
@@ -168,16 +209,20 @@ public:
     validator_type;
 
     /// \brief Register a function type of the statistics validation
-    /// function for AuthCounters.
+    /// function for Counters.
     ///
     /// This method never throws an exception.
     ///
     /// \param validator A function type of the validation of
     /// statistics specification.
     ///
-    void registerStatisticsValidator(AuthCounters::validator_type validator) const;
+    void registerStatisticsValidator(Counters::validator_type validator) const;
 };
 
+} // namespace statistics
+} // namespace auth
+} // namespace isc
+
 #endif // STATISTICS_H
 
 // Local Variables:
diff --git a/src/bin/auth/statistics_items.h b/src/bin/auth/statistics_items.h
new file mode 100644
index 0000000..5839206
--- /dev/null
+++ b/src/bin/auth/statistics_items.h
@@ -0,0 +1,609 @@
+// 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 __STATISTICS_ITEMS_H
+#define __STATISTICS_ITEMS_H 1
+
+/// This file defines a set of statistics items in Auth module for internal
+/// use. This file is intended to be included in statistics.cc.
+
+namespace {
+
+struct CounterTypeTree {
+    const char* const name;
+    const struct CounterTypeTree* const sub_tree;
+    const int counter_id;
+};
+
+// enum for query/response counters
+enum QRCounterType {
+    // Request Attributes
+    QR_REQUEST_IPV4,        ///< Number of IPv4 requests received
+    QR_REQUEST_IPV6,        ///< Number of IPv6 requests received
+    QR_REQUEST_EDNS0,       ///< Number of requests with EDNS(0) received
+    QR_REQUEST_BADEDNSVER,  ///< Number of requests with unsupported EDNS version received
+    QR_REQUEST_TSIG,        ///< Number of requests with TSIG received
+    QR_REQUEST_SIG0,        ///< Number of requests with SIG(0) received; not implemented in BIND 10
+    QR_REQUEST_BADSIG,      ///< Number of requests with invalid TSIG or SIG(0) signature received
+    QR_REQUEST_UDP,         ///< Number of UDP requests received
+    QR_REQUEST_TCP,         ///< Number of TCP requests received
+    QR_REQUEST_DNSSEC_OK,   ///< Number of requests with DO bit
+    // Request Opcodes
+    QR_OPCODE_QUERY,        ///< Number of Opcode=QUERY requests received
+    QR_OPCODE_IQUERY,       ///< Number of Opcode=IQUERY requests received
+    QR_OPCODE_STATUS,       ///< Number of Opcode=STATUS requests received
+    QR_OPCODE_NOTIFY,       ///< Number of Opcode=NOTIFY requests received
+    QR_OPCODE_UPDATE,       ///< Number of Opcode=UPDATE requests received
+    QR_OPCODE_OTHER,        ///< Number of requests in other OpCode received
+    // Query Types
+    QR_QTYPE_A,             ///< Number of QTYPE = A queries received
+    QR_QTYPE_NS,            ///< Number of QTYPE = NS queries received
+    QR_QTYPE_MD,            ///< Number of QTYPE = MD queries received
+    QR_QTYPE_MF,            ///< Number of QTYPE = MF queries received
+    QR_QTYPE_CNAME,         ///< Number of QTYPE = CNAME queries received
+    QR_QTYPE_SOA,           ///< Number of QTYPE = SOA queries received
+    QR_QTYPE_MB,            ///< Number of QTYPE = MB queries received
+    QR_QTYPE_MG,            ///< Number of QTYPE = MG queries received
+    QR_QTYPE_MR,            ///< Number of QTYPE = MR queries received
+    QR_QTYPE_NULL,          ///< Number of QTYPE = NULL queries received
+    QR_QTYPE_WKS,           ///< Number of QTYPE = WKS queries received
+    QR_QTYPE_PTR,           ///< Number of QTYPE = PTR queries received
+    QR_QTYPE_HINFO,         ///< Number of QTYPE = HINFO queries received
+    QR_QTYPE_MINFO,         ///< Number of QTYPE = MINFO queries received
+    QR_QTYPE_MX,            ///< Number of QTYPE = MX queries received
+    QR_QTYPE_TXT,           ///< Number of QTYPE = TXT queries received
+    QR_QTYPE_RP,            ///< Number of QTYPE = RP queries received
+    QR_QTYPE_AFSDB,         ///< Number of QTYPE = AFSDB queries received
+    QR_QTYPE_X25,           ///< Number of QTYPE = X25 queries received
+    QR_QTYPE_ISDN,          ///< Number of QTYPE = ISDN queries received
+    QR_QTYPE_RT,            ///< Number of QTYPE = RT queries received
+    QR_QTYPE_NSAP,          ///< Number of QTYPE = NSAP queries received
+    QR_QTYPE_NSAP_PTR,      ///< Number of QTYPE = NSAP-PTR queries received
+    QR_QTYPE_SIG,           ///< Number of QTYPE = SIG queries received
+    QR_QTYPE_KEY,           ///< Number of QTYPE = KEY queries received
+    QR_QTYPE_PX,            ///< Number of QTYPE = PX queries received
+    QR_QTYPE_GPOS,          ///< Number of QTYPE = GPOS queries received
+    QR_QTYPE_AAAA,          ///< Number of QTYPE = AAAA queries received
+    QR_QTYPE_LOC,           ///< Number of QTYPE = LOC queries received
+    QR_QTYPE_NXT,           ///< Number of QTYPE = NXT queries received
+    QR_QTYPE_EID,           ///< Number of QTYPE = EID queries received
+    QR_QTYPE_NIMLOC,        ///< Number of QTYPE = NIMLOC queries received
+    QR_QTYPE_SRV,           ///< Number of QTYPE = SRV queries received
+    QR_QTYPE_ATMA,          ///< Number of QTYPE = ATMA queries received
+    QR_QTYPE_NAPTR,         ///< Number of QTYPE = NAPTR queries received
+    QR_QTYPE_KX,            ///< Number of QTYPE = KX queries received
+    QR_QTYPE_CERT,          ///< Number of QTYPE = CERT queries received
+    QR_QTYPE_A6,            ///< Number of QTYPE = A6 queries received
+    QR_QTYPE_DNAME,         ///< Number of QTYPE = DNAME queries received
+    QR_QTYPE_SINK,          ///< Number of QTYPE = SINK queries received
+    QR_QTYPE_OPT,           ///< Number of QTYPE = OPT queries received
+    QR_QTYPE_APL,           ///< Number of QTYPE = APL queries received
+    QR_QTYPE_DS,            ///< Number of QTYPE = DS queries received
+    QR_QTYPE_SSHFP,         ///< Number of QTYPE = SSHFP queries received
+    QR_QTYPE_IPSECKEY,      ///< Number of QTYPE = IPSECKEY queries received
+    QR_QTYPE_RRSIG,         ///< Number of QTYPE = RRSIG queries received
+    QR_QTYPE_NSEC,          ///< Number of QTYPE = NSEC queries received
+    QR_QTYPE_DNSKEY,        ///< Number of QTYPE = DNSKEY queries received
+    QR_QTYPE_DHCID,         ///< Number of QTYPE = DHCID queries received
+    QR_QTYPE_NSEC3,         ///< Number of QTYPE = NSEC3 queries received
+    QR_QTYPE_NSEC3PARAM,    ///< Number of QTYPE = NSEC3PARAM queries received
+    QR_QTYPE_HIP,           ///< Number of QTYPE = HIP queries received
+    QR_QTYPE_NINFO,         ///< Number of QTYPE = NINFO queries received
+    QR_QTYPE_RKEY,          ///< Number of QTYPE = RKEY queries received
+    QR_QTYPE_TALINK,        ///< Number of QTYPE = TALINK queries received
+    QR_QTYPE_SPF,           ///< Number of QTYPE = SPF queries received
+    QR_QTYPE_UINFO,         ///< Number of QTYPE = UINFO queries received
+    QR_QTYPE_UID,           ///< Number of QTYPE = UID queries received
+    QR_QTYPE_GID,           ///< Number of QTYPE = GID queries received
+    QR_QTYPE_UNSPEC,        ///< Number of QTYPE = UNSPEC queries received
+    QR_QTYPE_TKEY,          ///< Number of QTYPE = TKEY queries received
+    QR_QTYPE_TSIG,          ///< Number of QTYPE = TSIG queries received
+    QR_QTYPE_IXFR,          ///< Number of QTYPE = IXFR queries received
+    QR_QTYPE_AXFR,          ///< Number of QTYPE = AXFR queries received
+    QR_QTYPE_MAILB,         ///< Number of QTYPE = MAILB queries received
+    QR_QTYPE_MAILA,         ///< Number of QTYPE = MAILA queries received
+    QR_QTYPE_URI,           ///< Number of QTYPE = URI queries received
+    QR_QTYPE_CAA,           ///< Number of QTYPE = CAA queries received
+    QR_QTYPE_TA,            ///< Number of QTYPE = TA queries received
+    QR_QTYPE_DLV,           ///< Number of QTYPE = DLV queries received
+    QR_QTYPE_OTHER,         ///< Number of queries in other QTYPE received
+    // Respose Attributes
+    QR_RESPONSE,            ///< Number of responses sent
+    QR_RESPONSE_TRUNCATED,  ///< Number of truncated responses sent
+    QR_RESPONSE_EDNS0,      ///< Number of responses with EDNS0; not implemented in BIND 10
+    QR_RESPONSE_TSIG,       ///< Number of responses with TSIG
+    QR_RESPONSE_SIG0,       ///< Number of responses with SIG(0); not implemented in BIND 10
+    QR_QRYSUCCESS,          ///< Number of queries resulted in rcode = NOERROR and answer RR >= 1
+    QR_QRYAUTHANS,          ///< Number of queries resulted in authoritative answer
+    QR_QRYNOAUTHANS,        ///< Number of queries resulted in non-authoritative answer
+    QR_QRYREFERRAL,         ///< Number of queries resulted in referral answer
+    QR_QRYNXRRSET,          ///< Number of queries resulted in NOERROR but answer RR == 0
+    QR_QRYREJECT,           ///< Number of queries rejected
+    // Response Rcodes
+    QR_RCODE_NOERROR,       ///< Number of queries resulted in RCODE = 0 (NoError)
+    QR_RCODE_FORMERR,       ///< Number of queries resulted in RCODE = 1 (FormErr)
+    QR_RCODE_SERVFAIL,      ///< Number of queries resulted in RCODE = 2 (ServFail)
+    QR_RCODE_NXDOMAIN,      ///< Number of queries resulted in RCODE = 3 (NXDomain)
+    QR_RCODE_NOTIMP,        ///< Number of queries resulted in RCODE = 4 (NotImp)
+    QR_RCODE_REFUSED,       ///< Number of queries resulted in RCODE = 5 (Refused)
+    QR_RCODE_YXDOMAIN,      ///< Number of queries resulted in RCODE = 6 (YXDomain)
+    QR_RCODE_YXRRSET,       ///< Number of queries resulted in RCODE = 7 (YXRRSet)
+    QR_RCODE_NXRRSET,       ///< Number of queries resulted in RCODE = 8 (NXRRSet)
+    QR_RCODE_NOTAUTH,       ///< Number of queries resulted in RCODE = 9 (NotAuth)
+    QR_RCODE_NOTZONE,       ///< Number of queries resulted in RCODE = 10 (NotZone)
+    QR_RCODE_BADSIGVERS,    ///< Number of queries resulted in RCODE = 16 (BADVERS, BADSIG)
+    QR_RCODE_BADKEY,        ///< Number of queries resulted in RCODE = 17 (BADKEY)
+    QR_RCODE_BADTIME,       ///< Number of queries resulted in RCODE = 18 (BADTIME)
+    QR_RCODE_BADMODE,       ///< Number of queries resulted in RCODE = 19 (BADMODE)
+    QR_RCODE_BADNAME,       ///< Number of queries resulted in RCODE = 20 (BADNAME)
+    QR_RCODE_BADALG,        ///< Number of queries resulted in RCODE = 21 (BADALG)
+    QR_RCODE_BADTRUNC,      ///< Number of queries resulted in RCODE = 22 (BADTRUNC)
+    QR_RCODE_OTHER,         ///< Number of queries resulted in other RCODEs
+    // End of counter types
+    QR_COUNTER_TYPES  ///< The number of defined counters
+};
+
+// item names for query/response counters
+const struct CounterTypeTree QRCounterRequest[] = {
+    { "v4",         NULL,   QR_REQUEST_IPV4       },
+    { "v6",         NULL,   QR_REQUEST_IPV6       },
+    { "edns0",      NULL,   QR_REQUEST_EDNS0      },
+    { "badednsver", NULL,   QR_REQUEST_BADEDNSVER },
+    { "tsig",       NULL,   QR_REQUEST_TSIG       },
+    { "sig0",       NULL,   QR_REQUEST_SIG0       },
+    { "badsig",     NULL,   QR_REQUEST_BADSIG     },
+    { "udp",        NULL,   QR_REQUEST_UDP        },
+    { "tcp",        NULL,   QR_REQUEST_TCP        },
+    { "dnssec_ok",  NULL,   QR_REQUEST_DNSSEC_OK  },
+    { NULL,         NULL,   -1                    }
+};
+const struct CounterTypeTree QRCounterOpcode[] = {
+    { "query",  NULL,   QR_OPCODE_QUERY  },
+    { "iquery", NULL,   QR_OPCODE_IQUERY },
+    { "status", NULL,   QR_OPCODE_STATUS },
+    { "notify", NULL,   QR_OPCODE_NOTIFY },
+    { "update", NULL,   QR_OPCODE_UPDATE },
+    { "other",  NULL,   QR_OPCODE_OTHER  },
+    { NULL,     NULL,   -1               }
+};
+const struct CounterTypeTree QRCounterQtype[] = {
+    { "a",          NULL,   QR_QTYPE_A,         },
+    { "ns",         NULL,   QR_QTYPE_NS         },
+    { "md",         NULL,   QR_QTYPE_MD         },
+    { "mf",         NULL,   QR_QTYPE_MF         },
+    { "cname",      NULL,   QR_QTYPE_CNAME      },
+    { "soa",        NULL,   QR_QTYPE_SOA        },
+    { "mb",         NULL,   QR_QTYPE_MB         },
+    { "mg",         NULL,   QR_QTYPE_MG         },
+    { "mr",         NULL,   QR_QTYPE_MR         },
+    { "null",       NULL,   QR_QTYPE_NULL       },
+    { "wks",        NULL,   QR_QTYPE_WKS        },
+    { "ptr",        NULL,   QR_QTYPE_PTR        },
+    { "hinfo",      NULL,   QR_QTYPE_HINFO      },
+    { "minfo",      NULL,   QR_QTYPE_MINFO      },
+    { "mx",         NULL,   QR_QTYPE_MX         },
+    { "txt",        NULL,   QR_QTYPE_TXT        },
+    { "rp",         NULL,   QR_QTYPE_RP         },
+    { "afsdb",      NULL,   QR_QTYPE_AFSDB      },
+    { "x25",        NULL,   QR_QTYPE_X25        },
+    { "isdn",       NULL,   QR_QTYPE_ISDN       },
+    { "rt",         NULL,   QR_QTYPE_RT         },
+    { "nsap",       NULL,   QR_QTYPE_NSAP       },
+    { "nsap-ptr",   NULL,   QR_QTYPE_NSAP_PTR   },
+    { "sig",        NULL,   QR_QTYPE_SIG        },
+    { "key",        NULL,   QR_QTYPE_KEY        },
+    { "px",         NULL,   QR_QTYPE_PX         },
+    { "gpos",       NULL,   QR_QTYPE_GPOS       },
+    { "aaaa",       NULL,   QR_QTYPE_AAAA       },
+    { "loc",        NULL,   QR_QTYPE_LOC        },
+    { "nxt",        NULL,   QR_QTYPE_NXT        },
+    { "eid",        NULL,   QR_QTYPE_EID        },
+    { "nimloc",     NULL,   QR_QTYPE_NIMLOC     },
+    { "srv",        NULL,   QR_QTYPE_SRV        },
+    { "atma",       NULL,   QR_QTYPE_ATMA       },
+    { "naptr",      NULL,   QR_QTYPE_NAPTR      },
+    { "kx",         NULL,   QR_QTYPE_KX         },
+    { "cert",       NULL,   QR_QTYPE_CERT       },
+    { "a6",         NULL,   QR_QTYPE_A6         },
+    { "dname",      NULL,   QR_QTYPE_DNAME      },
+    { "sink",       NULL,   QR_QTYPE_SINK       },
+    { "opt",        NULL,   QR_QTYPE_OPT        },
+    { "apl",        NULL,   QR_QTYPE_APL        },
+    { "ds",         NULL,   QR_QTYPE_DS         },
+    { "sshfp",      NULL,   QR_QTYPE_SSHFP      },
+    { "ipseckey",   NULL,   QR_QTYPE_IPSECKEY   },
+    { "rrsig",      NULL,   QR_QTYPE_RRSIG      },
+    { "nsec",       NULL,   QR_QTYPE_NSEC       },
+    { "dnskey",     NULL,   QR_QTYPE_DNSKEY     },
+    { "dhcid",      NULL,   QR_QTYPE_DHCID      },
+    { "nsec3",      NULL,   QR_QTYPE_NSEC3      },
+    { "nsec3param", NULL,   QR_QTYPE_NSEC3PARAM },
+    { "hip",        NULL,   QR_QTYPE_HIP        },
+    { "ninfo",      NULL,   QR_QTYPE_NINFO      },
+    { "rkey",       NULL,   QR_QTYPE_RKEY       },
+    { "talink",     NULL,   QR_QTYPE_TALINK     },
+    { "spf",        NULL,   QR_QTYPE_SPF        },
+    { "uinfo",      NULL,   QR_QTYPE_UINFO      },
+    { "uid",        NULL,   QR_QTYPE_UID        },
+    { "gid",        NULL,   QR_QTYPE_GID        },
+    { "unspec",     NULL,   QR_QTYPE_UNSPEC     },
+    { "tkey",       NULL,   QR_QTYPE_TKEY       },
+    { "tsig",       NULL,   QR_QTYPE_TSIG       },
+    { "ixfr",       NULL,   QR_QTYPE_IXFR       },
+    { "axfr",       NULL,   QR_QTYPE_AXFR       },
+    { "mailb",      NULL,   QR_QTYPE_MAILB      },
+    { "maila",      NULL,   QR_QTYPE_MAILA      },
+    { "uri",        NULL,   QR_QTYPE_URI        },
+    { "caa",        NULL,   QR_QTYPE_CAA        },
+    { "ta",         NULL,   QR_QTYPE_TA         },
+    { "dlv",        NULL,   QR_QTYPE_DLV        },
+    { "other",      NULL,   QR_QTYPE_OTHER      },
+    { NULL,         NULL,   -1                  }
+};
+const struct CounterTypeTree QRCounterResponse[] = {
+    { "truncated",  NULL,   QR_RESPONSE_TRUNCATED },
+    { "edns0",      NULL,   QR_RESPONSE_EDNS0     },
+    { "tsig",       NULL,   QR_RESPONSE_TSIG      },
+    { "sig0",       NULL,   QR_RESPONSE_SIG0      },
+    { NULL,         NULL,   -1                    }
+};
+const struct CounterTypeTree QRCounterRcode[] = {
+    { "noerror",    NULL,   QR_RCODE_NOERROR    },
+    { "formerr",    NULL,   QR_RCODE_FORMERR    },
+    { "servfail",   NULL,   QR_RCODE_SERVFAIL   },
+    { "nxdomain",   NULL,   QR_RCODE_NXDOMAIN   },
+    { "notimp",     NULL,   QR_RCODE_NOTIMP     },
+    { "refused",    NULL,   QR_RCODE_REFUSED    },
+    { "yxdomain",   NULL,   QR_RCODE_YXDOMAIN   },
+    { "yxrrset",    NULL,   QR_RCODE_YXRRSET    },
+    { "nxrrset",    NULL,   QR_RCODE_NXRRSET    },
+    { "notauth",    NULL,   QR_RCODE_NOTAUTH    },
+    { "notzone",    NULL,   QR_RCODE_NOTZONE    },
+    { "badsigvers", NULL,   QR_RCODE_BADSIGVERS },
+    { "badkey",     NULL,   QR_RCODE_BADKEY     },
+    { "badtime",    NULL,   QR_RCODE_BADTIME    },
+    { "badmode",    NULL,   QR_RCODE_BADMODE    },
+    { "badname",    NULL,   QR_RCODE_BADNAME    },
+    { "badalg",     NULL,   QR_RCODE_BADALG     },
+    { "badtrunc",   NULL,   QR_RCODE_BADTRUNC   },
+    { "other",      NULL,   QR_RCODE_OTHER      },
+    { NULL,         NULL,   -1 }
+};
+const struct CounterTypeTree QRCounterTree[] = {
+    { "request",        QRCounterRequest,   -1              },
+    { "opcode",         QRCounterOpcode,    -1              },
+    { "qtype",          QRCounterQtype,     -1              },
+    { "responses",      NULL,               QR_RESPONSE     },
+    { "response",       QRCounterResponse,  -1              },
+    { "qrysuccess",     NULL,               QR_QRYSUCCESS   },
+    { "qryauthans",     NULL,               QR_QRYAUTHANS   },
+    { "qrynoauthans",   NULL,               QR_QRYNOAUTHANS },
+    { "qryreferral",    NULL,               QR_QRYREFERRAL  },
+    { "qrynxrrset",     NULL,               QR_QRYNXRRSET   },
+    { "authqryrej",     NULL,               QR_QRYREJECT    },
+    { "rcode",          QRCounterRcode,     -1              },
+    { NULL,             NULL,               -1              }
+};
+
+const int QROpCodeToQRCounterType[16] = {
+    QR_OPCODE_QUERY,    // Opcode =  0: Query
+    QR_OPCODE_IQUERY,   // Opcode =  1: Iquery
+    QR_OPCODE_STATUS,   // Opcode =  2: STATUS
+    QR_OPCODE_OTHER,    // Opcode =  3: (Unassigned)
+    QR_OPCODE_NOTIFY,   // Opcode =  4: Notify
+    QR_OPCODE_UPDATE,   // Opcode =  5: Update
+    QR_OPCODE_OTHER,    // Opcode =  6: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode =  7: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode =  8: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode =  9: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode = 10: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode = 11: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode = 12: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode = 13: (Unassigned)
+    QR_OPCODE_OTHER,    // Opcode = 14: (Unassigned)
+    QR_OPCODE_OTHER     // Opcode = 15: (Unassigned)
+};
+const int QRQTypeToQRCounterType[258] = {
+    QR_QTYPE_OTHER,         // RRtype =   0: special use
+    QR_QTYPE_A,             // RRtype =   1: A
+    QR_QTYPE_NS,            // RRtype =   2: NS
+    QR_QTYPE_MD,            // RRtype =   3: MD
+    QR_QTYPE_MF,            // RRtype =   4: MF
+    QR_QTYPE_CNAME,         // RRtype =   5: CNAME
+    QR_QTYPE_SOA,           // RRtype =   6: SOA
+    QR_QTYPE_MB,            // RRtype =   7: MB
+    QR_QTYPE_MG,            // RRtype =   8: MG
+    QR_QTYPE_MR,            // RRtype =   9: MR
+    QR_QTYPE_NULL,          // RRtype =  10: NULL
+    QR_QTYPE_WKS,           // RRtype =  11: WKS
+    QR_QTYPE_PTR,           // RRtype =  12: PTR
+    QR_QTYPE_HINFO,         // RRtype =  13: HINFO
+    QR_QTYPE_MINFO,         // RRtype =  14: MINFO
+    QR_QTYPE_MX,            // RRtype =  15: MX
+    QR_QTYPE_TXT,           // RRtype =  16: TXT
+    QR_QTYPE_RP,            // RRtype =  17: RP
+    QR_QTYPE_AFSDB,         // RRtype =  18: AFSDB
+    QR_QTYPE_X25,           // RRtype =  19: X25
+    QR_QTYPE_ISDN,          // RRtype =  20: ISDN
+    QR_QTYPE_RT,            // RRtype =  21: RT
+    QR_QTYPE_NSAP,          // RRtype =  22: NSAP
+    QR_QTYPE_NSAP_PTR,      // RRtype =  23: NSAP-PTR
+    QR_QTYPE_SIG,           // RRtype =  24: SIG
+    QR_QTYPE_KEY,           // RRtype =  25: KEY
+    QR_QTYPE_PX,            // RRtype =  26: PX
+    QR_QTYPE_GPOS,          // RRtype =  27: GPOS
+    QR_QTYPE_AAAA,          // RRtype =  28: AAAA
+    QR_QTYPE_LOC,           // RRtype =  29: LOC
+    QR_QTYPE_NXT,           // RRtype =  30: NXT
+    QR_QTYPE_EID,           // RRtype =  31: EID        
+    QR_QTYPE_NIMLOC,        // RRtype =  32: NIMLOC     
+    QR_QTYPE_SRV,           // RRtype =  33: SRV        
+    QR_QTYPE_ATMA,          // RRtype =  34: ATMA       
+    QR_QTYPE_NAPTR,         // RRtype =  35: NAPTR      
+    QR_QTYPE_KX,            // RRtype =  36: KX         
+    QR_QTYPE_CERT,          // RRtype =  37: CERT       
+    QR_QTYPE_A6,            // RRtype =  38: A6         
+    QR_QTYPE_DNAME,         // RRtype =  39: DNAME      
+    QR_QTYPE_SINK,          // RRtype =  40: SINK       
+    QR_QTYPE_OPT,           // RRtype =  41: OPT        
+    QR_QTYPE_APL,           // RRtype =  42: APL        
+    QR_QTYPE_DS,            // RRtype =  43: DS         
+    QR_QTYPE_SSHFP,         // RRtype =  44: SSHFP      
+    QR_QTYPE_IPSECKEY,      // RRtype =  45: IPSECKEY   
+    QR_QTYPE_RRSIG,         // RRtype =  46: RRSIG      
+    QR_QTYPE_NSEC,          // RRtype =  47: NSEC       
+    QR_QTYPE_DNSKEY,        // RRtype =  48: DNSKEY     
+    QR_QTYPE_DHCID,         // RRtype =  49: DHCID      
+    QR_QTYPE_NSEC3,         // RRtype =  50: NSEC3      
+    QR_QTYPE_NSEC3PARAM,    // RRtype =  51: NSEC3PARAM 
+    QR_QTYPE_OTHER,         // RRtype =  52: TLSA
+    QR_QTYPE_OTHER,         // RRtype =  53: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  54: (Unassigned)
+    QR_QTYPE_HIP,           // RRtype =  55: HIP
+    QR_QTYPE_NINFO,         // RRtype =  56: NINFO
+    QR_QTYPE_RKEY,          // RRtype =  57: RKEY
+    QR_QTYPE_TALINK,        // RRtype =  58: TALINK
+    QR_QTYPE_OTHER,         // RRtype =  59: CDS
+    QR_QTYPE_OTHER,         // RRtype =  60: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  61: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  62: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  63: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  64: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  65: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  66: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  67: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  68: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  69: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  70: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  71: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  72: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  73: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  74: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  75: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  76: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  77: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  78: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  79: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  80: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  81: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  82: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  83: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  84: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  85: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  86: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  87: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  88: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  89: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  90: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  91: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  92: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  93: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  94: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  95: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  96: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  97: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype =  98: (Unassigned)
+    QR_QTYPE_SPF,           // RRtype =  99: SPF
+    QR_QTYPE_UINFO,         // RRtype = 100: UINFO
+    QR_QTYPE_UID,           // RRtype = 101: UID
+    QR_QTYPE_GID,           // RRtype = 102: GID
+    QR_QTYPE_UNSPEC,        // RRtype = 103: UNSPEC
+    QR_QTYPE_OTHER,         // RRtype = 104: NID
+    QR_QTYPE_OTHER,         // RRtype = 105: L32
+    QR_QTYPE_OTHER,         // RRtype = 106: L64
+    QR_QTYPE_OTHER,         // RRtype = 107: LP 
+    QR_QTYPE_OTHER,         // RRtype = 108: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 109: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 110: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 111: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 112: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 113: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 114: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 115: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 116: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 117: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 118: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 119: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 120: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 121: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 122: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 123: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 124: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 125: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 126: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 127: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 128: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 129: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 130: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 131: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 132: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 133: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 134: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 135: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 136: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 137: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 138: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 139: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 140: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 141: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 142: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 143: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 144: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 145: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 146: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 147: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 148: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 149: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 150: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 151: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 152: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 153: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 154: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 155: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 156: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 157: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 158: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 159: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 160: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 161: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 162: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 163: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 164: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 165: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 166: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 167: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 168: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 169: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 170: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 171: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 172: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 173: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 174: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 175: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 176: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 177: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 178: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 179: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 180: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 181: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 182: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 183: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 184: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 185: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 186: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 187: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 188: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 189: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 190: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 191: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 192: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 193: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 194: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 195: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 196: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 197: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 198: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 199: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 200: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 201: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 202: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 203: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 204: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 205: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 206: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 207: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 208: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 209: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 210: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 211: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 212: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 213: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 214: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 215: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 216: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 217: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 218: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 219: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 220: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 221: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 222: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 223: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 224: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 225: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 226: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 227: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 228: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 229: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 230: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 231: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 232: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 233: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 234: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 235: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 236: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 237: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 238: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 239: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 240: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 241: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 242: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 243: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 244: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 245: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 246: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 247: (Unassigned)
+    QR_QTYPE_OTHER,         // RRtype = 248: (Unassigned)
+    QR_QTYPE_TKEY,          // RRtype = 249: TKEY
+    QR_QTYPE_TSIG,          // RRtype = 250: TSIG
+    QR_QTYPE_IXFR,          // RRtype = 251: IXFR
+    QR_QTYPE_AXFR,          // RRtype = 252: AXFR
+    QR_QTYPE_MAILB,         // RRtype = 253: MAILB
+    QR_QTYPE_MAILA,         // RRtype = 254: MAILA
+    QR_QTYPE_OTHER,         // RRtype = 255: for All records
+    QR_QTYPE_URI,           // RRtype = 256: URI
+    QR_QTYPE_CAA            // RRtype = 257: CAA
+};
+const int QRRCodeToQRCounterType[23] = {
+    QR_RCODE_NOERROR,       // Rcode =  0: NoError
+    QR_RCODE_FORMERR,       // Rcode =  1: FormErr
+    QR_RCODE_SERVFAIL,      // Rcode =  2: ServFail
+    QR_RCODE_NXDOMAIN,      // Rcode =  3: NXDomain
+    QR_RCODE_NOTIMP,        // Rcode =  4: NotImp
+    QR_RCODE_REFUSED,       // Rcode =  5: Refused
+    QR_RCODE_YXDOMAIN,      // Rcode =  6: YXDomain
+    QR_RCODE_YXRRSET,       // Rcode =  7: YXRRSet
+    QR_RCODE_NXRRSET,       // Rcode =  8: NXRRSet
+    QR_RCODE_NOTAUTH,       // Rcode =  9: NotAuth
+    QR_RCODE_NOTZONE,       // Rcode = 10: NotZone
+    QR_RCODE_OTHER,         // Rcode = 11: (Unassigned)
+    QR_RCODE_OTHER,         // Rcode = 12: (Unassigned)
+    QR_RCODE_OTHER,         // Rcode = 13: (Unassigned)
+    QR_RCODE_OTHER,         // Rcode = 14: (Unassigned)
+    QR_RCODE_OTHER,         // Rcode = 15: (Unassigned)
+    QR_RCODE_BADSIGVERS,    // Rcode = 16: BADVERS, BADSIG
+    QR_RCODE_BADKEY,        // Rcode = 17: BADKEY
+    QR_RCODE_BADTIME,       // Rcode = 18: BADTIME
+    QR_RCODE_BADMODE,       // Rcode = 19: BADMODE
+    QR_RCODE_BADNAME,       // Rcode = 20: BADNAME
+    QR_RCODE_BADALG,        // Rcode = 21: BADALG
+    QR_RCODE_BADTRUNC       // Rcode = 22: BADTRUNC
+};
+
+} // anonymous namespace
+
+#endif // __STATISTICS_ITEMS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 3138c27..6a91309 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -41,7 +41,7 @@ run_unittests_SOURCES += ../query.h ../query.cc
 run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
 run_unittests_SOURCES += ../command.h ../command.cc
 run_unittests_SOURCES += ../common.h ../common.cc
-run_unittests_SOURCES += ../statistics.h ../statistics.cc
+run_unittests_SOURCES += ../statistics.h ../statistics.cc ../statistics_items.h
 run_unittests_SOURCES += ../datasrc_config.h ../datasrc_config.cc
 run_unittests_SOURCES += datasrc_util.h datasrc_util.cc
 run_unittests_SOURCES += auth_srv_unittest.cc
@@ -75,7 +75,6 @@ run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libb10-server-common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libb10-nsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
 run_unittests_LDADD += $(top_builddir)/src/lib/config/tests/libfake_session.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libb10-threads.la
 run_unittests_LDADD += $(GTEST_LDADD)
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 8ebf4e3..8015043 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -35,6 +35,7 @@
 #include <auth/command.h>
 #include <auth/common.h>
 #include <auth/statistics.h>
+#include <auth/statistics_items.h>
 #include <auth/datasrc_config.h>
 
 #include <util/unittests/mock_socketsession.h>
@@ -76,6 +77,7 @@ using namespace isc::server_common::portconfig;
 using isc::datasrc::memory::ZoneTableSegment;
 using isc::UnitTestUtil;
 using boost::scoped_ptr;
+using isc::auth::statistics::Counters;
 
 namespace {
 const char* const CONFIG_TESTDB =
@@ -123,29 +125,47 @@ protected:
 
     // Helper for checking Rcode statistic counters;
     // Checks for one specific Rcode statistics counter value
-    void checkRcodeCounter(const Rcode& rcode, int expected_value) const {
-        EXPECT_EQ(expected_value, server.getCounter(rcode)) <<
-                  "Expected Rcode count for " << rcode.toText() <<
-                  " " << expected_value << ", was: " <<
-                  server.getCounter(rcode);
+    void checkRcodeCounter(const std::string& rcode_name, const int rcode_value,
+                           const int expected_value) const
+    {
+            EXPECT_EQ(expected_value, rcode_value) <<
+                      "Expected Rcode count for " << rcode_name <<
+                      " " << expected_value << ", was: " <<
+                      rcode_value;
     }
 
     // Checks whether all Rcode counters are set to zero
     void checkAllRcodeCountersZero() const {
-        for (int i = 0; i < 17; i++) {
-            checkRcodeCounter(Rcode(i), 0);
-        }
+        // with checking NOERROR == 0 and the others are 0
+        checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 0);
     }
 
     // Checks whether all Rcode counters are set to zero except the given
     // rcode (it is checked to be set to 'value')
     void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) const {
-        for (int i = 0; i < 17; i++) {
-            const Rcode rc(i);
-            if (rc == rcode) {
-                checkRcodeCounter(Rcode(i), value);
-            } else {
-                checkRcodeCounter(Rcode(i), 0);
+        std::string target_rcode_name = rcode.toText();
+        std::transform(target_rcode_name.begin(), target_rcode_name.end(),
+                       target_rcode_name.begin(), ::tolower);
+        // rcode 16 is registered as both BADVERS and BADSIG
+        if (target_rcode_name == "badvers") {
+            target_rcode_name = "badsigvers";
+        }
+
+        const std::map<std::string, ConstElementPtr>
+            stats_map(server.getStatistics()->mapValue());
+
+        const std::string rcode_prefix("rcode.");
+        for (std::map<std::string, ConstElementPtr>::const_iterator
+                 i = stats_map.begin(), e = stats_map.end();
+             i != e;
+             ++i)
+        {
+            if (i->first.compare(0, rcode_prefix.size(), rcode_prefix) == 0) {
+                if (i->first.compare(rcode_prefix + target_rcode_name) == 0) {
+                    checkRcodeCounter(i->first, i->second->intValue(), value);
+                } else {
+                    checkRcodeCounter(i->first, i->second->intValue(), 0);
+                }
             }
         }
     }
@@ -222,6 +242,29 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
                 renderer.getLength());
 }
 
+// Check if the item has expected value.
+// Before reading the item, check the item exists.
+void
+expectCounterItem(ConstElementPtr stats,
+                  const std::string& item, const int expected) {
+    ConstElementPtr value(Element::create(0));
+    if (item == "queries.udp" || item == "queries.tcp" || expected != 0) {
+        // if the value of the item is not zero, the item exists and has
+        // expected value
+        // item "queries.udp" and "queries.tcp" exists whether the value
+        // is zero or nonzero
+        ASSERT_TRUE(stats->find(item, value)) << "    Item: " << item;
+        // Get the value of the item with another method because of API bug
+        // (ticket #2302)
+        value = stats->find(item);
+        EXPECT_EQ(expected, value->intValue()) << "    Item: " << item;
+    } else {
+        // otherwise the item does not exist
+        ASSERT_FALSE(stats->find(item, value)) << "    Item: " << item <<
+            std::endl << "   Value: " << value->intValue();
+    }
+}
+
 // We did not configure any client lists. Therefore it should be REFUSED
 TEST_F(AuthSrvTest, noClientList) {
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -405,7 +448,9 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
         "It should be unsigned with this error";
     // TSIG should have failed, and so the per opcode counter shouldn't be
     // incremented.
-    EXPECT_EQ(0, server.getCounter(Opcode::RESERVED14()));
+    ConstElementPtr stats = server.getStatistics();
+    expectCounterItem(stats, "opcode.normal", 0);
+    expectCounterItem(stats, "opcode.other", 0);
 
     checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
 }
@@ -1041,8 +1086,12 @@ TEST_F(AuthSrvTest,
 
 // Submit UDP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterUDPNormal) {
-    // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics();
+    expectCounterItem(stats_init, "queries.udp", 0);
+    expectCounterItem(stats_init, "queries.tcp", 0);
+    expectCounterItem(stats_init, "opcode.query", 0);
+    expectCounterItem(stats_init, "rcode.refused", 0);
     // Create UDP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -1050,18 +1099,25 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
     createRequestPacket(request_message, IPPROTO_UDP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
-    // After processing UDP query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
-    // The counter for opcode Query should also be one
-    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
-    // The counter for REFUSED responses should also be one, the rest zero
-    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
+    // After processing the UDP query, these counters should be incremented:
+    //   queries.udp, opcode.query, rcode.refused
+    // and these counters should not be incremented:
+    //   queries.tcp
+    ConstElementPtr stats_after = server.getStatistics();
+    expectCounterItem(stats_after, "queries.udp", 1);
+    expectCounterItem(stats_after, "queries.tcp", 0);
+    expectCounterItem(stats_after, "opcode.query", 1);
+    expectCounterItem(stats_after, "rcode.refused", 1);
 }
 
 // Submit TCP normal query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPNormal) {
-    // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics();
+    expectCounterItem(stats_init, "queries.udp", 0);
+    expectCounterItem(stats_init, "queries.tcp", 0);
+    expectCounterItem(stats_init, "opcode.query", 0);
+    expectCounterItem(stats_init, "rcode.refused", 0);
     // Create TCP message and process.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                        default_qid, Name("example.com"),
@@ -1069,18 +1125,24 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
     createRequestPacket(request_message, IPPROTO_TCP);
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
-    // After processing TCP query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
-    // The counter for SUCCESS responses should also be one
-    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
-    // The counter for REFUSED responses should also be one, the rest zero
-    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
+    // After processing the TCP query, these counters should be incremented:
+    //   queries.tcp, opcode.query, rcode.refused
+    // and these counters should not be incremented:
+    //   queries.udp
+    ConstElementPtr stats_after = server.getStatistics();
+    expectCounterItem(stats_after, "queries.udp", 0);
+    expectCounterItem(stats_after, "queries.tcp", 1);
+    expectCounterItem(stats_after, "opcode.query", 1);
+    expectCounterItem(stats_after, "rcode.refused", 1);
 }
 
 // Submit TCP AXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
-    // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics();
+    expectCounterItem(stats_init, "queries.udp", 0);
+    expectCounterItem(stats_init, "queries.tcp", 0);
+    expectCounterItem(stats_init, "opcode.query", 0);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1089,16 +1151,24 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
-    // After processing TCP AXFR query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
-    // No rcodes should be incremented
-    checkAllRcodeCountersZero();
+    // After processing the TCP AXFR query, these counters should be
+    // incremented:
+    //   queries.tcp, opcode.query
+    // and these counters should not be incremented:
+    //   queries.udp
+    ConstElementPtr stats_after = server.getStatistics();
+    expectCounterItem(stats_after, "queries.udp", 0);
+    expectCounterItem(stats_after, "queries.tcp", 1);
+    expectCounterItem(stats_after, "opcode.query", 1);
 }
 
 // Submit TCP IXFR query and check query counter
 TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
-    // The counter should be initialized to 0.
-    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    // The counters should be initialized to 0.
+    ConstElementPtr stats_init = server.getStatistics();
+    expectCounterItem(stats_init, "queries.udp", 0);
+    expectCounterItem(stats_init, "queries.tcp", 0);
+    expectCounterItem(stats_init, "opcode.query", 0);
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                          Name("example.com"), RRClass::IN(), RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
@@ -1107,14 +1177,27 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
-    // After processing TCP IXFR query, the counter should be 1.
-    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    // After processing the TCP IXFR query, these counters should be
+    // incremented:
+    //   queries.tcp, opcode.query
+    // and these counters should not be incremented:
+    //   queries.udp
+    ConstElementPtr stats_after = server.getStatistics();
+    expectCounterItem(stats_after, "queries.udp", 0);
+    expectCounterItem(stats_after, "queries.tcp", 1);
+    expectCounterItem(stats_after, "opcode.query", 1);
 }
 
 TEST_F(AuthSrvTest, queryCounterOpcodes) {
-    for (int i = 0; i < 16; ++i) {
+    // Check for 0..2, 3(=other), 4..5
+    // The counter should be initialized to 0.
+    for (int i = 0; i < 6; ++i) {
         // The counter should be initialized to 0.
-        EXPECT_EQ(0, server.getCounter(Opcode(i)));
+        expectCounterItem(server.getStatistics(),
+                          std::string("opcode.") +
+                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
+                                                  QR_OPCODE_QUERY].name,
+                          0);
 
         // For each possible opcode, create a request message and send it
         UnitTestUtil::createRequestMessage(request_message, Opcode(i),
@@ -1132,7 +1215,45 @@ TEST_F(AuthSrvTest, queryCounterOpcodes) {
         }
 
         // Confirm the counter.
-        EXPECT_EQ(i + 1, server.getCounter(Opcode(i)));
+        expectCounterItem(server.getStatistics(),
+                          std::string("opcode.") +
+                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
+                                                  QR_OPCODE_QUERY].name,
+                          i + 1);
+    }
+    // Check for 6..15
+    // they are treated as the 'other' opcode
+    // the 'other' opcode counter is 4 at this point
+    int expected = 4;
+    for (int i = 6; i < 16; ++i) {
+        // The counter should be initialized to 0.
+        expectCounterItem(server.getStatistics(),
+                          std::string("opcode.") +
+                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
+                                              QR_OPCODE_QUERY].name,
+                          expected);
+
+        // For each possible opcode, create a request message and send it
+        UnitTestUtil::createRequestMessage(request_message, Opcode(i),
+                                           default_qid, Name("example.com"),
+                                           RRClass::IN(), RRType::NS());
+        createRequestPacket(request_message, IPPROTO_UDP);
+
+        // "send" the request once
+        parse_message->clear(Message::PARSE);
+        server.processMessage(*io_message, *parse_message,
+                              *response_obuffer,
+                              &dnsserv);
+
+        // the 'other' opcode counter should be incremented
+        ++expected;
+
+        // Confirm the counter.
+        expectCounterItem(server.getStatistics(),
+                          std::string("opcode.") +
+                              QRCounterOpcode[QROpCodeToQRCounterType[i] -
+                                              QR_OPCODE_QUERY].name,
+                          expected);
     }
 }
 
@@ -1718,6 +1839,15 @@ namespace {
         isc::config::parseAnswer(command_result, response);
         EXPECT_EQ(0, command_result);
     }
+
+    void sendCommand(AuthSrv& server, const std::string& command,
+                     ConstElementPtr args, int expected_result) {
+        ConstElementPtr response = execAuthServerCommand(server, command,
+                                                         args);
+        int command_result = -1;
+        isc::config::parseAnswer(command_result, response);
+        EXPECT_EQ(expected_result, command_result);
+    }
 } // end anonymous namespace
 
 TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
@@ -1789,4 +1919,18 @@ TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
                 Opcode::UPDATE().getCode(), QR_FLAG, 0, 0, 0, 0);
 }
 
+TEST_F(AuthSrvTest, loadZoneCommand) {
+    // Just some very basic tests, to check the command is accepted, and that
+    // it raises on bad arguments, but not on correct ones (full testing
+    // is handled in the unit tests for the corresponding classes)
+
+    // Empty map should fail
+    ElementPtr args(Element::createMap());
+    sendCommand(server, "loadzone", args, 1);
+    // Setting an origin should be enough (even if it isn't actually loaded,
+    // it should be initially accepted)
+    args->set("origin", Element::create("example.com"));
+    sendCommand(server, "loadzone", args, 0);
+}
+
 }
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 280def6..be90d73 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -169,281 +169,6 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) {
     EXPECT_EQ(0, rcode_);
 }
 
-// A helper function commonly used for the "loadzone" command tests.
-// It configures the server with a memory data source containing two
-// zones, and checks the zones are correctly loaded.
-void
-zoneChecks(AuthSrv& server) {
-    const RRClass rrclass(RRClass::IN());
-
-    DataSrcClientsMgr::Holder holder(server.getDataSrcClientsMgr());
-    EXPECT_EQ(ZoneFinder::SUCCESS,
-              holder.findClientList(rrclass)->find(Name("ns.test1.example"))
-              .finder_->find(Name("ns.test1.example"), RRType::A())->code);
-    EXPECT_EQ(ZoneFinder::NXRRSET,
-              holder.findClientList(rrclass)->find(Name("ns.test1.example")).
-              finder_->find(Name("ns.test1.example"), RRType::AAAA())->code);
-    EXPECT_EQ(ZoneFinder::SUCCESS,
-              holder.findClientList(rrclass)->find(Name("ns.test2.example")).
-              finder_->find(Name("ns.test2.example"), RRType::A())->code);
-    EXPECT_EQ(ZoneFinder::NXRRSET,
-              holder.findClientList(rrclass)->find(Name("ns.test2.example")).
-              finder_->find(Name("ns.test2.example"), RRType::AAAA())->code);
-}
-
-void
-installDataSrcClientLists(AuthSrv& server, ClientListMapPtr lists) {
-    server.getDataSrcClientsMgr().setDataSrcClientLists(lists);
-}
-
-void
-configureZones(AuthSrv& server) {
-    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
-                        TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
-                        TEST_DATA_BUILDDIR "/test2.zone.copied"));
-
-    const ConstElementPtr config(Element::fromJSON("{"
-        "\"IN\": [{"
-        "   \"type\": \"MasterFiles\","
-        "   \"params\": {"
-        "       \"test1.example\": \"" +
-                string(TEST_DATA_BUILDDIR "/test1.zone.copied") + "\","
-        "       \"test2.example\": \"" +
-                string(TEST_DATA_BUILDDIR "/test2.zone.copied") + "\""
-        "   },"
-        "   \"cache-enable\": true"
-        "}]}"));
-
-    installDataSrcClientLists(server, configureDataSource(config));
-
-    zoneChecks(server);
-}
-
-void
-newZoneChecks(AuthSrv& server) {
-    const RRClass rrclass(RRClass::IN());
-
-    DataSrcClientsMgr::Holder holder(server.getDataSrcClientsMgr());
-    EXPECT_EQ(ZoneFinder::SUCCESS, holder.findClientList(rrclass)->
-              find(Name("ns.test1.example")).finder_->
-              find(Name("ns.test1.example"), RRType::A())->code);
-
-    // now test1.example should have ns/AAAA
-    EXPECT_EQ(ZoneFinder::SUCCESS, holder.findClientList(rrclass)->
-              find(Name("ns.test1.example")).finder_->
-              find(Name("ns.test1.example"), RRType::AAAA())->code);
-
-    // test2.example shouldn't change
-    EXPECT_EQ(ZoneFinder::SUCCESS, holder.findClientList(rrclass)->
-              find(Name("ns.test2.example")).finder_->
-              find(Name("ns.test2.example"), RRType::A())->code);
-    EXPECT_EQ(ZoneFinder::NXRRSET,
-              holder.findClientList(rrclass)->
-              find(Name("ns.test2.example")).finder_->
-              find(Name("ns.test2.example"), RRType::AAAA())->code);
-}
-
-TEST_F(AuthCommandTest, loadZone) {
-    configureZones(server_);
-
-    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
-                        "/test1-new.zone.in "
-                        TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
-                        "/test2-new.zone.in "
-                        TEST_DATA_BUILDDIR "/test2.zone.copied"));
-
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\"}"));
-    checkAnswer(0);
-    newZoneChecks(server_);
-}
-
-TEST_F(AuthCommandTest,
-#ifdef USE_STATIC_LINK
-       DISABLED_loadZoneSQLite3
-#else
-       loadZoneSQLite3
-#endif
-    )
-{
-    // Prepare the database first
-    const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
-    const string bad_db = TEST_DATA_BUILDDIR "/does-not-exist.sqlite3";
-    stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
-    createSQLite3DB(RRClass::IN(), Name("example.org"), test_db.c_str(), ss);
-    // This describes the data source in the configuration
-    const ConstElementPtr config(Element::fromJSON("{"
-        "\"IN\": [{"
-        "    \"type\": \"sqlite3\","
-        "    \"params\": {\"database_file\": \"" + test_db + "\"},"
-        "    \"cache-enable\": true,"
-        "    \"cache-zones\": [\"example.org\"]"
-        "}]}"));
-    installDataSrcClientLists(server_, configureDataSource(config));
-
-    {
-        DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
-
-        // Check that the A record at www.example.org does not exist
-        EXPECT_EQ(ZoneFinder::NXDOMAIN,
-                  holder.findClientList(RRClass::IN())->
-                  find(Name("example.org")).finder_->
-                  find(Name("www.example.org"), RRType::A())->code);
-
-        // Add the record to the underlying sqlite database, by loading
-        // it as a separate datasource, and updating it
-        ConstElementPtr sql_cfg = Element::fromJSON("{ \"type\": \"sqlite3\","
-                                                    "\"database_file\": \""
-                                                    + test_db + "\"}");
-        DataSourceClientContainer sql_ds("sqlite3", sql_cfg);
-        ZoneUpdaterPtr sql_updater =
-            sql_ds.getInstance().getUpdater(Name("example.org"), false);
-        RRsetPtr rrset(new RRset(Name("www.example.org."), RRClass::IN(),
-                                 RRType::A(), RRTTL(60)));
-        rrset->addRdata(rdata::createRdata(rrset->getType(),
-                                           rrset->getClass(),
-                                           "192.0.2.1"));
-        sql_updater->addRRset(*rrset);
-        sql_updater->commit();
-
-        EXPECT_EQ(ZoneFinder::NXDOMAIN,
-                  holder.findClientList(RRClass::IN())->
-                  find(Name("example.org")).finder_->
-                  find(Name("www.example.org"), RRType::A())->code);
-    }
-
-    // Now send the command to reload it
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"example.org\"}"));
-    checkAnswer(0, "Successful load");
-
-    {
-        DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
-        // And now it should be present too.
-        EXPECT_EQ(ZoneFinder::SUCCESS,
-                  holder.findClientList(RRClass::IN())->
-                  find(Name("example.org")).finder_->
-                  find(Name("www.example.org"), RRType::A())->code);
-    }
-
-    // Some error cases. First, the zone has no configuration. (note .com here)
-    result_ = execAuthServerCommand(server_, "loadzone",
-        Element::fromJSON("{\"origin\": \"example.com\"}"));
-    checkAnswer(1, "example.com");
-
-    {
-        DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
-        // The previous zone is not hurt in any way
-        EXPECT_EQ(ZoneFinder::SUCCESS,
-                  holder.findClientList(RRClass::IN())->
-                  find(Name("example.org")).finder_->
-                  find(Name("example.org"), RRType::SOA())->code);
-    }
-
-    const ConstElementPtr config2(Element::fromJSON("{"
-        "\"IN\": [{"
-        "    \"type\": \"sqlite3\","
-        "    \"params\": {\"database_file\": \"" + bad_db + "\"},"
-        "    \"cache-enable\": true,"
-        "    \"cache-zones\": [\"example.com\"]"
-        "}]}"));
-    EXPECT_THROW(configureDataSource(config2),
-                 ConfigurableClientList::ConfigurationError);
-
-    result_ = execAuthServerCommand(server_, "loadzone",
-        Element::fromJSON("{\"origin\": \"example.com\"}"));
-    checkAnswer(1, "Unreadable");
-
-    DataSrcClientsMgr::Holder holder(server_.getDataSrcClientsMgr());
-    // The previous zone is not hurt in any way
-    EXPECT_EQ(ZoneFinder::SUCCESS,
-              holder.findClientList(RRClass::IN())->
-              find(Name("example.org")).finder_->
-              find(Name("example.org"), RRType::SOA())->code);
-}
-
-TEST_F(AuthCommandTest, loadBrokenZone) {
-    configureZones(server_);
-
-    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
-                        "/test1-broken.zone.in "
-                        TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\"}"));
-    checkAnswer(1);
-    zoneChecks(server_);     // zone shouldn't be replaced
-}
-
-TEST_F(AuthCommandTest, loadUnreadableZone) {
-    configureZones(server_);
-
-    // install the zone file as unreadable
-    ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
-                        "/test1.zone.in "
-                        TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\"}"));
-    checkAnswer(1);
-    zoneChecks(server_);     // zone shouldn't be replaced
-}
-
-TEST_F(AuthCommandTest, loadZoneWithoutDataSrc) {
-    // try to execute load command without configuring the zone beforehand.
-    // it should fail.
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\"}"));
-    checkAnswer(1);
-}
-
-TEST_F(AuthCommandTest, loadZoneInvalidParams) {
-    configureZones(server_);
-
-    // null arg
-    result_ = execAuthServerCommand(server_, "loadzone", ElementPtr());
-    checkAnswer(1, "Null arg");
-
-    // zone class is bogus
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\","
-                                        " \"class\": \"no_such_class\"}"));
-    checkAnswer(1, "No such class");
-
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"test1.example\","
-                                        " \"class\": 1}"));
-    checkAnswer(1, "Integral class");
-
-
-    // origin is missing
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON("{}"));
-    checkAnswer(1, "Missing origin");
-
-    // zone doesn't exist in the data source
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON("{\"origin\": \"xx\"}"));
-    checkAnswer(1, "No such zone");
-
-    // origin is bogus
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON(
-                                        "{\"origin\": \"...\"}"));
-    checkAnswer(1, "Wrong name");
-
-    result_ = execAuthServerCommand(server_, "loadzone",
-                                    Element::fromJSON("{\"origin\": 10}"));
-    checkAnswer(1, "Integral name");
-}
-
 TEST_F(AuthCommandTest, getStats) {
     result_ = execAuthServerCommand(server_, "getstats", ConstElementPtr());
     parseAnswer(rcode_, result_);
diff --git a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
index 22d33cf..585e7c3 100644
--- a/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_builder_unittest.cc
@@ -12,36 +12,62 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <util/unittests/check_valgrind.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
 #include <cc/data.h>
 
+#include <datasrc/client.h>
+#include <datasrc/factory.h>
+
 #include <auth/datasrc_clients_mgr.h>
+#include <auth/datasrc_config.h>
+
+#include <testutils/dnsmessage_test.h>
+
 #include "test_datasrc_clients_mgr.h"
+#include "datasrc_util.h"
 
 #include <gtest/gtest.h>
 
 #include <boost/function.hpp>
 
+#include <cstdlib>
+#include <string>
+#include <sstream>
+
 using isc::data::ConstElementPtr;
+using namespace isc::dns;
+using namespace isc::data;
 using namespace isc::datasrc;
 using namespace isc::auth::datasrc_clientmgr_internal;
+using namespace isc::auth::unittest;
+using namespace isc::testutils;
 
 namespace {
 class DataSrcClientsBuilderTest : public ::testing::Test {
 protected:
     DataSrcClientsBuilderTest() :
+        clients_map(new std::map<RRClass,
+                    boost::shared_ptr<ConfigurableClientList> >),
         builder(&command_queue, &cond, &queue_mutex, &clients_map, &map_mutex),
-        cond(command_queue, delayed_command_queue),
+        cond(command_queue, delayed_command_queue), rrclass(RRClass::IN()),
         shutdown_cmd(SHUTDOWN, ConstElementPtr()),
         noop_cmd(NOOP, ConstElementPtr())
     {}
 
-    TestDataSrcClientsBuilder builder;
+    void configureZones();      // used for loadzone related tests
+
+    ClientListMapPtr clients_map; // configured clients
     std::list<Command> command_queue; // test command queue
     std::list<Command> delayed_command_queue; // commands available after wait
-    ClientListMapPtr clients_map; // configured clients
+    TestDataSrcClientsBuilder builder;
     TestCondVar cond;
     TestMutex queue_mutex;
     TestMutex map_mutex;
+    const RRClass rrclass;
     const Command shutdown_cmd;
     const Command noop_cmd;
 };
@@ -74,13 +100,20 @@ TEST_F(DataSrcClientsBuilderTest, exception) {
     // them.  Right now, we simply abort to prevent the system from running
     // with half-broken state.  Eventually we should introduce a better
     // error handling.
-    command_queue.push_back(noop_cmd);
-    queue_mutex.throw_from_noop = TestMutex::EXCLASS;
-    EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+    if (!isc::util::unittests::runningOnValgrind()) {
+        command_queue.push_back(noop_cmd);
+        queue_mutex.throw_from_noop = TestMutex::EXCLASS;
+        EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+
+        command_queue.push_back(noop_cmd);
+        queue_mutex.throw_from_noop = TestMutex::INTEGER;
+        EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+    }
 
     command_queue.push_back(noop_cmd);
-    queue_mutex.throw_from_noop = TestMutex::INTEGER;
-    EXPECT_DEATH_IF_SUPPORTED({builder.run();}, "");
+    command_queue.push_back(shutdown_cmd); // we need to stop the loop
+    queue_mutex.throw_from_noop = TestMutex::INTERNAL;
+    builder.run();
 }
 
 TEST_F(DataSrcClientsBuilderTest, condWait) {
@@ -106,10 +139,10 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
     Command reconfig_cmd(RECONFIGURE, ConstElementPtr());
 
     // Initially, no clients should be there
-    EXPECT_EQ(ClientListMapPtr(), clients_map);
+    EXPECT_TRUE(clients_map->empty());
 
     // A config that doesn't do much except be accepted
-    ConstElementPtr good_config = isc::data::Element::fromJSON(
+    ConstElementPtr good_config = Element::fromJSON(
         "{"
         "\"IN\": [{"
         "   \"type\": \"MasterFiles\","
@@ -121,7 +154,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
 
     // A configuration that is 'correct' in the top-level, but contains
     // bad data for the type it specifies
-    ConstElementPtr bad_config = isc::data::Element::fromJSON(
+    ConstElementPtr bad_config = Element::fromJSON(
         "{"
         "\"IN\": [{"
         "   \"type\": \"MasterFiles\","
@@ -142,7 +175,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
     // If a 'bad' command argument got here, the config validation should
     // have failed already, but still, the handler should return true,
     // and the clients_map should not be updated.
-    reconfig_cmd.second = isc::data::Element::create("{ \"foo\": \"bar\" }");
+    reconfig_cmd.second = Element::create("{ \"foo\": \"bar\" }");
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(working_config_clients, clients_map);
     // Building failed, so map mutex should not have been locked again
@@ -173,7 +206,7 @@ TEST_F(DataSrcClientsBuilderTest, reconfigure) {
     EXPECT_EQ(2, map_mutex.lock_count);
 
     // And finally, try an empty config to disable all datasource clients
-    reconfig_cmd.second = isc::data::Element::createMap();
+    reconfig_cmd.second = Element::createMap();
     EXPECT_TRUE(builder.handleCommand(reconfig_cmd));
     EXPECT_EQ(0, clients_map->size());
     EXPECT_EQ(3, map_mutex.lock_count);
@@ -193,4 +226,292 @@ TEST_F(DataSrcClientsBuilderTest, badCommand) {
                  isc::Unexpected);
 }
 
+// A helper function commonly used for the "loadzone" command tests.
+// It configures the given data source client lists with a memory data source
+// containing two zones, and checks the zones are correctly loaded.
+void
+zoneChecks(ClientListMapPtr clients_map, RRClass rrclass) {
+    EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+              find(Name("ns.test1.example")).finder_->
+              find(Name("ns.test1.example"), RRType::A())->code);
+    EXPECT_EQ(ZoneFinder::NXRRSET, clients_map->find(rrclass)->second->
+              find(Name("ns.test1.example")).finder_->
+              find(Name("ns.test1.example"), RRType::AAAA())->code);
+    EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+              find(Name("ns.test2.example")).finder_->
+              find(Name("ns.test2.example"), RRType::A())->code);
+    EXPECT_EQ(ZoneFinder::NXRRSET, clients_map->find(rrclass)->second->
+              find(Name("ns.test2.example")).finder_->
+              find(Name("ns.test2.example"), RRType::AAAA())->code);
+}
+
+// Another helper that checks after completing loadzone command.
+void
+newZoneChecks(ClientListMapPtr clients_map, RRClass rrclass) {
+    EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+              find(Name("ns.test1.example")).finder_->
+              find(Name("ns.test1.example"), RRType::A())->code);
+    // now test1.example should have ns/AAAA
+    EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+              find(Name("ns.test1.example")).finder_->
+              find(Name("ns.test1.example"), RRType::AAAA())->code);
+
+    // test2.example shouldn't change
+    EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+              find(Name("ns.test2.example")).finder_->
+              find(Name("ns.test2.example"), RRType::A())->code);
+    EXPECT_EQ(ZoneFinder::NXRRSET,
+              clients_map->find(rrclass)->second->
+              find(Name("ns.test2.example")).finder_->
+              find(Name("ns.test2.example"), RRType::AAAA())->code);
+}
+
+void
+DataSrcClientsBuilderTest::configureZones() {
+    ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
+                             TEST_DATA_BUILDDIR "/test1.zone.copied"));
+    ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
+                             TEST_DATA_BUILDDIR "/test2.zone.copied"));
+
+    const ConstElementPtr config(
+        Element::fromJSON(
+            "{"
+            "\"IN\": [{"
+            "   \"type\": \"MasterFiles\","
+            "   \"params\": {"
+            "       \"test1.example\": \"" +
+            std::string(TEST_DATA_BUILDDIR "/test1.zone.copied") + "\","
+            "       \"test2.example\": \"" +
+            std::string(TEST_DATA_BUILDDIR "/test2.zone.copied") + "\""
+            "   },"
+            "   \"cache-enable\": true"
+            "}]}"));
+    clients_map = configureDataSource(config);
+    zoneChecks(clients_map, rrclass);
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadZone) {
+    // pre test condition checks
+    EXPECT_EQ(0, map_mutex.lock_count);
+    EXPECT_EQ(0, map_mutex.unlock_count);
+
+    configureZones();
+
+    EXPECT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
+                        "/test1-new.zone.in "
+                        TEST_DATA_BUILDDIR "/test1.zone.copied"));
+    EXPECT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
+                        "/test2-new.zone.in "
+                        TEST_DATA_BUILDDIR "/test2.zone.copied"));
+
+    const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+                                   "{\"class\": \"IN\","
+                                   " \"origin\": \"test1.example\"}"));
+    EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
+    EXPECT_EQ(1, map_mutex.lock_count); // we should have acquired the lock
+    EXPECT_EQ(1, map_mutex.unlock_count); // and released it.
+
+    newZoneChecks(clients_map, rrclass);
+}
+
+TEST_F(DataSrcClientsBuilderTest,
+#ifdef USE_STATIC_LINK
+       DISABLED_loadZoneSQLite3
+#else
+       loadZoneSQLite3
+#endif
+    )
+{
+    // Prepare the database first
+    const std::string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
+    std::stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
+    createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
+    // This describes the data source in the configuration
+    const ConstElementPtr config(Element::fromJSON("{"
+        "\"IN\": [{"
+        "    \"type\": \"sqlite3\","
+        "    \"params\": {\"database_file\": \"" + test_db + "\"},"
+        "    \"cache-enable\": true,"
+        "    \"cache-zones\": [\"example.org\"]"
+        "}]}"));
+    clients_map = configureDataSource(config);
+
+    // Check that the A record at www.example.org does not exist
+    EXPECT_EQ(ZoneFinder::NXDOMAIN,
+              clients_map->find(rrclass)->second->
+              find(Name("example.org")).finder_->
+              find(Name("www.example.org"), RRType::A())->code);
+
+    // Add the record to the underlying sqlite database, by loading
+    // it as a separate datasource, and updating it
+    ConstElementPtr sql_cfg = Element::fromJSON("{ \"type\": \"sqlite3\","
+                                                "\"database_file\": \""
+                                                + test_db + "\"}");
+    DataSourceClientContainer sql_ds("sqlite3", sql_cfg);
+    ZoneUpdaterPtr sql_updater =
+        sql_ds.getInstance().getUpdater(Name("example.org"), false);
+    sql_updater->addRRset(
+        *textToRRset("www.example.org. 60 IN A 192.0.2.1"));
+    sql_updater->commit();
+
+    EXPECT_EQ(ZoneFinder::NXDOMAIN,
+              clients_map->find(rrclass)->second->
+              find(Name("example.org")).finder_->
+              find(Name("www.example.org"), RRType::A())->code);
+
+    // Now send the command to reload it
+    const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+                                   "{\"class\": \"IN\","
+                                   " \"origin\": \"example.org\"}"));
+    EXPECT_TRUE(builder.handleCommand(loadzone_cmd));
+    // And now it should be present too.
+    EXPECT_EQ(ZoneFinder::SUCCESS,
+              clients_map->find(rrclass)->second->
+              find(Name("example.org")).finder_->
+              find(Name("www.example.org"), RRType::A())->code);
+
+    // An error case: the zone has no configuration. (note .com here)
+    const Command nozone_cmd(LOADZONE, Element::fromJSON(
+                                 "{\"class\": \"IN\","
+                                 " \"origin\": \"example.com\"}"));
+    EXPECT_THROW(builder.handleCommand(nozone_cmd),
+                 TestDataSrcClientsBuilder::InternalCommandError);
+    // The previous zone is not hurt in any way
+    EXPECT_EQ(ZoneFinder::SUCCESS, clients_map->find(rrclass)->second->
+              find(Name("example.org")).finder_->
+              find(Name("example.org"), RRType::SOA())->code);
+
+    // attempt of reloading a zone but in-memory cache is disabled.
+    const ConstElementPtr config2(Element::fromJSON("{"
+        "\"IN\": [{"
+        "    \"type\": \"sqlite3\","
+        "    \"params\": {\"database_file\": \"" + test_db + "\"},"
+        "    \"cache-enable\": false,"
+        "    \"cache-zones\": [\"example.org\"]"
+        "}]}"));
+    clients_map = configureDataSource(config2);
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE, Element::fromJSON(
+                                 "{\"class\": \"IN\","
+                                 " \"origin\": \"example.org\"}"))),
+                 TestDataSrcClientsBuilder::InternalCommandError);
+
+    // basically impossible case: in-memory cache is completely disabled.
+    // In this implementation of manager-builder, this should never happen,
+    // but it catches it like other configuration error and keeps going.
+    clients_map->clear();
+    boost::shared_ptr<ConfigurableClientList> nocache_list(
+        new ConfigurableClientList(rrclass));
+    nocache_list->configure(
+        Element::fromJSON(
+            "[{\"type\": \"sqlite3\","
+            "  \"params\": {\"database_file\": \"" + test_db + "\"},"
+            "  \"cache-enable\": true,"
+            "  \"cache-zones\": [\"example.org\"]"
+            "}]"), false);           // false = disable cache
+    (*clients_map)[rrclass] = nocache_list;
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE, Element::fromJSON(
+                                 "{\"class\": \"IN\","
+                                 " \"origin\": \"example.org\"}"))),
+                 TestDataSrcClientsBuilder::InternalCommandError);
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadBrokenZone) {
+    configureZones();
+
+    ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_DATA_DIR
+                             "/test1-broken.zone.in "
+                             TEST_DATA_BUILDDIR "/test1.zone.copied"));
+    // there's an error in the new zone file.  reload will be rejected.
+    const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+                                   "{\"class\": \"IN\","
+                                   " \"origin\": \"test1.example\"}"));
+    EXPECT_THROW(builder.handleCommand(loadzone_cmd),
+                 TestDataSrcClientsBuilder::InternalCommandError);
+    zoneChecks(clients_map, rrclass);     // zone shouldn't be replaced
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadUnreadableZone) {
+    configureZones();
+
+    // install the zone file as unreadable
+    ASSERT_EQ(0, std::system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
+                             "/test1.zone.in "
+                             TEST_DATA_BUILDDIR "/test1.zone.copied"));
+    const Command loadzone_cmd(LOADZONE, Element::fromJSON(
+                                   "{\"class\": \"IN\","
+                                   " \"origin\": \"test1.example\"}"));
+    EXPECT_THROW(builder.handleCommand(loadzone_cmd),
+                 TestDataSrcClientsBuilder::InternalCommandError);
+    zoneChecks(clients_map, rrclass);     // zone shouldn't be replaced
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadZoneWithoutDataSrc) {
+    // try to execute load command without configuring the zone beforehand.
+    // it should fail.
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE,
+                             Element::fromJSON(
+                                 "{\"class\": \"IN\", "
+                                 " \"origin\": \"test1.example\"}"))),
+                 TestDataSrcClientsBuilder::InternalCommandError);
+}
+
+TEST_F(DataSrcClientsBuilderTest, loadZoneInvalidParams) {
+    configureZones();
+
+    if (!isc::util::unittests::runningOnValgrind()) {
+        // null arg: this causes assertion failure
+        EXPECT_DEATH_IF_SUPPORTED({
+                builder.handleCommand(Command(LOADZONE, ElementPtr()));
+            }, "");
+    }
+
+    // zone class is bogus (note that this shouldn't happen except in tests)
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE,
+                             Element::fromJSON(
+                                 "{\"origin\": \"test1.example\","
+                                 " \"class\": \"no_such_class\"}"))),
+                 InvalidRRClass);
+
+    // not a string
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE,
+                             Element::fromJSON(
+                                 "{\"origin\": \"test1.example\","
+                                 " \"class\": 1}"))),
+                 isc::data::TypeError);
+
+    // class or origin is missing: result in assertion failure
+    if (!isc::util::unittests::runningOnValgrind()) {
+        EXPECT_DEATH_IF_SUPPORTED({
+                builder.handleCommand(Command(LOADZONE,
+                                              Element::fromJSON(
+                                                  "{\"class\": \"IN\"}")));
+            }, "");
+    }
+
+    // zone doesn't exist in the data source
+    EXPECT_THROW(
+        builder.handleCommand(
+            Command(LOADZONE,
+                    Element::fromJSON(
+                        "{\"class\": \"IN\", \"origin\": \"xx\"}"))),
+        TestDataSrcClientsBuilder::InternalCommandError);
+
+    // origin is bogus
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE,
+                             Element::fromJSON(
+                                 "{\"class\": \"IN\", \"origin\": \"...\"}"))),
+                 EmptyLabel);
+    EXPECT_THROW(builder.handleCommand(
+                     Command(LOADZONE,
+                             Element::fromJSON(
+                                 "{\"origin\": 10, \"class\": 1}"))),
+                 isc::data::TypeError);
+}
+
 } // unnamed namespace
diff --git a/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc b/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
index 7d1eb4d..c37ef11 100644
--- a/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
+++ b/src/bin/auth/tests/datasrc_clients_mgr_unittest.cc
@@ -196,6 +196,55 @@ TEST(DataSrcClientsMgrTest, holder) {
     EXPECT_THROW(TestDataSrcClientsMgr::Holder holder2(mgr), isc::Unexpected);
 }
 
+TEST(DataSrcClientsMgrTest, reload) {
+    TestDataSrcClientsMgr mgr;
+    EXPECT_TRUE(FakeDataSrcClientsBuilder::started);
+    EXPECT_TRUE(FakeDataSrcClientsBuilder::command_queue->empty());
+
+    isc::data::ElementPtr args =
+        isc::data::Element::fromJSON("{ \"class\": \"IN\","
+                                     "  \"origin\": \"example.com\" }");
+    mgr.loadZone(args);
+    EXPECT_EQ(1, FakeDataSrcClientsBuilder::command_queue->size());
+    mgr.loadZone(args);
+    EXPECT_EQ(2, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // Should fail with non-string 'class' value
+    args->set("class", Element::create(1));
+    EXPECT_THROW(mgr.loadZone(args), CommandError);
+    EXPECT_EQ(2, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // And with badclass
+    args->set("class", Element::create("BADCLASS"));
+    EXPECT_THROW(mgr.loadZone(args), CommandError);
+    EXPECT_EQ(2, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // Should succeed without 'class'
+    args->remove("class");
+    mgr.loadZone(args);
+    EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // but fail without origin, without sending new commands
+    args->remove("origin");
+    EXPECT_THROW(mgr.loadZone(args), CommandError);
+    EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // And for 'origin' that is not a string
+    args->set("origin", Element::create(1));
+    EXPECT_THROW(mgr.loadZone(args), CommandError);
+    EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // And origin that is not a correct name
+    args->set("origin", Element::create(".."));
+    EXPECT_THROW(mgr.loadZone(args), CommandError);
+    EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
+
+    // same for empty data and data that is not a map
+    EXPECT_THROW(mgr.loadZone(isc::data::ConstElementPtr()), CommandError);
+    EXPECT_THROW(mgr.loadZone(isc::data::Element::createList()), CommandError);
+    EXPECT_EQ(3, FakeDataSrcClientsBuilder::command_queue->size());
+}
+
 TEST(DataSrcClientsMgrTest, realThread) {
     // Using the non-test definition with a real thread.  Just checking
     // no disruption happens.
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index d2f5a5a..052a70e 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -27,345 +27,97 @@
 #include <cc/session.h>
 
 #include <auth/statistics.h>
+#include <auth/statistics_items.h>
 
 #include <dns/tests/unittest_util.h>
 
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
 using namespace std;
 using namespace isc::cc;
 using namespace isc::dns;
 using namespace isc::data;
+using isc::auth::statistics::Counters;
+using isc::auth::statistics::QRAttributes;
 
 namespace {
 
-class AuthCountersTest : public ::testing::Test {
-private:
-    class MockSession : public AbstractSession {
-    public:
-        MockSession() :
-            // by default we return a simple "success" message.
-            msg_(Element::fromJSON("{\"result\": [0, \"SUCCESS\"]}")),
-            throw_session_error_(false), throw_session_timeout_(false)
-        {}
-        virtual void establish(const char* socket_file);
-        virtual void disconnect();
-        virtual int group_sendmsg(ConstElementPtr msg, string group,
-                                  string instance, string to);
-        virtual bool group_recvmsg(ConstElementPtr& envelope,
-                                   ConstElementPtr& msg,
-                                   bool nonblock, int seq);
-        virtual void subscribe(string group, string instance);
-        virtual void unsubscribe(string group, string instance);
-        virtual void startRead(boost::function<void()> read_callback);
-        virtual int reply(ConstElementPtr envelope, ConstElementPtr newmsg);
-        virtual bool hasQueuedMsgs() const;
-        virtual void setTimeout(size_t) {}
-        virtual size_t getTimeout() const { return (0); };
-
-        void setThrowSessionError(bool flag);
-        void setThrowSessionTimeout(bool flag);
-
-        void setMessage(ConstElementPtr msg) { msg_ = msg; }
-
-        ConstElementPtr sent_msg;
-        string msg_destination;
-    private:
-        ConstElementPtr msg_;
-        bool throw_session_error_;
-        bool throw_session_timeout_;
-    };
-
+class CountersTest : public ::testing::Test {
 protected:
-    AuthCountersTest() : counters() {
-    }
-    ~AuthCountersTest() {
-    }
-    AuthCounters counters;
-    // no need to be inherited from the original class here.
-    class MockModuleSpec {
-    public:
-        bool validateStatistics(ConstElementPtr, const bool valid) const
-            { return (valid); }
-    };
-    MockModuleSpec module_spec_;
+    CountersTest() : counters() {}
+    ~CountersTest() {}
+    Counters counters;
 };
 
+// Test if the values of the counters are all zero except for the items
+// specified in except_for.
 void
-AuthCountersTest::MockSession::establish(const char*) {}
-
-void
-AuthCountersTest::MockSession::disconnect() {}
-
-void
-AuthCountersTest::MockSession::subscribe(string, string)
-{}
-
-void
-AuthCountersTest::MockSession::unsubscribe(string, string)
-{}
-
-void
-AuthCountersTest::MockSession::startRead(boost::function<void()>)
-{}
-
-int
-AuthCountersTest::MockSession::reply(ConstElementPtr, ConstElementPtr) {
-    return (-1);
-}
-
-bool
-AuthCountersTest::MockSession::hasQueuedMsgs() const {
-    return (false);
-}
-
-int
-AuthCountersTest::MockSession::group_sendmsg(ConstElementPtr msg,
-                                              string group, string, string)
-{
-    if (throw_session_error_) {
-        isc_throw(SessionError, "Session Error");
-    }
-    sent_msg = msg;
-    msg_destination = group;
-    return (0);
-}
-
-bool
-AuthCountersTest::MockSession::group_recvmsg(ConstElementPtr&,
-                                              ConstElementPtr& msg, bool, int)
-{
-    if (throw_session_timeout_) {
-        isc_throw(SessionTimeout, "Session Timeout");
-    }
-    msg = msg_;
-    return (true);
-}
-
-void
-AuthCountersTest::MockSession::setThrowSessionError(bool flag) {
-    throw_session_error_ = flag;
-}
-
-void
-AuthCountersTest::MockSession::setThrowSessionTimeout(bool flag) {
-    throw_session_timeout_ = flag;
-}
-
-TEST_F(AuthCountersTest, incrementUDPCounter) {
-    // The counter should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
-    EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_UDP_QUERY));
-    // After increment, the counter should be 1.
-    EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
-}
-
-TEST_F(AuthCountersTest, incrementTCPCounter) {
-    // The counter should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
-    EXPECT_NO_THROW(counters.inc(AuthCounters::SERVER_TCP_QUERY));
-    // After increment, the counter should be 1.
-    EXPECT_EQ(1, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
-}
-
-TEST_F(AuthCountersTest, incrementInvalidCounter) {
-    // Expect to throw an isc::OutOfRange
-    EXPECT_THROW(counters.inc(AuthCounters::SERVER_COUNTER_TYPES),
-                 isc::OutOfRange);
-}
-
-TEST_F(AuthCountersTest, incrementOpcodeCounter) {
-    // The counter should be initialized to 0.  If we increment it by 1
-    // the counter should be 1.
-    for (int i = 0; i < 16; ++i) {
-        EXPECT_EQ(0, counters.getCounter(Opcode(i)));
-        counters.inc(Opcode(i));
-        EXPECT_EQ(1, counters.getCounter(Opcode(i)));
-    }
-}
-
-TEST_F(AuthCountersTest, incrementRcodeCounter) {
-    // The counter should be initialized to 0.  If we increment it by 1
-    // the counter should be 1.
-    for (int i = 0; i < 17; ++i) {
-        EXPECT_EQ(0, counters.getCounter(Rcode(i)));
-        counters.inc(Rcode(i));
-        EXPECT_EQ(1, counters.getCounter(Rcode(i)));
-    }
-}
-
-void
-opcodeDataCheck(ConstElementPtr data, const int expected[16]) {
-    const char* item_names[] = {
-        "query", "iquery", "status", "reserved3", "notify", "update",
-        "reserved6", "reserved7", "reserved8", "reserved9", "reserved10",
-        "reserved11", "reserved12", "reserved13", "reserved14", "reserved15",
-        NULL
-    };
-    int i;
-    for (i = 0; i < 16; ++i) {
-        ASSERT_NE(static_cast<const char*>(NULL), item_names[i]);
-        const string item_name = "opcode." + string(item_names[i]);
-        if (expected[i] == 0) {
-            EXPECT_FALSE(data->get(item_name));
-        } else {
-            EXPECT_EQ(expected[i], data->get(item_name)->intValue());
-        }
-    }
-    // We should have examined all names
-    ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
-}
-
-void
-rcodeDataCheck(ConstElementPtr data, const int expected[17]) {
-    const char* item_names[] = {
-        "noerror", "formerr", "servfail", "nxdomain", "notimp", "refused",
-        "yxdomain", "yxrrset", "nxrrset", "notauth", "notzone", "reserved11",
-        "reserved12", "reserved13", "reserved14", "reserved15", "badvers",
-        NULL
-    };
-    int i;
-    for (i = 0; i < 17; ++i) {
-        ASSERT_NE(static_cast<const char*>(NULL), item_names[i]);
-        const string item_name = "rcode." + string(item_names[i]);
-        if (expected[i] == 0) {
-            EXPECT_FALSE(data->get(item_name));
-        } else {
-            EXPECT_EQ(expected[i], data->get(item_name)->intValue());
+checkCountersAllZeroExcept(const isc::data::ConstElementPtr counters,
+                           const std::set<std::string>& except_for) {
+    std::map<std::string, ConstElementPtr> stats_map = counters->mapValue();
+
+    for (std::map<std::string, ConstElementPtr>::const_iterator
+            i = stats_map.begin(), e = stats_map.end();
+            i != e;
+            ++i)
+    {
+        int expect = 0;
+        if (except_for.count(i->first) != 0) {
+            expect = 1;
         }
+        EXPECT_EQ(expect, i->second->intValue()) << "Expected counter "
+            << i->first << " = " << expect << ", actual: "
+            << i->second->intValue();
     }
-    // We should have examined all names
-    ASSERT_EQ(static_cast<const char*>(NULL), item_names[i]);
 }
 
-TEST_F(AuthCountersTest, getStatisticsWithoutValidator) {
-    // Get statistics data.
-    // Validate if it answers correct data.
+TEST_F(CountersTest, incrementNormalQuery) {
+    Message response(Message::RENDER);
+    QRAttributes qrattrs;
+    std::set<std::string> expect_nonzero;
 
-    // Counters should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
+    expect_nonzero.clear();
+    checkCountersAllZeroExcept(counters.getStatistics(), expect_nonzero);
 
-    // UDP query counter is set to 2.
-    counters.inc(AuthCounters::SERVER_UDP_QUERY);
-    counters.inc(AuthCounters::SERVER_UDP_QUERY);
-    // TCP query counter is set to 1.
-    counters.inc(AuthCounters::SERVER_TCP_QUERY);
-    ConstElementPtr statistics_data = counters.getStatistics();
+    qrattrs.setQueryIPVersion(AF_INET6);
+    qrattrs.setQueryTransportProtocol(IPPROTO_UDP);
+    qrattrs.setQueryOpCode(Opcode::QUERY_CODE);
+    qrattrs.setQueryEDNS(true, false);
+    qrattrs.setQueryDO(true);
+    qrattrs.answerWasSent();
 
-    // UDP query counter is 2 and TCP query counter is 1.
-    EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue());
-    EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue());
+    response.setRcode(Rcode::REFUSED());
+    response.addQuestion(Question(Name("example.com"),
+                                  RRClass::IN(), RRType::AAAA()));
 
-    // By default opcode counters are all 0 and omitted
-    const int opcode_results[16] = { 0, 0, 0, 0, 0, 0, 0, 0,
-                                     0, 0, 0, 0, 0, 0, 0, 0 };
-    opcodeDataCheck(statistics_data, opcode_results);
-    // By default rcode counters are all 0 and omitted
-    const int rcode_results[17] = { 0, 0, 0, 0, 0, 0, 0, 0, 0,
-                                    0, 0, 0, 0, 0, 0, 0, 0 };
-    rcodeDataCheck(statistics_data, rcode_results);
-}
+    counters.inc(qrattrs, response);
 
-void
-updateOpcodeCounters(AuthCounters &counters, const int expected[16]) {
-    for (int i = 0; i < 16; ++i) {
-        for (int j = 0; j < expected[i]; ++j) {
-            counters.inc(Opcode(i));
-        }
-    }
+    expect_nonzero.clear();
+    expect_nonzero.insert("opcode.query");
+    expect_nonzero.insert("queries.udp");
+    expect_nonzero.insert("rcode.refused");
+    checkCountersAllZeroExcept(counters.getStatistics(), expect_nonzero);
 }
 
-void
-updateRcodeCounters(AuthCounters &counters, const int expected[17]) {
-    for (int i = 0; i < 17; ++i) {
-        for (int j = 0; j < expected[i]; ++j) {
-            counters.inc(Rcode(i));
+int
+countTreeElements(const struct CounterTypeTree* tree) {
+    int count = 0;
+    for (int i = 0; tree[i].name != NULL; ++i) {
+        if (tree[i].sub_tree == NULL) {
+            ++count;
+        } else {
+            count += countTreeElements(tree[i].sub_tree);
         }
     }
+    return count;
 }
 
-TEST_F(AuthCountersTest, getStatisticsWithOpcodeCounters) {
-    // Increment some of the opcode counters.  Then they should appear in the
-    // submitted data; others shouldn't
-    const int opcode_results[16] = { 1, 2, 3, 0, 4, 5, 0, 0,
-                                     0, 0, 0, 0, 0, 0, 0, 0 };
-    updateOpcodeCounters(counters, opcode_results);
-    ConstElementPtr statistics_data = counters.getStatistics();
-    opcodeDataCheck(statistics_data, opcode_results);
-}
-
-TEST_F(AuthCountersTest, getStatisticsWithAllOpcodeCounters) {
-    // Increment all opcode counters.  Then they should appear in the
-    // submitted data.
-    const int opcode_results[16] = { 1, 1, 1, 1, 1, 1, 1, 1,
-                                     1, 1, 1, 1, 1, 1, 1, 1 };
-    updateOpcodeCounters(counters, opcode_results);
-    ConstElementPtr statistics_data = counters.getStatistics();
-    opcodeDataCheck(statistics_data, opcode_results);
-}
-
-TEST_F(AuthCountersTest, getStatisticsWithRcodeCounters) {
-    // Increment some of the rcode counters.  Then they should appear in the
-    // submitted data; others shouldn't
-    const int rcode_results[17] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,
-                                    10, 0, 0, 0, 0, 0, 0, 11 };
-    updateRcodeCounters(counters, rcode_results);
-    ConstElementPtr statistics_data = counters.getStatistics();
-    rcodeDataCheck(statistics_data, rcode_results);
+TEST(StatisticsItemsTest, QRItemNamesCheck) {
+    EXPECT_EQ(QR_COUNTER_TYPES, countTreeElements(QRCounterTree));
 }
 
-TEST_F(AuthCountersTest, getStatisticsWithAllRcodeCounters) {
-    // Increment all rcode counters.  Then they should appear in the
-    // submitted data.
-    const int rcode_results[17] = { 1, 1, 1, 1, 1, 1, 1, 1, 1,
-                                     1, 1, 1, 1, 1, 1, 1, 1 };
-    updateOpcodeCounters(counters, rcode_results);
-    ConstElementPtr statistics_data = counters.getStatistics();
-    opcodeDataCheck(statistics_data, rcode_results);
-}
-
-TEST_F(AuthCountersTest, getStatisticsWithValidator) {
-
-    //a validator for the unittest
-    AuthCounters::validator_type validator;
-    ConstElementPtr el;
-
-    // Get statistics data with correct statistics validator.
-    validator = boost::bind(
-        &AuthCountersTest::MockModuleSpec::validateStatistics,
-        &module_spec_, _1, true);
-
-    EXPECT_TRUE(validator(el));
-
-    // register validator to AuthCounters
-    counters.registerStatisticsValidator(validator);
-
-    // Counters should be initialized to 0.
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_UDP_QUERY));
-    EXPECT_EQ(0, counters.getCounter(AuthCounters::SERVER_TCP_QUERY));
-
-    // UDP query counter is set to 2.
-    counters.inc(AuthCounters::SERVER_UDP_QUERY);
-    counters.inc(AuthCounters::SERVER_UDP_QUERY);
-    // TCP query counter is set to 1.
-    counters.inc(AuthCounters::SERVER_TCP_QUERY);
-
-    // checks the value returned by getStatistics
-    ConstElementPtr statistics_data = counters.getStatistics();
-
-    // UDP query counter is 2 and TCP query counter is 1.
-    EXPECT_EQ(2, statistics_data->get("queries.udp")->intValue());
-    EXPECT_EQ(1, statistics_data->get("queries.tcp")->intValue());
-
-    // Get statistics data with incorrect statistics validator.
-    validator = boost::bind(
-        &AuthCountersTest::MockModuleSpec::validateStatistics,
-        &module_spec_, _1, false);
-
-    EXPECT_FALSE(validator(el));
-
-    counters.registerStatisticsValidator(validator);
-
-    // checks the value returned by getStatistics
-    EXPECT_FALSE(counters.getStatistics());
-}
 }
diff --git a/src/bin/auth/tests/test_datasrc_clients_mgr.cc b/src/bin/auth/tests/test_datasrc_clients_mgr.cc
index 44c8b7d..82937c0 100644
--- a/src/bin/auth/tests/test_datasrc_clients_mgr.cc
+++ b/src/bin/auth/tests/test_datasrc_clients_mgr.cc
@@ -50,6 +50,8 @@ TestDataSrcClientsBuilder::doNoop() {
         isc_throw(Exception, "test exception");
     case TestMutex::INTEGER:
         throw 42;
+    case TestMutex::INTERNAL:
+        isc_throw(InternalCommandError, "internal error, should be ignored");
     }
 }
 } // namespace datasrc_clientmgr_internal
diff --git a/src/bin/auth/tests/test_datasrc_clients_mgr.h b/src/bin/auth/tests/test_datasrc_clients_mgr.h
index 4abffa2..9b1a367 100644
--- a/src/bin/auth/tests/test_datasrc_clients_mgr.h
+++ b/src/bin/auth/tests/test_datasrc_clients_mgr.h
@@ -43,7 +43,8 @@ public:
     // None: no throw from specialized doNoop()
     // EXCLASS: throw some exception class object
     // INTEGER: throw an integer
-    enum ExceptionFromNoop { NONE, EXCLASS, INTEGER };
+    // INTERNAL: internal error (shouldn't terminate the thread)
+    enum ExceptionFromNoop { NONE, EXCLASS, INTEGER, INTERNAL };
 
     TestMutex() : lock_count(0), unlock_count(0), noop_count(0),
                   throw_from_noop(NONE)
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index f598472..b9af5c2 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -511,10 +511,7 @@ class TestBindCmdInterpreter(unittest.TestCase):
 
     def test_csv_file_dir(self):
         # Checking default value
-        if "HOME" in os.environ:
-            home_dir = os.environ["HOME"]
-        else:
-            home_dir = pwd.getpwnam(getpass.getuser()).pw_dir
+        home_dir = pwd.getpwnam(getpass.getuser()).pw_dir
         self.assertEqual(home_dir + os.sep + '.bind10' + os.sep,
                          bindcmd.BindCmdInterpreter().csv_file_dir)
 
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 68aadea..55fe619 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -60,6 +60,7 @@ b10_dhcp6_CXXFLAGS = -Wno-unused-parameter
 endif
 
 b10_dhcp6_LDADD  = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index dbffc40..e330e19 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// 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
@@ -21,10 +21,12 @@
 #include <boost/scoped_ptr.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
+#include <util/encode/hex.h>
 #include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <config/ccsession.h>
 #include <log/logger_support.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/triplet.h>
 #include <dhcp/pool.h>
 #include <dhcp/subnet.h>
@@ -60,12 +62,18 @@ typedef std::map<string, string> StringStorage;
 /// no subnet object created yet to store them.
 typedef std::vector<Pool6Ptr> PoolStorage;
 
+/// @brief Collection of options.
+typedef std::vector<OptionPtr> OptionStorage;
+
 /// @brief Global uint32 parameters that will be used as defaults.
 Uint32Storage uint32_defaults;
 
 /// @brief global string parameters that will be used as defaults.
 StringStorage string_defaults;
 
+/// @brief Global storage for options that will be used as defaults.
+OptionStorage option_defaults;
+
 /// @brief a dummy configuration parser
 ///
 /// It is a debugging parser. It does not configure anything,
@@ -135,6 +143,9 @@ protected:
 ///
 /// For overview of usability of this generic purpose parser, see
 /// \ref dhcpv6-config-inherit page.
+///
+/// @todo this class should be turned into the template class which
+/// will handle all uintX_types of data (see ticket #2415).
 class Uint32Parser : public DhcpConfigParser {
 public:
 
@@ -151,12 +162,37 @@ public:
     ///
     /// @param value pointer to the content of parsed values
     virtual void build(ConstElementPtr value) {
+        bool parse_error = false;
+        // Cast the provided value to int64 value to check.
+        int64_t int64value = 0;
         try {
-            value_ = boost::lexical_cast<uint32_t>(value->str());
-        } catch (const boost::bad_lexical_cast &) {
+            // Parsing the value as a int64 value allows to
+            // check if the provided value is within the range
+            // of uint32_t (is not negative or greater than
+            // maximal uint32_t value.
+            int64value = boost::lexical_cast<int64_t>(value->str());
+        } catch (const boost::bad_lexical_cast&) {
+            parse_error = true;
+        }
+        if (!parse_error) {
+            if ((int64value < 0) ||
+                (int64value > std::numeric_limits<uint32_t>::max())) {
+                parse_error = true;
+            } else {
+                try {
+                    value_ = boost::lexical_cast<uint32_t>(value->str());
+                } catch (const boost::bad_lexical_cast &) {
+                    parse_error = true;
+                }
+            }
+
+        }
+
+        if (parse_error) {
             isc_throw(BadValue, "Failed to parse value " << value->str()
                       << " as unsigned 32-bit integer.");
         }
+
         storage_->insert(pair<string, uint32_t>(param_name_, value_));
     }
 
@@ -445,6 +481,296 @@ protected:
     PoolStorage* pools_;
 };
 
+/// @brief Parser for option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option. These entries include option name, option code
+/// and data carried by the option. If parsing is successful than
+/// instance of an option is created and added to the storage provided
+/// by the calling class.
+///
+/// @todo This class parses and validates option name. However it is
+/// not used anywhere util support for option spaces is implemented
+/// (see tickets #2319, #2314). When option spaces are implemented
+/// there will be a way to reference the particular option using
+/// its type (code) or option name.
+class OptionDataParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Class constructor.
+    OptionDataParser(const std::string&)
+        : options_(NULL) { }
+
+    /// @brief Parses the single option data.
+    ///
+    /// This method parses the data of a single option from the configuration.
+    /// The option data includes option name, option code and data being
+    /// carried by this option. Eventually it creates the instance of the
+    /// option.
+    ///
+    /// @warning setStorage must be called with valid storage pointer prior
+    /// to calling this method.
+    ///
+    /// @param option_data_entries collection of entries that define value
+    /// for a particular option.
+    /// @throw Dhcp6ConfigError if invalid parameter specified in
+    /// the configuration.
+    /// @throw isc::InvalidOperation if failed to set storage prior to
+    /// calling build.
+    /// @throw isc::BadValue if option data storage is invalid.
+    virtual void build(ConstElementPtr option_data_entries) {
+        if (options_ == NULL) {
+            isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
+                      "parsing option data.");
+        }
+        BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
+            ParserPtr parser;
+            if (param.first == "name") {
+                boost::shared_ptr<StringParser>
+                    name_parser(dynamic_cast<StringParser*>(StringParser::Factory(param.first)));
+                if (name_parser) {
+                    name_parser->setStorage(&string_values_);
+                    parser = name_parser;
+                }
+            } else if (param.first == "code") {
+                boost::shared_ptr<Uint32Parser>
+                    code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::Factory(param.first)));
+                if (code_parser) {
+                    code_parser->setStorage(&uint32_values_);
+                    parser = code_parser;
+                }
+            } else if (param.first == "data") {
+                boost::shared_ptr<StringParser>
+                    value_parser(dynamic_cast<StringParser*>(StringParser::Factory(param.first)));
+                if (value_parser) {
+                    value_parser->setStorage(&string_values_);
+                    parser = value_parser;
+                }
+            } else {
+                isc_throw(Dhcp6ConfigError,
+                          "Parser error: option-data parameter not supported: "
+                          << param.first);
+            }
+            parser->build(param.second);
+        }
+        // Try to create the option instance.
+        createOption();
+    }
+
+    /// @brief Does nothing.
+    ///
+    /// This function does noting because option data is committed
+    /// by a higher level parser.
+    virtual void commit() { }
+
+    /// @brief Set storage for the parser.
+    ///
+    /// Sets storage for the parser. This storage points to the
+    /// vector of options and is used by multiple instances of
+    /// OptionDataParser. Each instance creates exactly one object
+    /// of dhcp::Option or derived type and appends it to this
+    /// storage.
+    ///
+    /// @param storage pointer to the options storage
+    void setStorage(OptionStorage* storage) {
+        options_ = storage;
+    }
+
+private:
+
+    /// @brief Create option instance.
+    ///
+    /// Creates an instance of an option and adds it to the provided
+    /// options storage. If the option data parsed by \ref build function
+    /// are invalid or insufficient it emits exception.
+    ///
+    /// @warning this function does not check if options_ storage pointer
+    /// is intitialized but this is not needed here because it is checked in
+    /// \ref build function.
+    ///
+    /// @throw Dhcp6ConfigError if parameters provided in the configuration
+    /// are invalid.
+    void createOption() {
+        // Option code is held in the uint32_t storage but is supposed to
+        // be uint16_t value. We need to check that value in the configuration
+        // does not exceed range of uint16_t and is not zero.
+        uint32_t option_code = getUint32Param("code");
+        if (option_code == 0) {
+            isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
+                      << " be equal to zero. Option code '0' is reserved in"
+                      << " DHCPv6.");
+        } else if (option_code > std::numeric_limits<uint16_t>::max()) {
+            isc_throw(Dhcp6ConfigError, "Parser error: value of 'code' must not"
+                      << " exceed " << std::numeric_limits<uint16_t>::max());
+        }
+        // Check the option name has been specified, is non-empty and does not
+        // contain spaces.
+        // @todo possibly some more restrictions apply here?
+        std::string option_name = getStringParam("name");
+        if (option_name.empty()) {
+            isc_throw(Dhcp6ConfigError, "Parser error: option name must not be"
+                      << " empty");
+        } else if (option_name.find(" ") != std::string::npos) {
+            isc_throw(Dhcp6ConfigError, "Parser error: option name must not contain"
+                      << " spaces");
+        }
+
+        // Get option data from the configuration database ('data' field).
+        // Option data is specified by the user as case insensitive string
+        // of hexadecimal digits for each option.
+        std::string option_data = getStringParam("data");
+        // Transform string of hexadecimal digits into binary format.
+        std::vector<uint8_t> binary;
+        try {
+            util::encode::decodeHex(option_data, binary);
+        } catch (...) {
+            isc_throw(Dhcp6ConfigError, "Parser error: option data is not a valid"
+                      << " string of hexadecimal digits: " << option_data);
+        }
+        // Get all existing DHCPv6 option definitions. The one that matches
+        // our option will be picked and used to create it.
+        OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
+        // Get search index #1. It allows searching for options definitions
+        // using option type value.
+        const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+        // Get all option definitions matching option code we want to create.
+        const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
+        size_t num_defs = std::distance(range.first, range.second);
+        OptionPtr option;
+        // Currently we do not allow duplicated definitions and if there are
+        // any duplicates we issue internal server error.
+        if (num_defs > 1) {
+            isc_throw(Dhcp6ConfigError, "Internal error: currently it is not"
+                      << " supported to initialize multiple option definitions"
+                      << " for the same option code. This will be supported once"
+                      << " there option spaces are implemented.");
+        } else if (num_defs == 0) {
+            // @todo We have a limited set of option definitions intiialized at the moment.
+            // In the future we want to initialize option definitions for all options.
+            // Consequently error will be issued if option definition does not exist
+            // for a particular option code. For now it is ok to create generic option
+            // if definition does not exist.
+            OptionPtr option(new Option(Option::V6, static_cast<uint16_t>(option_code),
+                                        binary));
+            // If option is created succesfully, add it to the storage.
+            options_->push_back(option);
+        } else {
+            // We have exactly one option definition for the particular option code.
+            // use it to create option instance.
+            const OptionDefinitionPtr& def = *(range.first);
+            // getFactory should never return NULL pointer.
+            Option::Factory* factory = def->getFactory();
+            assert(factory != NULL);
+            try {
+                OptionPtr option = factory(Option::V6, option_code, binary);
+                options_->push_back(option);
+            } catch (const isc::Exception& ex) {
+                isc_throw(Dhcp6ConfigError, "Parser error: option data does not match"
+                          << " option definition (code " << option_code << "): "
+                          << ex.what());
+            }
+        }
+    }
+
+    /// @brief Get a parameter from the strings storage.
+    ///
+    /// @param param_id parameter identifier.
+    /// @throw Dhcp6ConfigError if parameter has not been found.
+    std::string getStringParam(const std::string& param_id) const {
+        StringStorage::const_iterator param = string_values_.find(param_id);
+        if (param == string_values_.end()) {
+            isc_throw(Dhcp6ConfigError, "Parser error: option-data parameter"
+                      << " '" << param_id << "' not specified");
+        }
+        return (param->second);
+    }
+
+    /// @brief Get a parameter from the uint32 values storage.
+    ///
+    /// @param param_id parameter identifier.
+    /// @throw Dhcp6ConfigError if parameter has not been found.
+    uint32_t getUint32Param(const std::string& param_id) const {
+        Uint32Storage::const_iterator param = uint32_values_.find(param_id);
+        if (param == uint32_values_.end()) {
+            isc_throw(Dhcp6ConfigError, "Parser error: option-data parameter"
+                      << " '" << param_id << "' not specified");
+        }
+        return (param->second);
+    }
+
+    /// Storage for uint32 values (e.g. option code).
+    Uint32Storage uint32_values_;
+    /// Storage for string values (e.g. option name or data).
+    StringStorage string_values_;
+    /// Pointer to options storage. This storage is provided by
+    /// the calling class and is shared by all OptionDataParser objects.
+    OptionStorage* options_;
+};
+
+/// @brief Parser for option data values with ina subnet.
+///
+/// This parser iterates over all entries that define options
+/// data for a particular subnet and creates a collection of options.
+/// If parsing is successful, all these options are added to the Subnet
+/// object.
+class OptionDataListParser : public DhcpConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Unless otherwise specified, parsed options will be stored in
+    /// a global option containers (option_default). That storage location
+    /// is overriden on a subnet basis.
+    OptionDataListParser(const std::string&)
+        : options_(&option_defaults) { }
+
+    /// @brief Parses entries that define options' data for a subnet.
+    ///
+    /// This method iterates over all entries that define option data
+    /// for options within a single subnet and creates options' instances.
+    ///
+    /// @param option_data_list pointer to a list of options' data sets.
+    /// @throw Dhcp6ConfigError if option parsing failed.
+    void build(ConstElementPtr option_data_list) {
+        BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
+            boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
+            // options_ member will hold instances of all options thus
+            // each OptionDataParser takes it as a storage.
+            parser->setStorage(options_);
+            // Build the instance of a singkle option.
+            parser->build(option_value);
+        }
+    }
+
+    /// @brief Set storage for option instances.
+    ///
+    /// @param storage pointer to options storage.
+    void setStorage(OptionStorage* storage) {
+        options_ = storage;
+    }
+
+
+    /// @brief Does nothing.
+    ///
+    /// @todo Currently this function does nothing but in the future
+    /// we may need to extend it to commit at this level.
+    void commit() { }
+
+    /// @brief Create DhcpDataListParser object
+    ///
+    /// @param param_name param name.
+    ///
+    /// @return DhcpConfigParser object.
+    static DhcpConfigParser* Factory(const std::string& param_name) {
+        return (new OptionDataListParser(param_name));
+    }
+
+    /// Pointer to options instances storage.
+    OptionStorage* options_;
+};
+
 /// @brief this class parses a single subnet
 ///
 /// This class parses the whole subnet definition. It creates parsers
@@ -464,35 +790,36 @@ public:
     void build(ConstElementPtr subnet) {
 
         BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
-
             ParserPtr parser(createSubnet6ConfigParser(param.first));
-
-            // if this is an Uint32 parser, tell it to store the values
-            // in values_, rather than in global storage
-            boost::shared_ptr<Uint32Parser> uintParser =
-                boost::dynamic_pointer_cast<Uint32Parser>(parser);
-            if (uintParser) {
-                uintParser->setStorage(&uint32_values_);
-            } else {
-
-                boost::shared_ptr<StringParser> stringParser =
-                    boost::dynamic_pointer_cast<StringParser>(parser);
-                if (stringParser) {
-                    stringParser->setStorage(&string_values_);
-                } else {
-
-                    boost::shared_ptr<PoolParser> poolParser =
-                        boost::dynamic_pointer_cast<PoolParser>(parser);
-                    if (poolParser) {
-                        poolParser->setStorage(&pools_);
-                    }
-                }
+            // The actual type of the parser is unknown here. We have to discover
+            // parser type here to invoke corresponding setStorage function on it.
+            // We discover parser type by trying to cast the parser to various
+            // parser types and checking which one was successful. For this one
+            // a setStorage and build methods are invoked.
+
+            // Try uint32 type parser.
+            if (buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
+                                                          param.second)) {
+                // Storage set, build invoked on the parser, proceed with
+                // next configuration element.
+                continue;
+            }
+            // Try string type parser.
+            if (buildParser<StringParser, StringStorage >(parser, string_values_,
+                                                          param.second)) {
+                continue;
+            }
+            // Try pools parser.
+            if (buildParser<PoolParser, PoolStorage >(parser, pools_,
+                                                      param.second)) {
+                continue;
+            }
+            // Try option data parser.
+            if (buildParser<OptionDataListParser, OptionStorage >(parser, options_,
+                                                                  param.second)) {
+                continue;
             }
-
-            parser->build(param.second);
-            parsers_.push_back(parser);
         }
-
         // Ok, we now have subnet parsed
     }
 
@@ -540,10 +867,78 @@ public:
             subnet->addPool6(*it);
         }
 
+        const Subnet::OptionContainer& options = subnet->getOptions();
+        const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+        // Add subnet specific options.
+        BOOST_FOREACH(OptionPtr option, options_) {
+            Subnet::OptionContainerTypeRange range = idx.equal_range(option->getType());
+            if (std::distance(range.first, range.second) > 0) {
+                LOG_WARN(dhcp6_logger, DHCP6_CONFIG_OPTION_DUPLICATE)
+                    .arg(option->getType()).arg(addr.toText());
+            }
+            subnet->addOption(option);
+        }
+
+        // Check all global options and add them to the subnet object if
+        // they have been configured in the global scope. If they have been
+        // configured in the subnet scope we don't add global option because
+        // the one configured in the subnet scope always takes precedense.
+        BOOST_FOREACH(OptionPtr option, option_defaults) {
+            // Get all options specified locally in the subnet and having
+            // code equal to global option's code.
+            Subnet::OptionContainerTypeRange range = idx.equal_range(option->getType());
+            // @todo: In the future we will be searching for options using either
+            // option code or namespace. Currently we have only the option
+            // code available so if there is at least one option found with the
+            // specific code we don't add globally configured option.
+            // @todo with this code the first globally configured option
+            // with the given code will be added to a subnet. We may
+            // want to issue warning about dropping configuration of
+            // global option if one already exsist.
+            if (std::distance(range.first, range.second) == 0) {
+                subnet->addOption(option);
+            }
+        }
+
         CfgMgr::instance().addSubnet6(subnet);
     }
 
-protected:
+private:
+
+    /// @brief Set storage for a parser and invoke build.
+    ///
+    /// This helper method casts the provided parser pointer to specified
+    /// type. If cast is successful it sets the corresponding storage for
+    /// this parser, invokes build on it and save the parser.
+    ///
+    /// @tparam T parser type to which parser argument should be cast.
+    /// @tparam Y storage type for the specified parser type.
+    /// @param parser parser on which build must be invoked.
+    /// @param storage reference to a storage that will be set for a parser.
+    /// @param subnet subnet element read from the configuration and being parsed.
+    /// @return true if parser pointer was successfully cast to specialized
+    /// parser type provided as Y.
+    template<typename T, typename Y>
+    bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
+        // We need to cast to T in order to set storage for the parser.
+        boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
+        // It is common that this cast is not successful because we try to cast to all
+        // supported parser types as we don't know the type of a parser in advance.
+        if (cast_parser) {
+            // Cast, successful so we go ahead with setting storage and actual parse.
+            cast_parser->setStorage(&storage);
+            parser->build(subnet);
+            parsers_.push_back(parser);
+            // We indicate that cast was successful so as the calling function
+            // may skip attempts to cast to other parser types and proceed to
+            // next element.
+            return (true);
+        }
+        // It was not successful. Indicate that another parser type
+        // should be tried.
+        return (false);
+    }
 
     /// @brief creates parsers for entries in subnet definition
     ///
@@ -569,6 +964,10 @@ protected:
         factories.insert(pair<string, ParserFactory*>(
                              "pool", PoolParser::Factory));
 
+        factories.insert(pair<string, ParserFactory*>(
+                             "option-data", OptionDataListParser::Factory));
+
+
         FactoryMap::iterator f = factories.find(config_id);
         if (f == factories.end()) {
             // Used for debugging only.
@@ -622,6 +1021,9 @@ protected:
     /// storage for pools belonging to this subnet
     PoolStorage pools_;
 
+    /// storage for options belonging to this subnet
+    OptionStorage options_;
+
     /// parsers are stored here
     ParserCollection parsers_;
 };
@@ -698,7 +1100,6 @@ public:
 DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
     FactoryMap factories;
 
-    //
     factories.insert(pair<string, ParserFactory*>(
                          "preferred-lifetime", Uint32Parser::Factory));
     factories.insert(pair<string, ParserFactory*>(
@@ -714,6 +1115,9 @@ DhcpConfigParser* createGlobalDhcpConfigParser(const std::string& config_id) {
                          "subnet6", Subnets6ListConfigParser::Factory));
 
     factories.insert(pair<string, ParserFactory*>(
+                         "option-data", OptionDataListParser::Factory));
+
+    factories.insert(pair<string, ParserFactory*>(
                          "version", StringParser::Factory));
 
     FactoryMap::iterator f = factories.find(config_id);
@@ -749,6 +1153,12 @@ configureDhcp6Server(Dhcpv6Srv& , ConstElementPtr config_set) {
                   "Null pointer is passed to configuration parser");
     }
 
+    /// Reset global storage. Containers being reset below may contain
+    /// data from the previous configuration attempts.
+    option_defaults.clear();
+    uint32_defaults.clear();
+    string_defaults.clear();
+
     /// @todo: append most essential info here (like "2 new subnets configured")
     string config_details;
 
diff --git a/src/bin/dhcp6/dhcp6.dox b/src/bin/dhcp6/dhcp6.dox
index ff47e31..c234f40 100644
--- a/src/bin/dhcp6/dhcp6.dox
+++ b/src/bin/dhcp6/dhcp6.dox
@@ -76,4 +76,6 @@
  simple as possible. In fact, currently the code has to call Subnet6->getT1() and
  do not implement any fancy inheritance logic.
 
+ @todo Add section about setting up options and their definitions with bindctl.
+
  */
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index f35f606..c5e9565 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -40,6 +40,37 @@
         "item_default": 4000
       },
 
+      { "item_name": "option-data",
+        "item_type": "list",
+        "item_optional": false,
+        "item_default": [],
+        "list_item_spec":
+        {
+          "item_name": "single-option-data",
+          "item_type": "map",
+          "item_optional": false,
+          "item_default": {},
+          "map_item_spec": [
+          {
+            "item_name": "name",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
+          },
+
+          { "item_name": "code",
+            "item_type": "integer",
+            "item_optional": false,
+            "item_default": 0
+          },
+          { "item_name": "data",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
+          } ]
+        }
+      },
+
       { "item_name": "subnet6",
         "item_type": "list",
         "item_optional": false,
@@ -92,10 +123,40 @@
                         "item_optional": false,
                         "item_default": ""
                     }
-                }
-            ]
-        }
-      }
+                },
+                { "item_name": "option-data",
+                  "item_type": "list",
+                  "item_optional": false,
+                  "item_default": [],
+                  "list_item_spec":
+                  {
+                    "item_name": "single-option-data",
+                    "item_type": "map",
+                    "item_optional": false,
+                    "item_default": {},
+                    "map_item_spec": [
+                    {
+                      "item_name": "name",
+                      "item_type": "string",
+                      "item_optional": false,
+                      "item_default": ""
+                    },
+                    {
+                      "item_name": "code",
+                      "item_type": "integer",
+                      "item_optional": false,
+                      "item_default": 0
+                    },
+                    {
+                      "item_name": "data",
+                      "item_type": "string",
+                      "item_optional": false,
+                      "item_default": ""
+                    } ]
+                  }
+                } ]
+            }
+       }
     ],
     "commands": [
         {
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index d5d3b72..5f9cd02 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -153,6 +153,16 @@ to a misconfiguration of the server. The packet processing will continue, but
 the response will only contain generic configuration parameters and no
 addresses or prefixes.
 
+% DHCP6_NO_SUBNET_DEF_OPT failed to find subnet for address %1 when adding default options
+This warning message indicates that when attempting to add default options to a response,
+the server found that it was not configured to support the subnet from which the DHCPv6
+request was received.  The packet has been ignored.
+
+% DHCP6_NO_SUBNET_REQ_OPT failed to find subnet for address %1 when adding requested options
+This warning message indicates that when attempting to add requested options to a response,
+the server found that it was not configured to support the subnet from which the DHCPv6
+request was received.  The packet has been ignored.
+
 % DHCP6_CONFIG_LOAD_FAIL failed to load configuration: %1
 This critical error message indicates that the initial DHCPv6
 configuration has failed. The server will start, but nothing will be
@@ -172,3 +182,8 @@ This is an informational message announcing the successful processing of a
 new configuration. it is output during server startup, and when an updated
 configuration is committed by the administrator.  Additional information
 may be provided.
+
+% DHCP6_CONFIG_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
+This warning message is issued on attempt to configure multiple options with the
+same option code for the particular subnet. Adding multiple options is uncommon
+for DHCPv6, yet it is not prohibited.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index ad3e1d7..a749035 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -20,10 +20,14 @@
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_ia.h>
+#include <dhcp/option6_int_array.h>
 #include <dhcp/pkt6.h>
+#include <dhcp/subnet.h>
+#include <dhcp/cfgmgr.h>
 #include <exceptions/exceptions.h>
 #include <util/io_utilities.h>
 #include <util/range_utilities.h>
@@ -36,6 +40,8 @@
 // once it is merged
 #include <dhcp/memfile_lease_mgr.h>
 
+#include <boost/foreach.hpp>
+
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
@@ -49,9 +55,13 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
 
-    // First call to instance() will create IfaceMgr (it's a singleton)
-    // it may throw something if things go wrong
+    // Initialize objects required for DHCP server operation.
     try {
+        // Initialize standard DHCPv6 option definitions. This function
+        // may throw bad_alloc if system goes out of memory during the
+        // creation if option definitions. It may also throw isc::Unexpected
+        // if definitions are wrong. This would mean error in implementation.
+        initStdOptionDefs();
 
         // Port 0 is used for testing purposes. It means that the server should
         // not open any sockets at all. Some tests, e.g. configuration parser,
@@ -63,7 +73,6 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port) {
                 shutdown_ = true;
                 return;
             }
-
             IfaceMgr::instance().openSockets6(port);
         }
 
@@ -300,23 +309,68 @@ void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
 }
 
-void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
-    // TODO: question is currently unused, but we need it at least to know
-    // message type we are answering
-
-    // Add server-id.
+void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+    // add server-id
     answer->addOption(getServerID());
-}
 
+    // Get the subnet object. It holds options to be sent to the client
+    // that belongs to the particular subnet.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+    // Warn if subnet is not supported and quit.
+    if (!subnet) {
+        LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_DEF_OPT)
+            .arg(question->getRemoteAddr().toText());
+        return;
+    }
+    // Add DNS_SERVERS option. It should have been configured.
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+    const Subnet::OptionContainerTypeRange range =
+        idx.equal_range(D6O_NAME_SERVERS);
+    // In theory we may have multiple options with the same
+    // option code. They are not differentiated right now
+    // until support for option spaces is implemented.
+    // Until that's the case, simply add the first found option.
+    if (std::distance(range.first, range.second) > 0) {
+        answer->addOption(range.first->option);
+    }
+}
 
-void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
-    // TODO: question is currently unused, but we need to extract ORO from it
-    // and act on its content. Now we just send DNS-SERVERS option.
+void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
+    // Get the subnet for a particular address.
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr());
+    if (!subnet) {
+        LOG_WARN(dhcp6_logger, DHCP6_NO_SUBNET_REQ_OPT)
+            .arg(question->getRemoteAddr().toText());
+        return;
+    }
 
     // Add dns-servers option.
     OptionPtr dnsservers(new Option6AddrLst(D6O_NAME_SERVERS,
                                             IOAddress(HARDCODED_DNS_SERVER)));
     answer->addOption(dnsservers);
+
+    // Client requests some options using ORO option. Try to
+    // get this option from client's message.
+    boost::shared_ptr<Option6IntArray<uint16_t> > option_oro =
+        boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(question->getOption(D6O_ORO));
+    // Option ORO not found. Don't do anything then.
+    if (!option_oro) {
+        return;
+    }
+    // Get the list of options that client requested.
+    const std::vector<uint16_t>& requested_opts = option_oro->getValues();
+    // Get the list of options configured for a subnet.
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+    // Try to match requested options with those configured for a subnet.
+    // If match is found, append configured option to the answer message.
+    BOOST_FOREACH(uint16_t opt, requested_opts) {
+        const Subnet::OptionContainerTypeRange& range = idx.equal_range(opt);
+        BOOST_FOREACH(Subnet::OptionDescriptor desc, range) {
+            answer->addOption(desc.option);
+        }
+    }
 }
 
 OptionPtr Dhcpv6Srv::createStatusCode(uint16_t code, const std::string& text) {
@@ -587,3 +641,8 @@ Dhcpv6Srv::serverReceivedPacketName(uint8_t type) {
     }
     return (UNKNOWN);
 }
+
+void
+Dhcpv6Srv::initStdOptionDefs() {
+    LibDHCP::initStdOptionDefs(Option::V6);
+}
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 3553bff..0beccaf 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -15,15 +15,17 @@
 #ifndef DHCPV6_SRV_H
 #define DHCPV6_SRV_H
 
+#include <iostream>
+
 #include <boost/noncopyable.hpp>
+#include <dhcp/alloc_engine.h>
 #include <dhcp/dhcp6.h>
-#include <dhcp/pkt6.h>
-#include <dhcp/option.h>
-#include <dhcp/subnet.h>
 #include <dhcp/duid.h>
+#include <dhcp/option.h>
 #include <dhcp/option6_ia.h>
-#include <dhcp/alloc_engine.h>
-#include <iostream>
+#include <dhcp/option_definition.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/subnet.h>
 
 namespace isc {
 
@@ -205,8 +207,6 @@ protected:
     /// @brief Appends requested options to server's answer.
     ///
     /// Appends options requested by client to the server's answer.
-    /// TODO: This method is currently a stub. It just appends DNS-SERVERS
-    /// option.
     ///
     /// @param question client's message
     /// @param answer server's message (options will be added here)
@@ -234,6 +234,18 @@ protected:
     ///         interfaces for new DUID generation are detected.
     void setServerID();
 
+    /// @brief Initializes option definitions for standard options.
+    ///
+    /// Each standard option's format is described by the
+    /// dhcp::OptionDefinition object. This function creates such objects
+    /// for each standard DHCPv6 option.
+    ///
+    /// @todo list thrown exceptions.
+    /// @todo extend this function to cover all standard options. Currently
+    /// it is limited to critical options only.
+    void initStdOptionDefs();
+
+private:
     /// server DUID (to be sent in server-identifier option)
     boost::shared_ptr<isc::dhcp::Option> serverid_;
 
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index 1629ae6..78caa2f 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -63,6 +63,7 @@ dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp6_unittests_LDADD = $(GTEST_LDADD)
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 22592e8..929a6d2 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -17,14 +17,18 @@
 #include <fstream>
 #include <sstream>
 
+#include <boost/foreach.hpp>
+
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/config_parser.h>
 #include <config/ccsession.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcp/subnet.h>
 #include <dhcp/cfgmgr.h>
+#include <dhcp/option6_ia.h>
 
 using namespace std;
 using namespace isc;
@@ -43,12 +47,138 @@ public:
         // deal with sockets here, just check if configuration handling
         // is sane.
         srv_ = new Dhcpv6Srv(0);
+        // Create instances of option definitions and put them into storage.
+        // This is normally initialized by the server when calling run()
+        // run() function.
+        LibDHCP::initStdOptionDefs(Option::V6);
     }
 
     ~Dhcp6ParserTest() {
         delete srv_;
     };
 
+    /// @brief Create the simple configuration with single option.
+    ///
+    /// This function allows to set one of the parameters that configure
+    /// option value. These parameters are: "name", "code" and "data".
+    ///
+    /// @param param_value string holiding option parameter value to be
+    /// injected into the configuration string.
+    /// @param parameter name of the parameter to be configured with
+    /// param value.
+    std::string createConfigWithOption(const std::string& param_value,
+                                       const std::string& parameter) {
+        std::map<std::string, std::string> params;
+        if (parameter == "name") {
+            params["name"] = param_value;
+            params["code"] = "80";
+            params["data"] = "AB CDEF0105";
+        } else if (parameter == "code") {
+            params["name"] = "option_foo";
+            params["code"] = param_value;
+            params["data"] = "AB CDEF0105";
+        } else if (parameter == "data") {
+            params["name"] = "option_foo";
+            params["code"] = "80";
+            params["data"] = param_value;
+        }
+        return (createConfigWithOption(params));
+    }
+
+    std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
+        std::ostringstream stream;
+        stream << "{ \"interface\": [ \"all\" ],"
+            "\"preferred-lifetime\": 3000,"
+            "\"rebind-timer\": 2000, "
+            "\"renew-timer\": 1000, "
+            "\"subnet6\": [ { "
+            "    \"pool\": [ \"2001:db8:1::/80\" ],"
+            "    \"subnet\": \"2001:db8:1::/64\", "
+            "    \"option-data\": [ {";
+        bool first = true;
+        typedef std::pair<std::string, std::string> ParamPair;
+        BOOST_FOREACH(ParamPair param, params) {
+            if (!first) {
+                stream << ", ";
+            } else {
+                first = false;
+            }
+            if (param.first == "name") {
+                stream << "\"name\": \"" << param.second << "\"";
+            } else if (param.first == "code") {
+                stream << "\"code\": " << param.second << "";
+            } else if (param.first == "data") {
+                stream << "\"data\": \"" << param.second << "\"";
+            }
+        }
+        stream <<
+            "        } ]"
+            " } ],"
+            "\"valid-lifetime\": 4000 }";
+        return (stream.str());
+    }
+
+    /// @brief Test invalid option parameter value.
+    ///
+    /// This test function constructs the simple configuration
+    /// string and injects invalid option configuration into it.
+    /// It expects that parser will fail with provided option code.
+    ///
+    /// @param param_value string holding invalid option parameter value
+    /// to be injected into configuration string.
+    /// @param parameter name of the parameter to be configured with
+    /// param_value (can be any of "name", "code", "data")
+    void testInvalidOptionParam(const std::string& param_value,
+                                const std::string& parameter) {
+        ConstElementPtr x;
+        std::string config = createConfigWithOption(param_value, parameter);
+        ElementPtr json = Element::fromJSON(config);
+        EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+        ASSERT_TRUE(x);
+        comment_ = parseAnswer(rcode_, x);
+        ASSERT_EQ(1, rcode_);
+    }
+
+    /// @brief Test option against given code and data.
+    ///
+    /// @param option_desc option descriptor that carries the option to
+    /// be tested.
+    /// @param expected_code expected code of the option.
+    /// @param expected_data expected data in the option.
+    /// @param expected_data_len length of the reference data.
+    /// @param extra_data if true extra data is allowed in an option
+    /// after tested data.
+    void testOption(const Subnet::OptionDescriptor& option_desc,
+                    uint16_t expected_code, const uint8_t* expected_data,
+                    size_t expected_data_len,
+                    bool extra_data = false) {
+        // Check if option descriptor contains valid option pointer.
+        ASSERT_TRUE(option_desc.option);
+        // Verify option type.
+        EXPECT_EQ(expected_code, option_desc.option->getType());
+        // We may have many different option types being created. Some of them
+        // have dedicated classes derived from Option class. In such case if
+        // we want to verify the option contents against expected_data we have
+        // to prepare raw buffer with the contents of the option. The easiest
+        // way is to call pack() which will prepare on-wire data.
+        util::OutputBuffer buf(option_desc.option->getData().size());
+        option_desc.option->pack(buf);
+        if (extra_data) {
+            // The length of the buffer must be at least equal to size of the
+            // reference data but it can sometimes be greater than that. This is
+            // because some options carry suboptions that increase the overall
+            // length.
+            ASSERT_GE(buf.getLength() - option_desc.option->getHeaderLen(),
+                      expected_data_len);
+        } else {
+            ASSERT_EQ(buf.getLength() - option_desc.option->getHeaderLen(),
+                      expected_data_len);
+        }
+        // Verify that the data is correct. However do not verify suboptions.
+        const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
+        EXPECT_TRUE(memcmp(expected_data, data, expected_data_len));
+    }
+
     Dhcpv6Srv* srv_;
 
     int rcode_;
@@ -73,7 +203,7 @@ TEST_F(Dhcp6ParserTest, version) {
 
 /// The goal of this test is to verify that the code accepts only
 /// valid commands and malformed or unsupported parameters are rejected.
-TEST_F(Dhcp6ParserTest, bogus_command) {
+TEST_F(Dhcp6ParserTest, bogusCommand) {
 
     ConstElementPtr x;
 
@@ -89,7 +219,7 @@ TEST_F(Dhcp6ParserTest, bogus_command) {
 /// The goal of this test is to verify if wrongly defined subnet will
 /// be rejected. Properly defined subnet must include at least one
 /// pool definition.
-TEST_F(Dhcp6ParserTest, empty_subnet) {
+TEST_F(Dhcp6ParserTest, emptySubnet) {
 
     ConstElementPtr status;
 
@@ -109,7 +239,7 @@ TEST_F(Dhcp6ParserTest, empty_subnet) {
 
 /// The goal of this test is to verify if defined subnet uses global
 /// parameter timer definitions.
-TEST_F(Dhcp6ParserTest, subnet_global_defaults) {
+TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
 
     ConstElementPtr status;
 
@@ -144,7 +274,7 @@ TEST_F(Dhcp6ParserTest, subnet_global_defaults) {
 
 // This test checks if it is possible to override global values
 // on a per subnet basis.
-TEST_F(Dhcp6ParserTest, subnet_local) {
+TEST_F(Dhcp6ParserTest, subnetLocal) {
 
     ConstElementPtr status;
 
@@ -181,7 +311,7 @@ TEST_F(Dhcp6ParserTest, subnet_local) {
 
 // Test verifies that a subnet with pool values that do not belong to that
 // pool are rejected.
-TEST_F(Dhcp6ParserTest, pool_out_of_subnet) {
+TEST_F(Dhcp6ParserTest, poolOutOfSubnet) {
 
     ConstElementPtr status;
 
@@ -209,7 +339,7 @@ TEST_F(Dhcp6ParserTest, pool_out_of_subnet) {
 // Goal of this test is to verify if pools can be defined
 // using prefix/length notation. There is no separate test for min-max
 // notation as it was tested in several previous tests.
-TEST_F(Dhcp6ParserTest, pool_prefix_len) {
+TEST_F(Dhcp6ParserTest, poolPrefixLen) {
 
     ConstElementPtr x;
 
@@ -240,4 +370,372 @@ TEST_F(Dhcp6ParserTest, pool_prefix_len) {
     EXPECT_EQ(4000, subnet->getValid());
 }
 
+// Goal of this test is to verify that global option
+// data is configured for the subnet if the subnet
+// configuration does not include options configuration.
+TEST_F(Dhcp6ParserTest, optionDataDefaults) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"option_foo\","
+        "    \"code\": 100,"
+        "    \"data\": \"AB CDEF0105\""
+        " },"
+        " {"
+        "    \"name\": \"option_foo2\","
+        "    \"code\": 101,"
+        "    \"data\": \"01\""
+        " } ],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(2, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(100);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 100, foo_expected, sizeof(foo_expected));
+
+    range = idx.equal_range(101);
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // Do another round of testing with second option.
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range.first, 101, foo2_expected, sizeof(foo2_expected));
+
+    // Check that options with other option codes are not returned.
+    for (uint16_t code = 102; code < 110; ++code) {
+        range = idx.equal_range(code);
+        EXPECT_EQ(0, std::distance(range.first, range.second));
+    }
+}
+
+// Goal of this test is to verify options configuration
+// for a single subnet. In particular this test checks
+// that local options configuration overrides global
+// option setting.
+TEST_F(Dhcp6ParserTest, optionDataInSingleSubnet) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"option-data\": [ {"
+        "      \"name\": \"option_foo\","
+        "      \"code\": 100,"
+        "      \"data\": \"AB\""
+        " } ],"
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"option_foo\","
+        "          \"code\": 100,"
+        "          \"data\": \"AB CDEF0105\""
+        "        },"
+        "        {"
+        "          \"name\": \"option_foo2\","
+        "          \"code\": 101,"
+        "          \"data\": \"01\""
+        "        } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(2, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(100);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 100, foo_expected, sizeof(foo_expected));
+
+    range = idx.equal_range(101);
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // Do another round of testing with second option.
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range.first, 101, foo2_expected, sizeof(foo2_expected));
+}
+
+// Goal of this test is to verify options configuration
+// for multiple subnets.
+TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1::/80\" ],"
+        "    \"subnet\": \"2001:db8:1::/64\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"option_foo\","
+        "          \"code\": 100,"
+        "          \"data\": \"0102030405060708090A\""
+        "        } ]"
+        " },"
+        " {"
+        "    \"pool\": [ \"2001:db8:2::/80\" ],"
+        "    \"subnet\": \"2001:db8:2::/64\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"option_foo2\","
+        "          \"code\": 101,"
+        "          \"data\": \"FFFEFDFCFB\""
+        "        } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet1 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet1);
+    const Subnet::OptionContainer& options1 = subnet1->getOptions();
+    ASSERT_EQ(1, options1.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx1 = options1.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range1 =
+        idx1.equal_range(100);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range1.first, range1.second));
+    const uint8_t foo_expected[] = {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x0A
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range1.first, 100, foo_expected, sizeof(foo_expected));
+
+    // Test another subnet in the same way.
+    Subnet6Ptr subnet2 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:2::4"));
+    ASSERT_TRUE(subnet2);
+    const Subnet::OptionContainer& options2 = subnet2->getOptions();
+    ASSERT_EQ(1, options2.size());
+
+    const Subnet::OptionContainerTypeIndex& idx2 = options2.get<1>();
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range2 =
+        idx2.equal_range(101);
+    ASSERT_EQ(1, std::distance(range2.first, range2.second));
+
+    const uint8_t foo2_expected[] = {
+        0xFF, 0xFE, 0xFD, 0xFC, 0xFB
+    };
+    testOption(*range2.first, 101, foo2_expected, sizeof(foo2_expected));
+}
+
+// Verify that empty option name is rejected in the configuration.
+TEST_F(Dhcp6ParserTest, optionNameEmpty) {
+    // Empty option names not allowed.
+    testInvalidOptionParam("", "name");
+}
+
+// Verify that empty option name with spaces is rejected
+// in the configuration.
+TEST_F(Dhcp6ParserTest, optionNameSpaces) {
+    // Spaces in option names not allowed.
+    testInvalidOptionParam("option foo", "name");
+}
+
+// Verify that negative option code is rejected in the configuration.
+TEST_F(Dhcp6ParserTest, optionCodeNegative) {
+    // Check negative option code -4. This should fail too.
+    testInvalidOptionParam("-4", "code");
+}
+
+// Verify that out of bounds option code is rejected in the configuration.
+TEST_F(Dhcp6ParserTest, optionCodeNonUint16) {
+    // The valid option codes are uint16_t values so passing
+    // uint16_t maximum value incremented by 1 should result
+    // in failure.
+    testInvalidOptionParam("65536", "code");
+}
+
+// Verify that out of bounds option code is rejected in the configuration.
+TEST_F(Dhcp6ParserTest, optionCodeHighNonUint16) {
+    // Another check for uint16_t overflow but this time
+    // let's pass even greater option code value.
+    testInvalidOptionParam("70000", "code");
+}
+
+// Verify that zero option code is rejected in the configuration.
+TEST_F(Dhcp6ParserTest, optionCodeZero) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("0", "code");
+}
+
+// Verify that option data which contains non hexadecimal characters
+// is rejected by the configuration.
+TEST_F(Dhcp6ParserTest, optionDataInvalidChar) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("01020R", "data");
+}
+
+// Verify that option data containins '0x' prefix is rejected
+// by the configuration.
+TEST_F(Dhcp6ParserTest, optionDataUnexpectedPrefix) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("0x0102", "data");
+}
+
+// Verify that option data consisting od an odd number of
+// hexadecimal digits is rejected in the configuration.
+TEST_F(Dhcp6ParserTest, optionDataOddLength) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("123", "data");
+}
+
+// Verify that either lower or upper case characters are allowed
+// to specify the option data.
+TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
+    ConstElementPtr x;
+    std::string config = createConfigWithOption("0a0b0C0D", "data");
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(1, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(80);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0x0A, 0x0B, 0x0C, 0x0D
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 80, foo_expected, sizeof(foo_expected));
+}
+
+// Verify that specific option object is returned for standard
+// option which has dedicated option class derived from Option.
+TEST_F(Dhcp6ParserTest, stdOptionData) {
+    ConstElementPtr x;
+    std::map<std::string, std::string> params;
+    params["name"] = "OPTION_IA_NA";
+    // Option code 3 means OPTION_IA_NA.
+    params["code"] = "3";
+    params["data"] = "ABCDEF01 02030405 06070809";
+    
+    std::string config = createConfigWithOption(params);
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(1, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(D6O_IA_NA);
+    // Expect single option with the code equal to IA_NA option code.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // The actual pointer to the option is held in the option field
+    // in the structure returned.
+    OptionPtr option = range.first->option;
+    ASSERT_TRUE(option);
+    // Option object returned for here is expected to be Option6IA
+    // which is derived from Option. This class is dedicated to
+    // represent standard option IA_NA.
+    boost::shared_ptr<Option6IA> optionIA =
+        boost::dynamic_pointer_cast<Option6IA>(option);
+    // If cast is unsuccessful than option returned was of a
+    // differnt type than Option6IA. This is wrong.
+    ASSERT_TRUE(optionIA);
+    // If cast was successful we may use accessors exposed by
+    // Option6IA to validate that the content of this option
+    // has been set correctly.
+    EXPECT_EQ(0xABCDEF01, optionIA->getIAID());
+    EXPECT_EQ(0x02030405, optionIA->getT1());
+    EXPECT_EQ(0x06070809, optionIA->getT2());
+}
+
 };
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 76f7eff..7e9a395 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -12,31 +12,41 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <gtest/gtest.h>
 
 #include <asiolink/io_address.h>
+#include <boost/scoped_ptr.hpp>
+#include <config/ccsession.h>
+#include <dhcp/cfgmgr.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/duid.h>
-#include <dhcp/cfgmgr.h>
+#include <dhcp/lease_mgr.h>
 #include <dhcp/option.h>
+#include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_int_array.h>
+#include <dhcp6/config_parser.h>
+#include <dhcp6/dhcp6_srv.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <util/buffer.h>
 #include <util/range_utilities.h>
-#include <dhcp/lease_mgr.h>
-#include <boost/scoped_ptr.hpp>
-#include <config.h>
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <gtest/gtest.h>
 
-using namespace std;
+using namespace boost;
 using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::util;
-using namespace isc::asiolink;
-using namespace boost;
+using namespace std;
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // Maybe it should be isc::test?
@@ -56,7 +66,7 @@ public:
 class Dhcpv6SrvTest : public ::testing::Test {
 public:
     // these are empty for now, but let's keep them around
-    Dhcpv6SrvTest() {
+    Dhcpv6SrvTest() : rcode_(-1) {
         subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
                                          2000, 3000, 4000));
         pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
@@ -181,6 +191,9 @@ public:
 
     // A DUID used in most tests (typically as client-id)
     DuidPtr duid_;
+
+    int rcode_;
+    ConstElementPtr comment_;
 };
 
 // Test verifies that the Dhcpv6_srv class can be instantiated. It checks a mode
@@ -191,19 +204,17 @@ TEST_F(Dhcpv6SrvTest, basic) {
     // interfaces.txt instead. It will pretend to have detected
     // fe80::1234 link-local address on eth0 interface. Obviously
     // an attempt to bind this socket will fail.
-    Dhcpv6Srv* srv = NULL;
+    boost::scoped_ptr<Dhcpv6Srv> srv;
+
     ASSERT_NO_THROW( {
         // Skip opening any sockets
-        srv = new Dhcpv6Srv(0);
+        srv.reset(new Dhcpv6Srv(0));
     });
-
-    delete srv;
-
-    ASSERT_NO_THROW( {
+    srv.reset();
+    ASSERT_NO_THROW({
         // open an unpriviledged port
-        srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+        srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000));
     });
-    delete srv;
 }
 
 // Test checks that DUID is generated properly
@@ -282,6 +293,159 @@ TEST_F(Dhcpv6SrvTest, DUID) {
     }
 }
 
+TEST_F(Dhcpv6SrvTest, solicitBasic1) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"preferred-lifetime\": 3000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pool\": [ \"2001:db8:1234::/80\" ],"
+        "    \"subnet\": \"2001:db8:1234::/64\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"OPTION_DNS_SERVERS\","
+        "          \"code\": 23,"
+        "          \"data\": \"2001 0DB8 1234 FFFF 0000 0000 0000 0001"
+        "2001 0DB8 1234 FFFF 0000 0000 0000 0002\""
+        "        },"
+        "        {"
+        "          \"name\": \"OPTION_FOO\","
+        "          \"code\": 1000,"
+        "          \"data\": \"1234\""
+        "        } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    boost::scoped_ptr<NakedDhcpv6Srv> srv;
+    ASSERT_NO_THROW(srv.reset(new NakedDhcpv6Srv(0)));
+
+    EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+
+    ASSERT_EQ(0, rcode_);
+
+    // a dummy content for client-id
+    OptionBuffer clntDuid(32);
+    for (int i = 0; i < 32; i++) {
+        clntDuid[i] = 100 + i;
+    }
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+
+    boost::shared_ptr<Option6IA> ia =
+        boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, 234));
+    ia->setT1(1501);
+    ia->setT2(2601);
+    sol->addOption(ia);
+
+    // Let's not send address in solicit yet
+    /*    boost::shared_ptr<Option6IAAddr>
+        addr(new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:ffff::ffff"), 5001, 7001));
+    ia->addOption(addr);
+    sol->addOption(ia); */
+
+    // constructed very simple SOLICIT message with:
+    // - client-id option (mandatory)
+    // - IA option (a request for address, without any addresses)
+
+    // expected returned ADVERTISE message:
+    // - copy of client-id
+    // - server-id
+    // - IA that includes IAADDR
+
+    OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID,
+                                              clntDuid.begin(),
+                                              clntDuid.begin() + 16));
+    sol->addOption(clientid);
+
+    boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
+
+    // check if we get response at all
+    ASSERT_TRUE(reply);
+
+    EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
+    EXPECT_EQ(1234, reply->getTransid());
+
+    // We have not requested option with code 1000 so it should not
+    // be included in the response.
+    ASSERT_FALSE(reply->getOption(1000));
+
+    // Let's now request option with code 1000.
+    // We expect that server will include this option in its reply.
+    boost::shared_ptr<Option6IntArray<uint16_t> >
+        option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
+    // Create vector with one code equal to 1000.
+    std::vector<uint16_t> codes(1, 1000);
+    // Pass this code to option.
+    option_oro->setValues(codes);
+    // Append ORO to SOLICIT message.
+    sol->addOption(option_oro);
+    
+    // Need to process SOLICIT again after requesting new option.
+    reply = srv->processSolicit(sol);
+    ASSERT_TRUE(reply);
+
+    EXPECT_EQ(DHCPV6_ADVERTISE, reply->getType());
+
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+
+    boost::shared_ptr<Option6IA> reply_ia =
+        boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(reply_ia);
+    EXPECT_EQ(234, reply_ia->getIAID());
+
+    // check that there's an address included
+    EXPECT_TRUE(reply_ia->getOption(D6O_IAADDR));
+
+    // check that server included our own client-id
+    tmp = reply->getOption(D6O_CLIENTID);
+    ASSERT_TRUE(tmp);
+    EXPECT_EQ(clientid->getType(), tmp->getType());
+    ASSERT_EQ(clientid->len(), tmp->len());
+
+    EXPECT_TRUE(clientid->getData() == tmp->getData());
+
+    // check that server included its server-id
+    tmp = reply->getOption(D6O_SERVERID);
+    EXPECT_EQ(tmp->getType(), srv->getServerID()->getType());
+    ASSERT_EQ(tmp->len(),  srv->getServerID()->len());
+
+    EXPECT_TRUE(tmp->getData() == srv->getServerID()->getData());
+ 
+    tmp = reply->getOption(D6O_NAME_SERVERS);
+    ASSERT_TRUE(tmp);
+    
+    boost::shared_ptr<Option6AddrLst> reply_nameservers =
+        boost::dynamic_pointer_cast<Option6AddrLst>(tmp);
+    ASSERT_TRUE(reply_nameservers);
+
+    Option6AddrLst::AddressContainer addrs = reply_nameservers->getAddresses();
+    ASSERT_EQ(2, addrs.size());
+    EXPECT_TRUE(addrs[0] == IOAddress("2001:db8:1234:FFFF::1"));
+    EXPECT_TRUE(addrs[1] == IOAddress("2001:db8:1234:FFFF::2"));
+
+    // There is a dummy option with code 1000 we requested from a server.
+    // Expect that this option is in server's response.
+    tmp = reply->getOption(1000);
+    ASSERT_TRUE(tmp);
+
+    // Check that the option contains valid data (from configuration).
+    std::vector<uint8_t> data = tmp->getData();
+    ASSERT_EQ(2, data.size());
+
+    const uint8_t foo_expected[] = {
+        0x12, 0x34
+    };
+    EXPECT_EQ(0, memcmp(&data[0], foo_expected, 2));
+
+    // more checks to be implemented
+}
+
+
 // There are no dedicated tests for Dhcpv6Srv::handleIA_NA and Dhcpv6Srv::assignLeases
 // as they are indirectly tested in Solicit and Request tests.
 
@@ -299,7 +463,7 @@ TEST_F(Dhcpv6SrvTest, DUID) {
 // - copy of client-id
 // - server-id
 // - IA that includes IAADDR
-TEST_F(Dhcpv6SrvTest, SolicitBasic) {
+TEST_F(Dhcpv6SrvTest, SolicitBasic2) {
     boost::scoped_ptr<NakedDhcpv6Srv> srv;
     ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
 
diff --git a/src/bin/sysinfo/.gitignore b/src/bin/sysinfo/.gitignore
index 9194aff..b5c759d 100644
--- a/src/bin/sysinfo/.gitignore
+++ b/src/bin/sysinfo/.gitignore
@@ -1,3 +1,4 @@
 /isc-sysinfo
-/sysinfo.py
 /isc-sysinfo.1
+/run_sysinfo.sh
+/sysinfo.py
diff --git a/src/bin/sysinfo/Makefile.am b/src/bin/sysinfo/Makefile.am
index 25a3556..1618535 100644
--- a/src/bin/sysinfo/Makefile.am
+++ b/src/bin/sysinfo/Makefile.am
@@ -1,4 +1,5 @@
 bin_SCRIPTS = isc-sysinfo
+noinst_SCRIPTS = run_sysinfo.sh
 
 CLEANFILES = isc-sysinfo sysinfo.pyc
 
diff --git a/src/bin/sysinfo/run_sysinfo.sh.in b/src/bin/sysinfo/run_sysinfo.sh.in
new file mode 100755
index 0000000..268b5a4
--- /dev/null
+++ b/src/bin/sysinfo/run_sysinfo.sh.in
@@ -0,0 +1,38 @@
+#! /bin/sh
+
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM 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.
+
+PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
+export PYTHON_EXEC
+
+SYSINFO_PATH=@abs_top_builddir@/src/bin/sysinfo
+
+# Note: we shouldn't need log_messages except for the seemingly necessary
+# dependency due to the automatic import in the isc package (its __init__.py
+# imports some other modules)
+PYTHONPATH=@abs_top_builddir@/src/lib/python:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python/isc/log_messages
+export PYTHONPATH
+
+# Likewise, we need only because isc.log requires some loadable modules.
+# sysinfo itself shouldn't need any of them.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
+	export @ENV_LIBRARY_PATH@
+fi
+
+cd ${SYSINFO_PATH}
+exec ${PYTHON_EXEC} -O sysinfo.py "$@"
diff --git a/src/bin/sysinfo/sysinfo.py.in b/src/bin/sysinfo/sysinfo.py.in
index 24cf309..477b9de 100755
--- a/src/bin/sysinfo/sysinfo.py.in
+++ b/src/bin/sysinfo/sysinfo.py.in
@@ -22,11 +22,8 @@ ISC sysinfo program.
 
 import sys; sys.path.append ('@@PYTHONPATH@@')
 import getopt
-import isc.util.process
 from isc.sysinfo import *
 
-isc.util.process.rename()
-
 def usage():
     print("Usage: %s [-h] [-o <output-file>]" % sys.argv[0], \
               file=sys.stderr)
@@ -88,7 +85,8 @@ def main():
 
     write_value(f, ' + Machine name: %s\n', s.get_platform_machine)
     write_value(f, ' + Hostname: %s\n', s.get_platform_hostname)
-    write_value(f, ' + Uptime: %d seconds\n', s.get_uptime)
+    write_value(f, ' + Uptime: %d seconds', s.get_uptime)
+    write_value(f, ' (%s)\n', s.get_uptime_desc)
 
     write_value(f, ' + Loadavg: %f %f %f\n', s.get_loadavg)
 
diff --git a/src/bin/tests/process_rename_test.py.in b/src/bin/tests/process_rename_test.py.in
index 1156b29..055ebdc 100644
--- a/src/bin/tests/process_rename_test.py.in
+++ b/src/bin/tests/process_rename_test.py.in
@@ -39,6 +39,11 @@ class TestRename(unittest.TestCase):
         Then scan them by looking at the source text
         (without actually running them)
         """
+
+        # Scripts named in this list are not expected to be renamed and
+        # should be excluded from the scan.
+        EXCLUDED_SCRIPTS = ['isc-sysinfo']
+
         # Regexp to find all the *_SCRIPTS = something lines (except for
         # noinst_SCRIPTS, which are scripts for tests), including line
         # continuations (backslash and newline)
@@ -59,6 +64,8 @@ class TestRename(unittest.TestCase):
                 for (var, _) in lines.findall(re.sub(excluded_lines, '',
                                                      makefile)):
                     for (script, _) in scripts.findall(var):
+                        if script in EXCLUDED_SCRIPTS:
+                            continue
                         self.__scan(d, script, fun)
 
 if __name__ == "__main__":
diff --git a/src/lib/bench/example/search_bench.cc b/src/lib/bench/example/search_bench.cc
index 84f95d9..d022d55 100644
--- a/src/lib/bench/example/search_bench.cc
+++ b/src/lib/bench/example/search_bench.cc
@@ -116,7 +116,6 @@ main(int argc, char* argv[]) {
         }
     }
     argc -= optind;
-    argv += optind;
     if (argc != 0) {
         usage();
     }
diff --git a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
index 4e6af56..7bb919f 100644
--- a/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
+++ b/src/lib/datasrc/memory/benchmarks/rdata_reader_bench.cc
@@ -183,7 +183,6 @@ main(int argc, char* argv[]) {
         }
     }
     argc -= optind;
-    argv += optind;
     if (argc != 0) {
         usage();
     }
diff --git a/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc b/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc
index bc418e8..266f4f5 100644
--- a/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc
+++ b/src/lib/datasrc/memory/benchmarks/rrset_render_bench.cc
@@ -179,7 +179,6 @@ main(int argc, char* argv[]) {
         }
     }
     argc -= optind;
-    argv += optind;
     if (argc != 0) {
         usage();
     }
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index be593fe..d1ff852 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -27,6 +27,8 @@
 
 #include <gtest/gtest.h>
 
+#include <boost/shared_ptr.hpp>
+
 #include <set>
 #include <fstream>
 
@@ -36,7 +38,9 @@ using isc::datasrc::memory::ZoneTableSegment;
 using isc::datasrc::memory::InMemoryZoneFinder;
 using namespace isc::data;
 using namespace isc::dns;
-using namespace boost;
+// don't import the entire boost namespace.  It will unexpectedly hide uintXX_t
+// for some systems.
+using boost::shared_ptr;
 using namespace std;
 
 namespace {
@@ -313,7 +317,7 @@ public:
                   result.life_keeper_);
         if (from_cache) {
             EXPECT_NE(shared_ptr<InMemoryZoneFinder>(),
-                      dynamic_pointer_cast<InMemoryZoneFinder>(
+                      boost::dynamic_pointer_cast<InMemoryZoneFinder>(
                           result.finder_)) << "Finder is not from cache";
             EXPECT_TRUE(NULL !=
                         dynamic_cast<InMemoryClient*>(result.dsrc_client_));
diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc
index 7032a41..6ac3f14 100644
--- a/src/lib/dhcp/addr_utilities.cc
+++ b/src/lib/dhcp/addr_utilities.cc
@@ -46,14 +46,13 @@ const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
 /// @param len prefix length
 isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
                                             uint8_t len) {
-
     if (len > 128) {
-        isc_throw(BadValue, "IPv6 prefix length too large:" << len);
+        isc_throw(isc::BadValue,
+                  "Too large netmask. 0..128 is allowed in IPv6");
     }
 
-    uint8_t packed[V6ADDRESS_LEN];
-
     // First we copy the whole address as 16 bytes.
+    uint8_t packed[V6ADDRESS_LEN];
     memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
 
     // If the length is divisible by 8, it is simple. We just zero out the host
@@ -126,12 +125,12 @@ isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefi
 isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
                                            uint8_t len) {
     if (len > 128) {
-        isc_throw(BadValue, "IPv6 prefix length too large:" << len);
+        isc_throw(isc::BadValue,
+                  "Too large netmask. 0..128 is allowed in IPv6");
     }
 
-    uint8_t packed[V6ADDRESS_LEN];
-
     // First we copy the whole address as 16 bytes.
+    uint8_t packed[V6ADDRESS_LEN];
     memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
 
     // if the length is divisible by 8, it is simple. We just fill the host part
diff --git a/src/lib/dhcp/alloc_engine.cc b/src/lib/dhcp/alloc_engine.cc
index c9954c2..650fd5f 100644
--- a/src/lib/dhcp/alloc_engine.cc
+++ b/src/lib/dhcp/alloc_engine.cc
@@ -15,6 +15,8 @@
 #include <alloc_engine.h>
 #include <string.h>
 
+#include <cstring>
+
 using namespace isc::asiolink;
 
 namespace isc {
@@ -32,11 +34,11 @@ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress&
     // First we copy the whole address as 16 bytes.
     if (addr.getFamily()==AF_INET) {
         // IPv4
-        memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4);
+        std::memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4);
         len = 4;
     } else {
         // IPv6
-        memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16);
+        std::memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16);
         len = 16;
     }
 
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 12e1bbd..99a3b91 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -23,6 +23,8 @@
 #include <dhcp/option.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_definition.h>
+#include <dhcp/option6_int_array.h>
 
 using namespace std;
 using namespace isc::dhcp;
@@ -34,6 +36,23 @@ std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
 // static array with factories for options
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 
+// Static container with DHCPv4 option definitions.
+OptionDefContainer LibDHCP::v4option_defs_;
+
+// Static container with DHCPv6 option definitions.
+OptionDefContainer LibDHCP::v6option_defs_;
+
+const OptionDefContainer&
+LibDHCP::getOptionDefs(Option::Universe u) {
+    switch (u) {
+    case Option::V4:
+        return (v4option_defs_);
+    case Option::V6:
+        return (v6option_defs_);
+    default:
+        isc_throw(isc::BadValue, "invalid universe " << u << " specified");
+    }
+}
 
 OptionPtr
 LibDHCP::optionFactory(Option::Universe u,
@@ -88,6 +107,11 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
                                               buf.begin() + offset,
                                               buf.begin() + offset + opt_len));
             break;
+        case D6O_ORO:
+            opt = OptionPtr(new Option6IntArray<uint16_t>(opt_type,
+                                                          buf.begin() + offset,
+                                                          buf.begin() + offset + opt_len));
+            break;
         default:
             opt = OptionPtr(new Option(Option::V6,
                                        opt_type,
@@ -201,3 +225,81 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
 
     return;
 }
+
+void
+LibDHCP::initStdOptionDefs(Option::Universe u) {
+    switch (u) {
+    case Option::V4:
+        initStdOptionDefs4();
+        break;
+    case Option::V6:
+        initStdOptionDefs6();
+        break;
+    default:
+        isc_throw(isc::BadValue, "invalid universe " << u << " specified");
+    }
+}
+
+void
+LibDHCP::initStdOptionDefs4() {
+    isc_throw(isc::NotImplemented, "initStdOptionDefs4 is not implemented");
+}
+
+void
+LibDHCP::initStdOptionDefs6() {
+    v6option_defs_.clear();
+
+    struct OptionParams {
+        std::string name;
+        uint16_t code;
+        OptionDefinition::DataType type;
+        bool array;
+    };
+    OptionParams params[] = {
+        { "CLIENTID", D6O_CLIENTID, OptionDefinition::BINARY_TYPE, false },
+        { "SERVERID", D6O_SERVERID, OptionDefinition::BINARY_TYPE, false },
+        { "IA_NA", D6O_IA_NA, OptionDefinition::RECORD_TYPE, false },
+        { "IAADDR", D6O_IAADDR, OptionDefinition::RECORD_TYPE, false },
+        { "ORO", D6O_ORO, OptionDefinition::UINT16_TYPE, true },
+        { "ELAPSED_TIME", D6O_ELAPSED_TIME, OptionDefinition::UINT16_TYPE, false },
+        { "STATUS_CODE", D6O_STATUS_CODE, OptionDefinition::RECORD_TYPE, false },
+        { "RAPID_COMMIT", D6O_RAPID_COMMIT, OptionDefinition::EMPTY_TYPE, false },
+        { "DNS_SERVERS", D6O_NAME_SERVERS, OptionDefinition::IPV6_ADDRESS_TYPE, true }
+    };
+    const int params_size = sizeof(params) / sizeof(params[0]);
+
+    for (int i = 0; i < params_size; ++i) {
+        OptionDefinitionPtr definition(new OptionDefinition(params[i].name,
+                                                            params[i].code,
+                                                            params[i].type,
+                                                            params[i].array));
+        switch(params[i].code) {
+        case D6O_IA_NA:
+            for (int j = 0; j < 3; ++j) {
+                definition->addRecordField(OptionDefinition::UINT32_TYPE);
+            }
+            break;
+        case D6O_IAADDR:
+            definition->addRecordField(OptionDefinition::IPV6_ADDRESS_TYPE);
+            definition->addRecordField(OptionDefinition::UINT32_TYPE);
+            definition->addRecordField(OptionDefinition::UINT32_TYPE);
+            break;
+        case D6O_STATUS_CODE:
+            definition->addRecordField(OptionDefinition::UINT16_TYPE);
+            definition->addRecordField(OptionDefinition::STRING_TYPE);
+            break;
+        default:
+            // The default case is intentionally left empty
+            // as it does not need any processing.
+            ;
+        }
+        try {
+            definition->validate();
+        } catch (const Exception& ex) {
+            isc_throw(isc::Unexpected, "internal server error: invalid definition of standard"
+                      << " DHCPv6 option (with code " << params[i].code << "): "
+                      << ex.what());
+        }
+        v6option_defs_.push_back(definition);
+    }
+}
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index ae90701..c057eea 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-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
@@ -15,9 +15,10 @@
 #ifndef LIBDHCP_H_
 #define LIBDHCP_H_
 
-#include <iostream>
-#include <util/buffer.h>
+#include <dhcp/option_definition.h>
 #include <dhcp/pkt6.h>
+#include <util/buffer.h>
+#include <iostream>
 
 namespace isc {
 namespace dhcp {
@@ -29,6 +30,12 @@ public:
     /// Map of factory functions.
     typedef std::map<unsigned short, Option::Factory*>  FactoryMap;
 
+    /// @brief Return collection of option definitions.
+    ///
+    /// @param u universe of the options (V4 or V6).
+    /// @return collection of option definitions.
+    static const OptionDefContainer& getOptionDefs(Option::Universe u);
+
     /// @brief Factory function to create instance of option.
     ///
     /// Factory method creates instance of specified option. The option
@@ -102,12 +109,54 @@ public:
     static void OptionFactoryRegister(Option::Universe u,
                                       uint16_t type,
                                       Option::Factory * factory);
-protected:
+
+    /// Initialize standard DHCP options (V4 or V6).
+    ///
+    /// The method creates option definitions for all options
+    /// (DHCPv4 or DHCPv6 depending on universe specified).
+    /// Currently DHCPv4 option definitions initialization is not
+    /// implemented thus this function will throw isc::NotImplemented
+    /// if V4 universe is specified.
+    ///
+    /// @param u universe
+    /// @throw isc::Unexpected if internal error occured during option
+    /// definitions creation.
+    /// @throw std::bad_alloc if system went out of memory.
+    /// @throw isc::NotImplemented when V4 universe specified.
+    static void initStdOptionDefs(Option::Universe u);
+
+private:
+
+    /// Initialize standard DHCPv4 option definitions.
+    ///
+    /// The method creates option definitions for all DHCPv4 options.
+    /// Currently this function is not implemented.
+    ///
+    /// @todo implemend this function.
+    ///
+    /// @throw isc::NotImplemeneted
+    static void initStdOptionDefs4();
+
+    /// Initialize standard DHCPv6 option definitions.
+    ///
+    /// The method creates option definitions for all DHCPv6 options.
+    ///
+    /// @throw isc::Unexpected if internal error occured during option
+    /// definitions creation.
+    /// @throw std::bad_alloc if system went out of memory.
+    static void initStdOptionDefs6();
+
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v4factories_;
 
     /// pointers to factories that produce DHCPv6 options
     static FactoryMap v6factories_;
+
+    /// Container with DHCPv4 option definitions.
+    static OptionDefContainer v4option_defs_;
+
+    /// Container with DHCPv6 option definitions.
+    static OptionDefContainer v6option_defs_;
 };
 
 }
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index f562316..87b3216 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -29,6 +29,7 @@ namespace dhcp {
 
 OptionDefinition::DataTypeUtil::DataTypeUtil() {
     data_types_["empty"] = EMPTY_TYPE;
+    data_types_["binary"] = BINARY_TYPE;
     data_types_["boolean"] = BOOLEAN_TYPE;
     data_types_["int8"] = INT8_TYPE;
     data_types_["int16"] = INT16_TYPE;
@@ -97,11 +98,15 @@ OptionDefinition::addRecordField(const DataType data_type) {
 
 Option::Factory*
 OptionDefinition::getFactory() const {
+    validate();
+
     // @todo This function must be extended to return more factory
     // functions that create instances of more specialized options.
     // This requires us to first implement those more specialized
     // options that will be derived from Option class.
-    if (type_ == IPV6_ADDRESS_TYPE && array_type_) {
+    if (type_ == BINARY_TYPE) {
+        return (factoryGeneric);
+    } else if (type_ == IPV6_ADDRESS_TYPE && array_type_) {
         return (factoryAddrList6);
     } else if (type_ == IPV4_ADDRESS_TYPE && array_type_) {
         return (factoryAddrList4);
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index c274ce9..166765f 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -16,13 +16,42 @@
 #define OPTION_DEFINITION_H_
 
 #include <dhcp/option_data_types.h>
-#include <dhcp/option6_int.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/multi_index/mem_fun.hpp>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief Forward declaration to OptionDefinition.
+class OptionDefinition;
+
+/// @brief Pointer to option definition object.
+typedef boost::shared_ptr<OptionDefinition> OptionDefinitionPtr;
+
+/// @brief Forward declaration to Option6Int.
+///
+/// This forward declaration is needed to access Option6Int class
+/// without having to include option6_int.h header. This is because
+/// this header includes libdhcp++.h and this causes circular
+/// inclusion between libdhcp++.h, option_definition.h and
+/// option6_int.h.
+template<typename T>
+class Option6Int;
+
+/// @brief Forward declaration to Option6IntArray.
+///
+/// This forward declaration is needed to access Option6IntArray class
+/// without having to include option6_int_array.h header. This is because
+/// this header includes libdhcp++.h and this causes circular
+/// inclusion between libdhcp++.h, option_definition.h and
+/// option6_int_array.h.
+template<typename T>
+class Option6IntArray;
+
 /// @brief Base class representing a DHCP option definition.
 ///
 /// This is a base class representing a DHCP option definition, which describes
@@ -52,7 +81,7 @@ namespace dhcp {
 ///
 /// Should the option comprise data fields of different types, the "record"
 /// option type is used. In such cases the data field types within the record
-/// are specified using \ref OptionDefinition::addRecordField.
+/// are specified using \ref OptioDefinition::addRecordField.
 ///
 /// When the OptionDefinition object has been sucessfully created, it can be
 /// queried to return the appropriate option factory function for the specified
@@ -84,6 +113,7 @@ public:
     /// Data types of DHCP option fields.
     enum DataType {
         EMPTY_TYPE,
+        BINARY_TYPE,
         BOOLEAN_TYPE,
         INT8_TYPE,
         INT16_TYPE,
@@ -202,7 +232,9 @@ public:
 
     /// @brief Return factory function for the given definition.
     ///
-    /// @return pointer to factory function.
+    /// @throw isc::OutOfRange if \ref validate returns it.
+    /// @throw isc::BadValue if \ref validate returns it.
+    /// @return pointer to a factory function.
     Option::Factory* getFactory() const;
 
     /// @brief Return option name.
@@ -377,6 +409,55 @@ private:
 };
 
 
+/// @brief Multi index container for DHCP option definitions.
+///
+/// This container allows to search for DHCP option definition
+/// using two indexes:
+/// - sequenced: used to access elements in the order they have
+/// been added to the container
+/// - option code: used to search defintions of options
+/// with a specified option code (aka option type).
+/// Note that this container can hold multiple options with the
+/// same code. For this reason, the latter index can be used to
+/// obtain a range of options for a particular option code.
+/// 
+/// @todo: need an index to search options using option space name
+/// once option spaces are implemented.
+typedef boost::multi_index_container<
+    // Container comprises elements of OptionDefinition type.
+    OptionDefinitionPtr,
+    // Here we start enumerating various indexes.
+    boost::multi_index::indexed_by<
+        // Sequenced index allows accessing elements in the same way
+        // as elements in std::list. Sequenced is an index #0.
+        boost::multi_index::sequenced<>,
+        // Start definition of index #1.
+        boost::multi_index::hashed_non_unique<
+            // Use option type as the index key. The type is held
+            // in OptionDefinition object so we have to call
+            // OptionDefinition::getCode to retrieve this key
+            // for each element. The option code is non-unique so
+            // multiple elements with the same option code can
+            // be returned by this index.
+            boost::multi_index::const_mem_fun<
+                OptionDefinition,
+                uint16_t,
+                &OptionDefinition::getCode
+            >
+        >
+    >
+> OptionDefContainer;
+
+/// Type of the index #1 - option type.
+typedef OptionDefContainer::nth_index<1>::type OptionDefContainerTypeIndex;
+/// Pair of iterators to represent the range of options definitions
+///  having the same option type value. The first element in this pair
+///  represents the begining of the range, the second element
+///  represents the end.
+typedef std::pair<OptionDefContainerTypeIndex::const_iterator,
+                  OptionDefContainerTypeIndex::const_iterator> OptionDefContainerTypeRange;
+
+
 } // namespace isc::dhcp
 } // namespace isc
 
diff --git a/src/lib/dhcp/subnet.h b/src/lib/dhcp/subnet.h
index 6f34fc1..00b3375 100644
--- a/src/lib/dhcp/subnet.h
+++ b/src/lib/dhcp/subnet.h
@@ -193,6 +193,11 @@ public:
 
     /// Type of the index #1 - option type.
     typedef OptionContainer::nth_index<1>::type OptionContainerTypeIndex;
+    /// Pair of iterators to represent the range of options having the
+    /// same option type value. The first element in this pair represents
+    /// the begining of the range, the second element represents the end.
+    typedef std::pair<OptionContainerTypeIndex::const_iterator,
+                      OptionContainerTypeIndex::const_iterator> OptionContainerTypeRange;
     /// Type of the index #2 - option persistency flag.
     typedef OptionContainer::nth_index<2>::type OptionContainerPersistIndex;
 
@@ -245,7 +250,7 @@ public:
     /// @return reference to collection of options configured for a subnet.
     /// The returned reference is valid as long as the Subnet object which
     /// returned it still exists.
-    const OptionContainer& getOptions() {
+    const OptionContainer& getOptions() const {
         return (options_);
     }
 
diff --git a/src/lib/dhcp/tests/alloc_engine_unittest.cc b/src/lib/dhcp/tests/alloc_engine_unittest.cc
index 9e5fbe7..91e08d8 100644
--- a/src/lib/dhcp/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcp/tests/alloc_engine_unittest.cc
@@ -20,6 +20,7 @@
 #include <dhcp/cfgmgr.h>
 #include <dhcp/memfile_lease_mgr.h>
 #include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
 #include <iostream>
 #include <sstream>
 #include <map>
@@ -30,7 +31,6 @@ using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp::test; // Memfile_LeaseMgr
-using namespace boost;
 
 namespace {
 
@@ -96,7 +96,6 @@ public:
 // This test checks if the Allocation Engine can be instantiated and that it
 // parses parameters string properly.
 TEST_F(AllocEngineTest, constructor) {
-
     AllocEngine* x = NULL;
 
     // Hashed and random allocators are not supported yet
@@ -131,9 +130,8 @@ detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
 
 // This test checks if the simple allocation can succeed
 TEST_F(AllocEngineTest, simpleAlloc) {
-
-    AllocEngine* engine = NULL;
-    ASSERT_NO_THROW(engine = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
@@ -151,15 +149,12 @@ TEST_F(AllocEngineTest, simpleAlloc) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease6(lease, from_mgr);
-
-    delete engine;
 }
 
 // This test checks if the fake allocation (for SOLICIT) can succeed
 TEST_F(AllocEngineTest, fakeAlloc) {
-
-    AllocEngine* engine = NULL;
-    ASSERT_NO_THROW(engine = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
@@ -179,9 +174,8 @@ TEST_F(AllocEngineTest, fakeAlloc) {
 // This test checks if the allocation with a hint that is valid (in range,
 // in pool and free) can succeed
 TEST_F(AllocEngineTest, allocWithValidHint) {
-
-    AllocEngine* engine = NULL;
-    ASSERT_NO_THROW(engine = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
     Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_,
@@ -203,16 +197,13 @@ TEST_F(AllocEngineTest, allocWithValidHint) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease6(lease, from_mgr);
-
-    delete engine;
 }
 
 // This test checks if the allocation with a hint that is in range,
 // in pool, but is currently used) can succeed
 TEST_F(AllocEngineTest, allocWithUsedHint) {
-
-    AllocEngine* engine = NULL;
-    ASSERT_NO_THROW(engine = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
     // let's create a lease and put it in the LeaseMgr
@@ -245,16 +236,13 @@ TEST_F(AllocEngineTest, allocWithUsedHint) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease6(lease, from_mgr);
-
-    delete engine;
 }
 
 // This test checks if the allocation with a hint that is out the blue
 // can succeed. The invalid hint should be ignored completely.
 TEST_F(AllocEngineTest, allocBogusHint) {
-
-    AllocEngine* engine = NULL;
-    ASSERT_NO_THROW(engine = new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
     ASSERT_TRUE(engine);
 
     // Client would like to get a 3000::abc lease, which does not belong to any
@@ -278,8 +266,6 @@ TEST_F(AllocEngineTest, allocBogusHint) {
 
     // Now check that the lease in LeaseMgr has the same parameters
     detailCompareLease6(lease, from_mgr);
-
-    delete engine;
 }
 
 // This test verifies that the allocator picks addresses that belong to the
diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc
index fc330ea..5cc6345 100644
--- a/src/lib/dhcp/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc
@@ -75,6 +75,7 @@ TEST_F(CfgMgrTest, subnet4) {
 
 // This test verifies if the configuration manager is able to hold and return
 // valid leases
+
 TEST_F(CfgMgrTest, subnet6) {
     CfgMgr& cfg_mgr = CfgMgr::instance();
 
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index d17eb65..0c9dc89 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -21,6 +21,11 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/libdhcp++.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_int.h>
+#include <dhcp/option6_int_array.h>
+#include <dhcp/option6_addrlst.h>
 #include "config.h"
 
 using namespace std;
@@ -46,6 +51,57 @@ public:
         Option* option = new Option(u, type, buf);
         return OptionPtr(option);
     }
+
+    /// @brief Test option option definition.
+    ///
+    /// This function tests if option definition for standard
+    /// option has been initialized correctly.
+    ///
+    /// @param code option code.
+    /// @param bug buffer to be used to create option instance.
+    /// @param expected_type type of the option created by the
+    /// factory function returned by the option definition.
+    static void testInitOptionDefs6(const uint16_t code,
+                             const OptionBuffer& buf,
+                             const std::type_info& expected_type) {
+        // Initialize stdandard options definitions. They are held
+        // in the static container throughout the program.
+        LibDHCP::initStdOptionDefs(Option::V6);
+        // Get all option definitions, we will use them to extract
+        // the definition for a particular option code.
+        OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
+        // Get the container index #1. This one allows for searching
+        // option definitions using option code.
+        const OptionDefContainerTypeIndex& idx = options.get<1>();
+        // Get 'all' option definitions for a particular option code.
+        // For standard options we expect that the range returned
+        // will contain single option as their codes are unique.
+        OptionDefContainerTypeRange range = idx.equal_range(code);
+        ASSERT_EQ(1, std::distance(range.first, range.second));
+        // If we have single option definition returned, the
+        // first iterator holds it.
+        OptionDefinitionPtr def = *(range.first);
+        // It should not happen that option definition is NULL but
+        // let's make sure (test should take things like that into
+        // account).
+        ASSERT_TRUE(def);
+        // Check that option definition is valid.
+        ASSERT_NO_THROW(def->validate());
+        // Get the factory function for the particular option
+        // definition. We will use this factory function to
+        // create option instance.
+        Option::Factory* factory = NULL;
+        ASSERT_NO_THROW(factory = def->getFactory());
+        OptionPtr option;
+        // Create the option.
+        ASSERT_NO_THROW(option = factory(Option::V6, code, buf));
+        // Make sure it is not NULL.
+        ASSERT_TRUE(option);
+        // And the actual object type is the one that we expect.
+        // Note that for many options there are dedicated classes
+        // derived from Option class to represent them.
+        EXPECT_TRUE(typeid(*option) == expected_type);
+    }
 };
 
 static const uint8_t packed[] = {
@@ -311,4 +367,30 @@ TEST(LibDhcpTest, unpackOptions4) {
     EXPECT_TRUE(x == options.end()); // option 2 not found
 }
 
+// Test that definitions of standard options have been initialized
+// correctly.
+// @todo Only limited number of option definitions are now created
+// This test have to be extended once all option definitions are
+// created.
+TEST(LibDhcpTest, initStdOptionDefs) {
+    LibDhcpTest::testInitOptionDefs6(D6O_CLIENTID, OptionBuffer(14, 1),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_SERVERID, OptionBuffer(14, 1),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_IA_NA, OptionBuffer(12, 1),
+                                     typeid(Option6IA));
+    LibDhcpTest::testInitOptionDefs6(D6O_IAADDR, OptionBuffer(24, 1),
+                                     typeid(Option6IAAddr));
+    LibDhcpTest::testInitOptionDefs6(D6O_ORO, OptionBuffer(10, 1),
+                                     typeid(Option6IntArray<uint16_t>));
+    LibDhcpTest::testInitOptionDefs6(D6O_ELAPSED_TIME, OptionBuffer(2, 1),
+                                     typeid(Option6Int<uint16_t>));
+    LibDhcpTest::testInitOptionDefs6(D6O_STATUS_CODE, OptionBuffer(10, 1),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_RAPID_COMMIT, OptionBuffer(),
+                                     typeid(Option));
+    LibDhcpTest::testInitOptionDefs6(D6O_NAME_SERVERS, OptionBuffer(32, 1),
+                                     typeid(Option6AddrLst));
+}
+
 }
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 4b83517..7e92680 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -281,6 +281,56 @@ TEST_F(OptionDefinitionTest, factoryEmpty) {
     EXPECT_THROW(factory(Option::V6, D6O_RAPID_COMMIT,OptionBuffer(2)),isc::BadValue);
 }
 
+TEST_F(OptionDefinitionTest, factoryBinary) {
+    // Binary option is the one that is represented by the generic
+    // Option class. In fact all options can be represented by this
+    // class but for some of them it is just natural. The SERVERID
+    // option consists of the option code, length and binary data so
+    // this one was picked for this test.
+    OptionDefinition opt_def("OPTION_SERVERID", D6O_SERVERID, "binary");
+    Option::Factory* factory(NULL);
+    EXPECT_NO_THROW(factory = opt_def.getFactory());
+    ASSERT_TRUE(factory != NULL);
+
+    // Prepare some dummy data (serverid): 0, 1, 2 etc.
+    OptionBuffer buf(14);
+    for (int i = 0; i < 14; ++i) {
+        buf[i] = i;
+    }
+    // Create option instance with the factory function.
+    // If the OptionDefinition code works properly than
+    // object of the type Option should be returned.
+    OptionPtr option_v6;
+    ASSERT_NO_THROW(
+        option_v6 = factory(Option::V6, D6O_SERVERID, buf);
+    );
+    // Expect base option type returned.
+    ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
+    // Sanity check on universe, length and size. These are
+    // the basic parameters identifying any option.
+    EXPECT_EQ(Option::V6, option_v6->getUniverse());
+    EXPECT_EQ(4, option_v6->getHeaderLen());
+    ASSERT_EQ(buf.size(), option_v6->getData().size());
+
+    // Get the server id data from the option and compare
+    // against reference buffer. They are expected to match.
+    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+                           option_v6->getData().end(),
+                           buf.begin()));
+
+    // Repeat the same test scenario for DHCPv4 option.
+    OptionPtr option_v4;
+    ASSERT_NO_THROW(option_v4 = factory(Option::V4, 214, buf));
+    // Expect 'empty' DHCPv4 option.
+    EXPECT_EQ(Option::V4, option_v4->getUniverse());
+    EXPECT_EQ(2, option_v4->getHeaderLen());
+    ASSERT_EQ(buf.size(), option_v4->getData().size());
+
+    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
+                           option_v6->getData().end(),
+                           buf.begin()));
+}
+
 TEST_F(OptionDefinitionTest, factoryIA6) {
     // This option consists of IAID, T1 and T2 fields (each 4 bytes long).
     const int option6_ia_len = 12;
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 977854d..e81ef76 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -93,8 +93,10 @@ libb10_dns___la_LDFLAGS = -no-undefined -version-info 2:0:0
 libb10_dns___la_SOURCES =
 libb10_dns___la_SOURCES += edns.h edns.cc
 libb10_dns___la_SOURCES += exceptions.h exceptions.cc
+libb10_dns___la_SOURCES += master_lexer_inputsource.h master_lexer_inputsource.cc
 libb10_dns___la_SOURCES += labelsequence.h labelsequence.cc
 libb10_dns___la_SOURCES += masterload.h masterload.cc
+libb10_dns___la_SOURCES += master_lexer.h master_lexer.cc
 libb10_dns___la_SOURCES += message.h message.cc
 libb10_dns___la_SOURCES += messagerenderer.h messagerenderer.cc
 libb10_dns___la_SOURCES += name.h name.cc
@@ -145,6 +147,8 @@ libdns___include_HEADERS = \
 	exceptions.h \
 	labelsequence.h \
 	message.h \
+	master_lexer.h \
+	masterload.h \
 	messagerenderer.h \
 	name.h \
 	question.h \
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
index abf3192..f5b3e38 100644
--- a/src/lib/dns/benchmarks/message_renderer_bench.cc
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -145,7 +145,6 @@ main(int argc, char* argv[]) {
         }
     }
     argc -= optind;
-    argv += optind;
     if (argc != 0) {
         usage();
     }
diff --git a/src/lib/dns/master_lexer.cc b/src/lib/dns/master_lexer.cc
new file mode 100644
index 0000000..2a5c886
--- /dev/null
+++ b/src/lib/dns/master_lexer.cc
@@ -0,0 +1,47 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/master_lexer.h>
+
+#include <cassert>
+#include <string>
+
+namespace {
+const char* const error_text[] = {
+    "lexer not started",        // NOT_STARTED
+    "unbalanced parentheses",   // UNBALANCED_PAREN
+    "unexpected end of input",  // UNEXPECTED_END
+    "unbalanced quotes"         // UNBALANCED_QUOTES
+};
+const size_t error_text_max_count = sizeof(error_text) / sizeof(error_text[0]);
+}
+
+namespace isc {
+namespace dns {
+
+std::string
+MasterLexer::Token::getErrorText() const {
+    if (type_ != ERROR) {
+        isc_throw(InvalidOperation,
+                  "Token::getErrorText() for non error type");
+    }
+
+    // The class integrity ensures the following:
+    assert(val_.error_code_ < error_text_max_count);
+    return (error_text[val_.error_code_]);
+}
+
+
+} // end of namespace dns
+} // end of namespace isc
diff --git a/src/lib/dns/master_lexer.h b/src/lib/dns/master_lexer.h
new file mode 100644
index 0000000..bd86a04
--- /dev/null
+++ b/src/lib/dns/master_lexer.h
@@ -0,0 +1,241 @@
+// 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 MASTER_LEXER_H
+#define MASTER_LEXER_H 1
+
+#include <exceptions/exceptions.h>
+
+#include <string>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dns {
+
+class MasterLexer {
+public:
+    class Token;       // we define it separately for better readability
+};
+
+/// \brief Tokens for \c MasterLexer
+///
+/// This is a simple value-class encapsulating a type of a lexer token and
+/// (if it has a value) its value.  Essentially, the class provides
+/// constructors corresponding to different types of tokens, and corresponding
+/// getter methods.  The type and value are fixed at the time of construction
+/// and will never be modified throughout the lifetime of the object.
+/// The getter methods are still provided to maximize the safety; an
+/// application cannot refer to a value that is invalid for the type of token.
+///
+/// This class is intentionally implemented as copyable and assignable
+/// (using the default version of copy constructor and assignment operator),
+/// but it's mainly for internal implementation convenience.  Applications will
+/// simply refer to Token object as a reference via the \c MasterLexer class.
+class MasterLexer::Token {
+public:
+    /// \brief Enumeration for token types
+    ///
+    /// \note At the time of initial implementation, all numeric tokens
+    /// that would be extracted from \c MasterLexer should be represented
+    /// as an unsigned 32-bit integer.  If we see the need for larger integers
+    /// or negative numbers, we can then extend the token types.
+    enum Type {
+        END_OF_LINE, ///< End of line detected (if asked for detecting it)
+        END_OF_FILE, ///< End of file detected (if asked for detecting it)
+        INITIAL_WS,  ///< White spaces at the beginning of a line
+        NOVALUE_TYPE_MAX = INITIAL_WS, ///< Max integer corresponding to
+                                       /// no-value (type only) types.
+                                       /// Mainly for internal use.
+        STRING, ///< A single string
+        QSTRING, ///< A single string quoted by double-quotes (").
+        NUMBER,  ///< A decimal number (unsigned 32-bit)
+        ERROR    ///< Error detected in getting a token
+    };
+
+    /// \brief Enumeration for lexer error codes
+    enum ErrorCode {
+        NOT_STARTED, ///< The lexer is just initialized and has no token
+        UNBALANCED_PAREN,       ///< Unbalanced parentheses detected
+        UNEXPECTED_END, ///< The lexer reaches the end of line or file
+                       /// unexpectedly
+        UNBALANCED_QUOTES,      ///< Unbalanced quotations detected
+        MAX_ERROR_CODE ///< Max integer corresponding to valid error codes.
+                       /// (excluding this one). Mainly for internal use.
+    };
+
+    /// \brief A simple representation of a range of a string.
+    ///
+    /// This is a straightforward pair of the start pointer of a string
+    /// and its length.  The \c STRING and \c QSTRING types of tokens
+    /// will be primarily represented in this form.
+    ///
+    /// Any character can be stored in the valid range of the region.
+    /// In particular, there can be a nul character (\0) in the middle of
+    /// the region.  On the other hand, it is not ensured that the string
+    /// is nul-terminated.  So the usual string manipulation API may not work
+    /// as expected.
+    struct StringRegion {
+        const char* beg;        ///< The start address of the string
+        size_t len;             ///< The length of the string in bytes
+    };
+
+    /// \brief Constructor for non-value type of token.
+    ///
+    /// \throw InvalidParameter A value type token is specified.
+    /// \param type The type of the token.  It must indicate a non-value
+    /// type (not larger than \c NOVALUE_TYPE_MAX).
+    explicit Token(Type type) : type_(type) {
+        if (type > NOVALUE_TYPE_MAX) {
+            isc_throw(InvalidParameter, "Token per-type constructor "
+                      "called with invalid type: " << type);
+        }
+    }
+
+    /// \brief Constructor for string and quoted-string types of token.
+    ///
+    /// The optional \c quoted parameter specifies whether it's a quoted or
+    /// non quoted string.
+    ///
+    /// The string is specified as a pair of a pointer to the start address
+    /// and its length.  Any character can be contained in any position of
+    /// the valid range (see \c StringRegion).
+    ///
+    /// When it's a quoted string, the quotation marks must be excluded
+    /// from the specified range.
+    ///
+    /// \param str_beg The start address of the string
+    /// \param str_len The size of the string in bytes
+    /// \param quoted true if it's a quoted string; false otherwise.
+    Token(const char* str_beg, size_t str_len, bool quoted = false) :
+        type_(quoted ? QSTRING : STRING)
+    {
+        val_.str_region_.beg = str_beg;
+        val_.str_region_.len = str_len;
+    }
+
+    /// \brief Constructor for number type of token.
+    ///
+    /// \brief number An unsigned 32-bit integer corresponding to the token
+    /// value.
+    explicit Token(uint32_t number) : type_(NUMBER) {
+        val_.number_ = number;
+    }
+
+    /// \brief Constructor for error type of token.
+    ///
+    /// \throw InvalidParameter Invalid error code value is specified.
+    /// \brief error_code A pre-defined constant of \c ErrorCode.
+    explicit Token(ErrorCode error_code) : type_(ERROR) {
+        if (!(error_code < MAX_ERROR_CODE)) {
+            isc_throw(InvalidParameter, "Invalid master lexer error code: "
+                      << error_code);
+        }
+        val_.error_code_ = error_code;
+    }
+
+    /// \brief Return the token type.
+    ///
+    /// \throw none
+    Type getType() const { return (type_); }
+
+    /// \brief Return the value of a string-variant token.
+    ///
+    /// \throw InvalidOperation Called on a non string-variant types of token.
+    /// \return A reference to \c StringRegion corresponding to the string
+    ///         token value.
+    const StringRegion& getStringRegion() const {
+        if (type_ != STRING && type_ != QSTRING) {
+            isc_throw(InvalidOperation,
+                      "Token::getStringRegion() for non string-variant type");
+        }
+        return (val_.str_region_);
+    }
+
+    /// \brief Return the value of a string-variant token as a string object.
+    ///
+    /// Note that the underlying string may contain a nul (\0) character
+    /// in the middle.  The returned string object will contain all characters
+    /// of the valid range of the underlying string.  So some string
+    /// operations such as c_str() may not work as expected.
+    ///
+    /// \throw InvalidOperation Called on a non string-variant types of token.
+    /// \throw std::bad_alloc Resource allocation failure in constructing the
+    ///                       string object.
+    /// \return A std::string object corresponding to the string token value.
+    std::string getString() const {
+        if (type_ != STRING && type_ != QSTRING) {
+            isc_throw(InvalidOperation,
+                      "Token::getString() for non string-variant type");
+        }
+        return (std::string(val_.str_region_.beg,
+                            val_.str_region_.beg + val_.str_region_.len));
+    }
+
+    /// \brief Return the value of a string-variant token as a string object.
+    ///
+    /// \throw InvalidOperation Called on a non number type of token.
+    /// \return The integer corresponding to the number token value.
+    uint32_t getNumber() const {
+        if (type_ != NUMBER) {
+            isc_throw(InvalidOperation,
+                      "Token::getNumber() for non number type");
+        }
+        return (val_.number_);
+    }
+
+    /// \brief Return the error code of a error type token.
+    ///
+    /// \throw InvalidOperation Called on a non error type of token.
+    /// \return The error code of the token.
+    ErrorCode getErrorCode() const {
+        if (type_ != ERROR) {
+            isc_throw(InvalidOperation,
+                      "Token::getErrorCode() for non error type");
+        }
+        return (val_.error_code_);
+    };
+
+    /// \brief Return a textual description of the error of a error type token.
+    ///
+    /// The returned string would be useful to produce a log message when
+    /// a zone file parser encounters an error.
+    ///
+    /// \throw InvalidOperation Called on a non error type of token.
+    /// \throw std::bad_alloc Resource allocation failure in constructing the
+    ///                       string object.
+    /// \return A string object that describes the meaning of the error.
+    std::string getErrorText() const;
+
+private:
+    Type type_;    // this is not const so the class can be assignable
+
+    // We use a union to represent different types of token values via the
+    // unified Token class.  The class integrity should ensure valid operation
+    // on the union; getter methods should only refer to the member set at
+    // the construction.
+    union {
+        StringRegion str_region_;
+        uint32_t number_;
+        ErrorCode error_code_;
+    } val_;
+};
+
+} // namespace dns
+} // namespace isc
+#endif  // MASTER_LEXER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/master_lexer_inputsource.cc b/src/lib/dns/master_lexer_inputsource.cc
new file mode 100644
index 0000000..e6d9cec
--- /dev/null
+++ b/src/lib/dns/master_lexer_inputsource.cc
@@ -0,0 +1,154 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/master_lexer_inputsource.h>
+
+#include <cerrno>
+#include <cstring>
+
+namespace isc {
+namespace dns {
+namespace master_lexer_internal {
+
+namespace { // unnamed namespace
+
+std::string
+createStreamName(const std::istream& input_stream) {
+     std::stringstream ss;
+     ss << "stream-" << &input_stream;
+     return (ss.str());
+}
+
+} // end of unnamed namespace
+
+InputSource::InputSource(std::istream& input_stream) :
+    at_eof_(false),
+    line_(1),
+    saved_line_(line_),
+    buffer_pos_(0),
+    name_(createStreamName(input_stream)),
+    input_(input_stream)
+{}
+
+InputSource::InputSource(const char* filename) :
+    at_eof_(false),
+    line_(1),
+    saved_line_(line_),
+    buffer_pos_(0),
+    name_(filename),
+    input_(file_stream_)
+{
+    errno = 0;
+    file_stream_.open(filename);
+    if (file_stream_.fail()) {
+        std::string error_txt("Error opening the input source file: ");
+        error_txt += filename;
+        if (errno != 0) {
+            error_txt += "; possible cause: ";
+            error_txt += std::strerror(errno);
+        }
+        isc_throw(OpenError, error_txt);
+    }
+}
+
+InputSource::~InputSource()
+{
+    if (file_stream_.is_open()) {
+        file_stream_.close();
+    }
+}
+
+int
+InputSource::getChar() {
+    if (buffer_pos_ == buffer_.size()) {
+        // We may have reached EOF at the last call to
+        // getChar(). at_eof_ will be set then. We then simply return
+        // early.
+        if (at_eof_) {
+            return (END_OF_STREAM);
+        }
+        // We are not yet at EOF. Read from the stream.
+        const int c = input_.get();
+        // Have we reached EOF now? If so, set at_eof_ and return early,
+        // but don't modify buffer_pos_ (which should still be equal to
+        // the size of buffer_).
+        if (input_.eof()) {
+            at_eof_ = true;
+            return (END_OF_STREAM);
+        }
+        // This has to come after the .eof() check as some
+        // implementations seem to check the eofbit also in .fail().
+        if (input_.fail()) {
+            isc_throw(ReadError,
+                      "Error reading from the input stream: " << getName());
+        }
+        buffer_.push_back(c);
+    }
+
+    const int c = buffer_[buffer_pos_];
+    ++buffer_pos_;
+    if (c == '\n') {
+        ++line_;
+    }
+
+    return (c);
+}
+
+void
+InputSource::ungetChar() {
+    if (at_eof_) {
+        at_eof_ = false;
+    } else if (buffer_pos_ == 0) {
+        isc_throw(UngetBeforeBeginning,
+                  "Cannot skip before the start of buffer");
+    } else {
+        --buffer_pos_;
+        if (buffer_[buffer_pos_] == '\n') {
+            --line_;
+        }
+    }
+}
+
+void
+InputSource::ungetAll() {
+    buffer_pos_ = 0;
+    line_ = saved_line_;
+    at_eof_ = false;
+}
+
+void
+InputSource::saveLine() {
+    saved_line_ = line_;
+}
+
+void
+InputSource::compact() {
+    if (buffer_pos_ == buffer_.size()) {
+        buffer_.clear();
+    } else {
+        buffer_.erase(buffer_.begin(), buffer_.begin() + buffer_pos_);
+    }
+
+    buffer_pos_ = 0;
+}
+
+void
+InputSource::mark() {
+    saveLine();
+    compact();
+}
+
+} // namespace master_lexer_internal
+} // namespace dns
+} // namespace isc
diff --git a/src/lib/dns/master_lexer_inputsource.h b/src/lib/dns/master_lexer_inputsource.h
new file mode 100644
index 0000000..33659d1
--- /dev/null
+++ b/src/lib/dns/master_lexer_inputsource.h
@@ -0,0 +1,164 @@
+// 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 DNS_INPUTSOURCE_H
+#define DNS_INPUTSOURCE_H 1
+
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dns {
+namespace master_lexer_internal {
+
+/// \brief An input source that is used internally by MasterLexer.
+///
+/// This is a helper internal class for MasterLexer, and represents
+/// state of a single source of the entire zone data to be
+/// parsed. Normally this means the master zone file, but MasterLexer
+/// can have multiple InputSources if $INCLUDE is used. The source can
+/// also be generic input stream (std::istream).
+///
+/// This class is not meant for public use. We also enforce that
+/// instances are non-copyable.
+class InputSource : boost::noncopyable {
+public:
+    /// \brief Returned by getChar() when end of stream is reached.
+    static const int END_OF_STREAM;
+
+    /// \brief Exception thrown when ungetChar() is made to go before
+    /// the start of buffer.
+    struct UngetBeforeBeginning : public OutOfRange {
+        UngetBeforeBeginning(const char* file, size_t line, const char* what) :
+            OutOfRange(file, line, what)
+        {}
+    };
+
+    /// \brief Exception thrown when we fail to read from the input
+    /// stream or file.
+    struct ReadError : public Unexpected {
+        ReadError(const char* file, size_t line, const char* what) :
+            Unexpected(file, line, what)
+        {}
+    };
+
+    /// \brief Exception thrown when we fail to open the input file.
+    struct OpenError : public Unexpected {
+        OpenError(const char* file, size_t line, const char* what) :
+            Unexpected(file, line, what)
+        {}
+    };
+
+    /// \brief Constructor which takes an input stream. The stream is
+    /// read-from, but it is not closed.
+    explicit InputSource(std::istream& input_stream);
+
+    /// \brief Constructor which takes a filename to read from. The
+    /// associated file stream is managed internally.
+    ///
+    /// \throws OpenError when opening the input file fails.
+    explicit InputSource(const char* filename);
+
+    /// \brief Destructor
+    ~InputSource();
+
+    /// \brief Returns a name for the InputSource. Typically this is the
+    /// filename, but if the InputSource was constructed for an
+    /// \c std::istream, it returns a name in the format "stream-%p".
+    const std::string& getName() const {
+        return (name_);
+    }
+
+    /// \brief Returns if the input source is at end of file.
+    bool atEOF() const {
+        return (at_eof_);
+    }
+
+    /// \brief Returns the current line number being read.
+    size_t getCurrentLine() const {
+        return (line_);
+    }
+
+    /// \brief Saves the current line being read. Later, when
+    /// \c ungetAll() is called, it skips back to the last-saved line.
+    ///
+    /// TODO: Please make this method private if it is unused after the
+    /// MasterLexer implementation is complete (and only \c mark() is
+    /// used instead).
+    void saveLine();
+
+    /// Removes buffered content before the current location in the
+    /// \c InputSource. It's not possible to \c ungetChar() after this,
+    /// unless we read more data using \c getChar().
+    ///
+    /// TODO: Please make this method private if it is unused after the
+    /// MasterLexer implementation is complete (and only \c mark() is
+    /// used instead).
+    void compact();
+
+    /// Calls \c saveLine() and \c compact() in sequence.
+    void mark();
+
+    /// \brief Returns a single character from the input source. If end
+    /// of file is reached, \c END_OF_STREAM is returned.
+    ///
+    /// \throws ReadError when reading from the input stream or file
+    /// fails.
+    int getChar();
+
+    /// \brief Skips backward a single character in the input
+    /// source. The last-read character is unget.
+    ///
+    /// \throws UngetBeforeBeginning if we go backwards past the start
+    /// of reading, or backwards past the last time compact() was
+    /// called.
+    void ungetChar();
+
+    /// Forgets what was read, and skips back to the position where
+    /// \c compact() was last called. If \c compact() was not called, it
+    /// skips back to where reading started. If \c saveLine() was called
+    /// previously, it sets the current line number to the line number
+    /// saved.
+    void ungetAll();
+
+private:
+    bool at_eof_;
+    size_t line_;
+    size_t saved_line_;
+
+    std::vector<char> buffer_;
+    size_t buffer_pos_;
+
+    const std::string name_;
+    std::ifstream file_stream_;
+    std::istream& input_;
+};
+
+const int InputSource::END_OF_STREAM = -1;
+
+} // namespace master_lexer_internal
+} // namespace dns
+} // namespace isc
+
+#endif  // DNS_INPUTSOURCE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index 5ea339d..079033a 100644
--- a/src/lib/dns/name.cc
+++ b/src/lib/dns/name.cc
@@ -123,15 +123,18 @@ typedef enum {
     ft_ordinary,                // parsing an ordinary label
     ft_initialescape,           // just found '\'
     ft_escape,                  // begin of handling a '\'-escaped sequence
-    ft_escdecimal,              // parsing a '\DDD' octet.
-
-    // Unused at this moment.  We'll revisit this when we support master file
-    // parser where @ is used to mean an origin name.
-    ft_at
+    ft_escdecimal               // parsing a '\DDD' octet.
 } ft_state;
-}
 
-Name::Name(const std::string &namestring, bool downcase) {
+// The parser of name from a string. It is a template, because
+// some parameters are used with two different types, while others
+// are private type aliases.
+template<class Iterator, class Offsets, class Data>
+void
+stringParse(Iterator s, Iterator send, bool downcase, Offsets& offsets,
+            Data& ndata)
+{
+    const Iterator orig_s(s);
     //
     // Initialize things to make the compiler happy; they're not required.
     //
@@ -142,17 +145,14 @@ Name::Name(const std::string &namestring, bool downcase) {
     //
     // Set up the state machine.
     //
-    std::string::const_iterator s = namestring.begin();
-    std::string::const_iterator send = namestring.end();
     bool done = false;
     bool is_root = false;
+    const bool empty = s == send;
     ft_state state = ft_init;
 
-    NameOffsets offsets;
+    // Prepare the output buffers.
     offsets.reserve(Name::MAX_LABELS);
     offsets.push_back(0);
-
-    NameString ndata;
     ndata.reserve(Name::MAX_WIRE);
 
     // should we refactor this code using, e.g, the state pattern?  Probably
@@ -171,7 +171,8 @@ Name::Name(const std::string &namestring, bool downcase) {
             if (c == '.') {
                 if (s != send) {
                     isc_throw(EmptyLabel,
-                              "non terminating empty label in " << namestring);
+                              "non terminating empty label in " <<
+                              string(orig_s, send));
                 }
                 is_root = true;
             } else if (c == '@' && s == send) {
@@ -200,7 +201,7 @@ Name::Name(const std::string &namestring, bool downcase) {
             if (c == '.') {
                 if (count == 0) {
                     isc_throw(EmptyLabel,
-                              "duplicate period in " << namestring);
+                              "duplicate period in " << string(orig_s, send));
                 }
                 ndata.at(offsets.back()) = count;
                 offsets.push_back(ndata.size());
@@ -212,9 +213,9 @@ Name::Name(const std::string &namestring, bool downcase) {
             } else if (c == '\\') {
                 state = ft_escape;
             } else {
-                if (++count > MAX_LABELLEN) {
+                if (++count > Name::MAX_LABELLEN) {
                     isc_throw(TooLongLabel,
-                              "label is too long in " << namestring);
+                              "label is too long in " << string(orig_s, send));
                 }
                 ndata.push_back(downcase ? maptolower[c] : c);
             }
@@ -224,15 +225,15 @@ Name::Name(const std::string &namestring, bool downcase) {
                 // This looks like a bitstring label, which was deprecated.
                 // Intentionally drop it.
                 isc_throw(BadLabelType,
-                          "invalid label type in " << namestring);
+                          "invalid label type in " << string(orig_s, send));
             }
             state = ft_escape;
             // FALLTHROUGH
         case ft_escape:
             if (!isdigit(c & 0xff)) {
-                if (++count > MAX_LABELLEN) {
+                if (++count > Name::MAX_LABELLEN) {
                     isc_throw(TooLongLabel,
-                              "label is too long in " << namestring);
+                              "label is too long in " << string(orig_s, send));
                 }
                 ndata.push_back(downcase ? maptolower[c] : c);
                 state = ft_ordinary;
@@ -246,7 +247,7 @@ Name::Name(const std::string &namestring, bool downcase) {
             if (!isdigit(c & 0xff)) {
                 isc_throw(BadEscape,
                           "mixture of escaped digit and non-digit in "
-                          << namestring);
+                          << string(orig_s, send));
             }
             value *= 10;
             value += digitvalue[c];
@@ -255,11 +256,11 @@ Name::Name(const std::string &namestring, bool downcase) {
                 if (value > 255) {
                     isc_throw(BadEscape,
                               "escaped decimal is too large in "
-                              << namestring);
+                              << string(orig_s, send));
                 }
-                if (++count > MAX_LABELLEN) {
+                if (++count > Name::MAX_LABELLEN) {
                     isc_throw(TooLongLabel,
-                              "label is too long in " << namestring);
+                              "label is too long in " << string(orig_s, send));
                 }
                 ndata.push_back(downcase ? maptolower[value] : value);
                 state = ft_ordinary;
@@ -274,13 +275,14 @@ Name::Name(const std::string &namestring, bool downcase) {
     if (!done) {                // no trailing '.' was found.
         if (ndata.size() == Name::MAX_WIRE) {
             isc_throw(TooLongName,
-                      "name is too long for termination in " << namestring);
+                      "name is too long for termination in " <<
+                      string(orig_s, send));
         }
         assert(s == send);
-        if (state != ft_ordinary && state != ft_at) {
+        if (state != ft_ordinary) {
             isc_throw(IncompleteName,
                       "incomplete textual name in " <<
-                      (namestring.empty() ? "<empty>" : namestring));
+                      (empty ? "<empty>" : string(orig_s, send)));
         }
         if (state == ft_ordinary) {
             assert(count != 0);
@@ -291,12 +293,93 @@ Name::Name(const std::string &namestring, bool downcase) {
             ndata.push_back('\0');
         }
     }
+}
+
+}
+
+Name::Name(const std::string &namestring, bool downcase) {
+    // Prepare inputs for the parser
+    const std::string::const_iterator s = namestring.begin();
+    const std::string::const_iterator send = namestring.end();
+
+    // Prepare outputs
+    NameOffsets offsets;
+    NameString ndata;
+
+    // To the parsing
+    stringParse(s, send, downcase, offsets, ndata);
+
+    // And get the output
+    labelcount_ = offsets.size();
+    assert(labelcount_ > 0 && labelcount_ <= Name::MAX_LABELS);
+    ndata_.assign(ndata.data(), ndata.size());
+    length_ = ndata_.size();
+    offsets_.assign(offsets.begin(), offsets.end());
+}
+
+Name::Name(const char* namedata, size_t data_len, const Name* origin,
+           bool downcase)
+{
+    // Check validity of data
+    if (namedata == NULL || data_len == 0) {
+        isc_throw(isc::InvalidParameter,
+                  "No data provided to Name constructor");
+    }
+    // If the last character is not a dot, it is a relative to origin.
+    // It is safe to check now, we know there's at least one character.
+    const bool absolute = (namedata[data_len - 1] == '.');
+    // If we are not absolute, we need the origin to complete the name.
+    if (!absolute && origin == NULL) {
+        isc_throw(MissingNameOrigin,
+                  "No origin available and name is relative");
+    }
+    // Prepare inputs for the parser
+    const char* end = namedata + data_len;
+
+    // Prepare outputs
+    NameOffsets offsets;
+    NameString ndata;
+
+    // Do the actual parsing
+    stringParse(namedata, end, downcase, offsets, ndata);
 
+    // Get the output
     labelcount_ = offsets.size();
     assert(labelcount_ > 0 && labelcount_ <= Name::MAX_LABELS);
     ndata_.assign(ndata.data(), ndata.size());
     length_ = ndata_.size();
     offsets_.assign(offsets.begin(), offsets.end());
+
+    if (!absolute) {
+        // Now, extend the data with the ones from origin. But eat the
+        // last label (the empty one).
+
+        // Drop the last character of the data (the \0) and append a copy of
+        // the origin's data
+        ndata_.erase(ndata_.end() - 1);
+        ndata_.append(origin->ndata_);
+
+        // Do a similar thing with offsets. However, we need to move them
+        // so they point after the prefix we parsed before.
+        size_t offset = offsets_.back();
+        offsets_.pop_back();
+        size_t offset_count = offsets_.size();
+        offsets_.insert(offsets_.end(), origin->offsets_.begin(),
+                        origin->offsets_.end());
+        for (NameOffsets::iterator it(offsets_.begin() + offset_count);
+             it != offsets_.end(); ++it) {
+            *it += offset;
+        }
+
+        // Adjust sizes.
+        length_ = ndata_.size();
+        labelcount_ = offsets_.size();
+
+        // And check the sizes are OK.
+        if (labelcount_ > Name::MAX_LABELS || length_ > Name::MAX_WIRE) {
+            isc_throw(TooLongName, "Combined name is too long");
+        }
+    }
 }
 
 namespace {
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index a3e3674..02c868f 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -105,6 +105,17 @@ public:
         NameParserException(file, line, what) {}
 };
 
+/// \brief Thrown when origin is NULL and is needed.
+///
+/// The exception is thrown when the Name constructor for master file
+/// is used, the provided data is relative and the origin parameter is
+/// set to NULL.
+class MissingNameOrigin : public NameParserException {
+public:
+    MissingNameOrigin(const char* file, size_t line, const char* what) :
+        NameParserException(file, line, what) {}
+};
+
 ///
 /// This is a supplemental class used only as a return value of
 /// Name::compare() and LabelSequence::compare().
@@ -261,6 +272,37 @@ public:
     /// \param namestr A string representation of the name to be constructed.
     /// \param downcase Whether to convert upper case alphabets to lower case.
     explicit Name(const std::string& namestr, bool downcase = false);
+
+    /// \brief Constructor for master file parser
+    ///
+    /// This acts similar to the above. But the data is passed as raw C-string
+    /// instead of wrapped-up C++ std::string.
+    ///
+    /// Also, when the origin is non-NULL and the name_data is not ending with
+    /// a dot, it is considered relative and the origin is appended to it.
+    ///
+    /// If the name_data is equal to "@", the content of origin is copied.
+    ///
+    /// \param name_data The raw data of the name.
+    /// \param data_len How many bytes in name_data is valid and considered
+    ///     part of the name.
+    /// \param origin If non-NULL, it is taken as the origin to complete
+    ///     relative names.
+    /// \param downcase Whether to convert upper case letters to lower case.
+    /// \throw NameParserException or any of its descendants in case the
+    ///     input data is invalid.
+    /// \throw isc::InvalidParameter In case name_data is NULL or data_len is
+    ///     0.
+    /// \throw std::bad_alloc In case allocation fails.
+    /// \note This constructor is specially designed for the use of master
+    ///     file parser. It mimics the behaviour of names in the master file
+    ///     and accepts raw data. It is not recommended to be used by anything
+    ///     else.
+    /// \todo Should we make it private and the parser a friend, to hide the
+    ///     constructor?
+    Name(const char* name_data, size_t data_len, const Name* origin,
+         bool downcase = false);
+
     /// Constructor from wire-format %data.
     ///
     /// The \c buffer parameter normally stores a complete DNS message
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index e8cbe10..35d659c 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -22,8 +22,10 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES = unittest_util.h unittest_util.cc
 run_unittests_SOURCES += edns_unittest.cc
+run_unittests_SOURCES += master_lexer_inputsource_unittest.cc
 run_unittests_SOURCES += labelsequence_unittest.cc
 run_unittests_SOURCES += messagerenderer_unittest.cc
+run_unittests_SOURCES += master_lexer_token_unittest.cc
 run_unittests_SOURCES += name_unittest.cc
 run_unittests_SOURCES += nsec3hash_unittest.cc
 run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc
diff --git a/src/lib/dns/tests/master_lexer_inputsource_unittest.cc b/src/lib/dns/tests/master_lexer_inputsource_unittest.cc
new file mode 100644
index 0000000..db0cbec
--- /dev/null
+++ b/src/lib/dns/tests/master_lexer_inputsource_unittest.cc
@@ -0,0 +1,325 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/master_lexer_inputsource.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <string.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::master_lexer_internal;
+
+namespace {
+
+class InputSourceTest : public ::testing::Test {
+protected:
+    InputSourceTest() :
+        str_("Line1 to scan.\nLine2 to scan.\nLine3 to scan.\n"),
+        str_length_(strlen(str_)),
+        iss_(str_),
+        source_(iss_)
+    {}
+
+    const char* str_;
+    const size_t str_length_;
+    stringstream iss_;
+    InputSource source_;
+};
+
+// Test the default return values set during InputSource construction.
+TEST_F(InputSourceTest, defaults) {
+    EXPECT_EQ(1, source_.getCurrentLine());
+    EXPECT_FALSE(source_.atEOF());
+}
+
+// getName() on file and stream sources
+TEST_F(InputSourceTest, getName) {
+    EXPECT_EQ(0, source_.getName().find("stream-"));
+
+    // Use some file; doesn't really matter what.
+    InputSource source2(TEST_DATA_SRCDIR "/masterload.txt");
+    EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", source2.getName());
+}
+
+TEST_F(InputSourceTest, nonExistentFile) {
+    EXPECT_THROW({
+        InputSource source(TEST_DATA_SRCDIR "/does-not-exist");
+    }, InputSource::OpenError);
+}
+
+// getChar() should return characters from the input stream in
+// sequence. ungetChar() should skip backwards.
+void
+checkGetAndUngetChar(InputSource& source,
+                     const char* str, const size_t str_length)
+{
+    for (size_t i = 0; i < str_length; ++i) {
+        EXPECT_EQ(str[i], source.getChar());
+        EXPECT_FALSE(source.atEOF());
+    }
+
+    // At this point, we still have not reached EOF.
+    EXPECT_FALSE(source.atEOF());
+
+    // This should cause EOF to be set.
+    EXPECT_EQ(InputSource::END_OF_STREAM, source.getChar());
+
+    // Now, EOF should be set.
+    EXPECT_TRUE(source.atEOF());
+
+    // Now, let's go backwards. This should cause the EOF to be set to
+    // false.
+    source.ungetChar();
+
+    // Now, EOF should be false.
+    EXPECT_FALSE(source.atEOF());
+
+    // This should cause EOF to be set again.
+    EXPECT_EQ(InputSource::END_OF_STREAM, source.getChar());
+
+    // Now, EOF should be set.
+    EXPECT_TRUE(source.atEOF());
+
+    // Now, let's go backwards in a loop. Start by skipping the EOF.
+    source.ungetChar();
+
+    for (size_t i = 0; i < str_length; ++i) {
+        const size_t index = str_length - 1 - i;
+        // Skip one character.
+        source.ungetChar();
+        EXPECT_EQ(str[index], source.getChar());
+        // Skip the character we received again.
+        source.ungetChar();
+    }
+
+    // Skipping past the start of buffer should throw.
+    EXPECT_THROW(source.ungetChar(), InputSource::UngetBeforeBeginning);
+}
+
+TEST_F(InputSourceTest, stream) {
+    checkGetAndUngetChar(source_, str_, str_length_);
+}
+
+TEST_F(InputSourceTest, file) {
+    std::ifstream fs(TEST_DATA_SRCDIR "/masterload.txt");
+    const std::string str((std::istreambuf_iterator<char>(fs)),
+                          std::istreambuf_iterator<char>());
+    fs.close();
+
+    InputSource source(TEST_DATA_SRCDIR "/masterload.txt");
+    checkGetAndUngetChar(source, str.c_str(), str.size());
+}
+
+// ungetAll() should skip back to the place where the InputSource
+// started at construction, or the last saved start of line.
+TEST_F(InputSourceTest, ungetAll) {
+    while (!source_.atEOF()) {
+        source_.getChar();
+    }
+
+    // Now, we are at EOF.
+    EXPECT_TRUE(source_.atEOF());
+    EXPECT_EQ(4, source_.getCurrentLine());
+
+    source_.ungetAll();
+
+    // Now we are back to where we started.
+    EXPECT_EQ(1, source_.getCurrentLine());
+    EXPECT_FALSE(source_.atEOF());
+}
+
+TEST_F(InputSourceTest, compact) {
+    // Compact at the start
+    source_.compact();
+
+    // Ungetting here must throw.
+    EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning);
+
+    for (size_t i = 0; i < str_length_; ++i) {
+        EXPECT_EQ(str_[i], source_.getChar());
+        EXPECT_FALSE(source_.atEOF());
+    }
+
+    // At this point, we still have not reached EOF.
+    EXPECT_FALSE(source_.atEOF());
+
+    // This should cause EOF to be set.
+    EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar());
+
+    // Now, EOF should be set.
+    EXPECT_TRUE(source_.atEOF());
+    EXPECT_EQ(4, source_.getCurrentLine());
+
+    // Compact again
+    source_.compact();
+
+    // We are still at EOF.
+    EXPECT_TRUE(source_.atEOF());
+    EXPECT_EQ(4, source_.getCurrentLine());
+
+    // Skip the EOF.
+    source_.ungetChar();
+
+    // Ungetting here must throw.
+    EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning);
+
+    EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar());
+    EXPECT_TRUE(source_.atEOF());
+}
+
+TEST_F(InputSourceTest, markDuring) {
+    // First, skip to line 2.
+    while (!source_.atEOF() &&
+           (source_.getCurrentLine() != 2)) {
+        source_.getChar();
+    }
+    EXPECT_FALSE(source_.atEOF());
+    EXPECT_EQ(2, source_.getCurrentLine());
+
+    // Now, unget a couple of characters. This should cause the
+    // buffer_pos_ to be not equal to the size of the buffer.
+    source_.ungetChar();
+    source_.ungetChar();
+
+    // Now "mark" the source, meaning that we save line number and also
+    // compact the internal buffer at this stage.
+    source_.mark();
+
+    // Ungetting here must throw.
+    EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning);
+
+    for (size_t i = 13; i < str_length_; ++i) {
+        EXPECT_EQ(str_[i], source_.getChar());
+        EXPECT_FALSE(source_.atEOF());
+    }
+
+    // At this point, we still have not reached EOF.
+    EXPECT_FALSE(source_.atEOF());
+
+    // This should cause EOF to be set.
+    EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar());
+
+    // Now, EOF should be set.
+    EXPECT_TRUE(source_.atEOF());
+
+    // Now, ungetAll() and check where it goes back.
+    source_.ungetAll();
+
+    // Ungetting here must throw.
+    EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning);
+
+    for (size_t i = 13; i < str_length_; ++i) {
+        EXPECT_EQ(str_[i], source_.getChar());
+        EXPECT_FALSE(source_.atEOF());
+    }
+
+    // At this point, we still have not reached EOF.
+    EXPECT_FALSE(source_.atEOF());
+
+    // This should cause EOF to be set.
+    EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar());
+
+    // Now, EOF should be set.
+    EXPECT_TRUE(source_.atEOF());
+}
+
+// Test line counters.
+TEST_F(InputSourceTest, lines) {
+    size_t line = 1;
+    while (!source_.atEOF()) {
+        if (source_.getChar() == '\n') {
+            ++line;
+        }
+        EXPECT_EQ(line, source_.getCurrentLine());
+    }
+
+    // Now, we are at EOF.
+    EXPECT_TRUE(source_.atEOF());
+    EXPECT_EQ(4, source_.getCurrentLine());
+
+    // Go backwards 2 characters, skipping the last EOF and '\n'.
+    source_.ungetChar();
+    source_.ungetChar();
+
+    EXPECT_FALSE(source_.atEOF());
+    EXPECT_EQ(3, source_.getCurrentLine());
+
+    source_.ungetAll();
+
+    // Now we are back to where we started.
+    EXPECT_EQ(1, source_.getCurrentLine());
+    EXPECT_FALSE(source_.atEOF());
+
+    // Now check that line numbers are decremented properly (as much as
+    // possible using the available API).
+    while (!source_.atEOF()) {
+        source_.getChar();
+    }
+    line = source_.getCurrentLine();
+
+    // Now, we are at EOF.
+    EXPECT_TRUE(source_.atEOF());
+    EXPECT_EQ(4, line);
+
+    EXPECT_THROW({
+        while (true) {
+            source_.ungetChar();
+            EXPECT_TRUE(((line == source_.getCurrentLine()) ||
+                         ((line - 1) == source_.getCurrentLine())));
+            line = source_.getCurrentLine();
+        }
+    }, InputSource::UngetBeforeBeginning);
+
+    // Now we are back to where we started.
+    EXPECT_EQ(1, source_.getCurrentLine());
+}
+
+// ungetAll() after saveLine() should skip back to the last-saved place.
+TEST_F(InputSourceTest, saveLine) {
+    // First, skip to line 2.
+    while (!source_.atEOF() &&
+           (source_.getCurrentLine() != 2)) {
+        source_.getChar();
+    }
+    EXPECT_FALSE(source_.atEOF());
+    EXPECT_EQ(2, source_.getCurrentLine());
+
+    // Now, save the line.
+    source_.saveLine();
+
+    // Now, go to EOF
+    while (!source_.atEOF()) {
+        source_.getChar();
+    }
+
+    // Now, we are at EOF.
+    EXPECT_TRUE(source_.atEOF());
+    EXPECT_EQ(4, source_.getCurrentLine());
+
+    // Now, ungetAll() and check where it goes back.
+    source_.ungetAll();
+
+    // Now we are back to where we last-saved.
+    EXPECT_EQ(2, source_.getCurrentLine());
+    EXPECT_FALSE(source_.atEOF());
+}
+
+} // end namespace
diff --git a/src/lib/dns/tests/master_lexer_token_unittest.cc b/src/lib/dns/tests/master_lexer_token_unittest.cc
new file mode 100644
index 0000000..a63b9ca
--- /dev/null
+++ b/src/lib/dns/tests/master_lexer_token_unittest.cc
@@ -0,0 +1,156 @@
+// 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 <dns/master_lexer.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace isc::dns;
+
+namespace {
+
+const char TEST_STRING[] = "string token";
+// This excludes the ending \0 character
+const size_t TEST_STRING_LEN = sizeof(TEST_STRING) - 1;
+
+class MasterLexerTokenTest : public ::testing::Test {
+protected:
+    MasterLexerTokenTest() :
+        token_eof(MasterLexer::Token::END_OF_FILE),
+        token_str(TEST_STRING, TEST_STRING_LEN),
+        token_num(42),
+        token_err(MasterLexer::Token::UNEXPECTED_END)
+    {}
+
+    const MasterLexer::Token token_eof; // an example of non-value type token
+    const MasterLexer::Token token_str;
+    const MasterLexer::Token token_num;
+    const MasterLexer::Token token_err;
+};
+
+
+TEST_F(MasterLexerTokenTest, strings) {
+    // basic construction and getter checks
+    EXPECT_EQ(MasterLexer::Token::STRING, token_str.getType());
+    EXPECT_EQ(std::string("string token"), token_str.getString());
+    const MasterLexer::Token::StringRegion str_region =
+        token_str.getStringRegion();
+    EXPECT_EQ(TEST_STRING, str_region.beg);
+    EXPECT_EQ(TEST_STRING_LEN, str_region.len);
+
+    // Even if the stored string contains a nul character (in this case,
+    // it happens to be at the end of the string, but could be in the middle),
+    // getString() should return a string object containing the nul.
+    std::string expected_str("string token");
+    expected_str.push_back('\0');
+    EXPECT_EQ(expected_str,
+              MasterLexer::Token(TEST_STRING, TEST_STRING_LEN + 1).getString());
+
+    // Construct type of qstring
+    EXPECT_EQ(MasterLexer::Token::QSTRING,
+              MasterLexer::Token(TEST_STRING, sizeof(TEST_STRING), true).
+              getType());
+    // if we explicitly set 'quoted' to false, it should be normal string
+    EXPECT_EQ(MasterLexer::Token::STRING,
+              MasterLexer::Token(TEST_STRING, sizeof(TEST_STRING), false).
+              getType());
+
+    // getString/StringRegion() aren't allowed for non string(-variant) types
+    EXPECT_THROW(token_eof.getString(), isc::InvalidOperation);
+    EXPECT_THROW(token_num.getString(), isc::InvalidOperation);
+    EXPECT_THROW(token_eof.getStringRegion(), isc::InvalidOperation);
+    EXPECT_THROW(token_num.getStringRegion(), isc::InvalidOperation);
+}
+
+TEST_F(MasterLexerTokenTest, numbers) {
+    EXPECT_EQ(42, token_num.getNumber());
+    EXPECT_EQ(MasterLexer::Token::NUMBER, token_num.getType());
+
+    // It's copyable and assignable.
+    MasterLexer::Token token(token_num);
+    EXPECT_EQ(42, token.getNumber());
+    EXPECT_EQ(MasterLexer::Token::NUMBER, token.getType());
+
+    token = token_num;
+    EXPECT_EQ(42, token.getNumber());
+    EXPECT_EQ(MasterLexer::Token::NUMBER, token.getType());
+
+    // it's okay to replace it with a different type of token
+    token = token_eof;
+    EXPECT_EQ(MasterLexer::Token::END_OF_FILE, token.getType());
+
+    // Possible max value
+    token = MasterLexer::Token(0xffffffff);
+    EXPECT_EQ(4294967295u, token.getNumber());
+
+    // getNumber() isn't allowed for non number types
+    EXPECT_THROW(token_eof.getNumber(), isc::InvalidOperation);
+    EXPECT_THROW(token_str.getNumber(), isc::InvalidOperation);
+}
+
+TEST_F(MasterLexerTokenTest, novalues) {
+    // Just checking we can construct them and getType() returns correct value.
+    EXPECT_EQ(MasterLexer::Token::END_OF_FILE, token_eof.getType());
+    EXPECT_EQ(MasterLexer::Token::END_OF_LINE,
+              MasterLexer::Token(MasterLexer::Token::END_OF_LINE).getType());
+    EXPECT_EQ(MasterLexer::Token::INITIAL_WS,
+              MasterLexer::Token(MasterLexer::Token::INITIAL_WS).getType());
+
+    // Special types of tokens cannot have value-based types
+    EXPECT_THROW(MasterLexer::Token t(MasterLexer::Token::STRING),
+                 isc::InvalidParameter);
+    EXPECT_THROW(MasterLexer::Token t(MasterLexer::Token::QSTRING),
+                 isc::InvalidParameter);
+    EXPECT_THROW(MasterLexer::Token t(MasterLexer::Token::NUMBER),
+                 isc::InvalidParameter);
+    EXPECT_THROW(MasterLexer::Token t(MasterLexer::Token::ERROR),
+                 isc::InvalidParameter);
+}
+
+TEST_F(MasterLexerTokenTest, errors) {
+    EXPECT_EQ(MasterLexer::Token::ERROR, token_err.getType());
+    EXPECT_EQ(MasterLexer::Token::UNEXPECTED_END, token_err.getErrorCode());
+    EXPECT_EQ("unexpected end of input", token_err.getErrorText());
+    EXPECT_EQ("lexer not started",
+              MasterLexer::Token(MasterLexer::Token::NOT_STARTED).
+              getErrorText());
+    EXPECT_EQ("unbalanced parentheses",
+              MasterLexer::Token(MasterLexer::Token::UNBALANCED_PAREN).
+              getErrorText());
+    EXPECT_EQ("unbalanced quotes",
+              MasterLexer::Token(MasterLexer::Token::UNBALANCED_QUOTES).
+              getErrorText());
+
+    // getErrorCode/Text() isn't allowed for non number types
+    EXPECT_THROW(token_num.getErrorCode(), isc::InvalidOperation);
+    EXPECT_THROW(token_num.getErrorText(), isc::InvalidOperation);
+
+    // Only the pre-defined error code is accepted.  Hardcoding '4' (max code
+    // + 1) is intentional; it'd be actually better if we notice it when we
+    // update the enum list (which shouldn't happen too often).
+    EXPECT_THROW(MasterLexer::Token(MasterLexer::Token::ErrorCode(4)),
+                 isc::InvalidParameter);
+
+    // Check the coexistence of "from number" and "from error-code"
+    // constructors won't cause confusion.
+    EXPECT_EQ(MasterLexer::Token::NUMBER,
+              MasterLexer::Token(static_cast<uint32_t>(
+                                     MasterLexer::Token::NOT_STARTED)).
+              getType());
+}
+}
diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc
index c327bdc..10d1e55 100644
--- a/src/lib/dns/tests/name_unittest.cc
+++ b/src/lib/dns/tests/name_unittest.cc
@@ -40,6 +40,22 @@ using namespace isc::util;
 const size_t Name::MAX_WIRE;
 const size_t Name::MAX_LABELS;
 
+// This is a name of maximum allowed number of labels
+const char* max_labels_str = "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 40
+                             "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 80
+                             "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 120
+                             "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 160
+                             "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 200
+                             "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 240
+                             "0.1.2.3.4.5.6";
+// This is a name of maximum allowed length
+const char* max_len_str = "123456789.123456789.123456789.123456789.123456789."
+                          "123456789.123456789.123456789.123456789.123456789."
+                          "123456789.123456789.123456789.123456789.123456789."
+                          "123456789.123456789.123456789.123456789.123456789."
+                          "123456789.123456789.123456789.123456789.123456789."
+                          "123";
+
 namespace {
 class NameTest : public ::testing::Test {
 protected:
@@ -47,6 +63,8 @@ protected:
                  example_name_upper("WWW.EXAMPLE.COM"),
                  small_name("aaa.example.com"),
                  large_name("zzz.example.com"),
+                 origin_name("example.com."),
+                 origin_name_upper("EXAMPLE.COM"),
                  buffer_actual(0), buffer_expected(0)
     {}
 
@@ -54,6 +72,8 @@ protected:
     Name example_name_upper;    // this will be modified and cannot be const
     const Name small_name;
     const Name large_name;
+    const Name origin_name;
+    const Name origin_name_upper;
     OutputBuffer buffer_actual, buffer_expected;
 
     //
@@ -137,6 +157,11 @@ checkBadTextName(const string& txt) {
     // NameParserException.
     EXPECT_THROW(Name(txt, false), ExceptionType);
     EXPECT_THROW(Name(txt, false), NameParserException);
+    // The same is thrown when constructing by the master-file constructor
+    EXPECT_THROW(Name(txt.c_str(), txt.length(), &Name::ROOT_NAME()),
+                 ExceptionType);
+    EXPECT_THROW(Name(txt.c_str(), txt.length(), &Name::ROOT_NAME()),
+                 NameParserException);
 }
 
 TEST_F(NameTest, fromText) {
@@ -201,27 +226,99 @@ TEST_F(NameTest, fromText) {
                                   "123456789.123456789.123456789.123456789."
                                   "123456789.1234");
     // This is a possible longest name and should be accepted
-    EXPECT_NO_THROW(Name("123456789.123456789.123456789.123456789.123456789."
-                         "123456789.123456789.123456789.123456789.123456789."
-                         "123456789.123456789.123456789.123456789.123456789."
-                         "123456789.123456789.123456789.123456789.123456789."
-                         "123456789.123456789.123456789.123456789.123456789."
-                         "123"));
+    EXPECT_NO_THROW(Name(string(max_len_str)));
     // \DDD must consist of 3 digits.
     checkBadTextName<IncompleteName>("\\12");
 
     // a name with the max number of labels.  should be constructed without
     // an error, and its length should be the max value.
-    Name maxlabels = Name("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 40
-                          "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 80
-                          "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 120
-                          "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 160
-                          "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 200
-                          "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 240
-                          "0.1.2.3.4.5.6.");
+    Name maxlabels = Name(string(max_labels_str));
     EXPECT_EQ(Name::MAX_LABELS, maxlabels.getLabelCount());
 }
 
+// on the rest while we prepare it.
+// Check the @ syntax is accepted and it just copies the origin.
+TEST_F(NameTest, copyOrigin) {
+    EXPECT_EQ(origin_name, Name("@", 1, &origin_name));
+    // The downcase works on the origin too. But only when we provide it.
+    EXPECT_EQ(origin_name, Name("@", 1, &origin_name_upper, true));
+    EXPECT_EQ(origin_name_upper, Name("@", 1, &origin_name_upper, true));
+    // If we don't provide the origin, it throws
+    EXPECT_THROW(Name("@", 1, NULL), MissingNameOrigin);
+}
+
+// Test the master-file constructor does not append the origin when the
+// provided name is absolute
+TEST_F(NameTest, dontAppendOrigin) {
+    EXPECT_EQ(example_name, Name("www.example.com.", 16, &origin_name));
+    // The downcase works (only if provided, though)
+    EXPECT_EQ(example_name, Name("WWW.EXAMPLE.COM.", 16, &origin_name, true));
+    EXPECT_EQ(example_name_upper, Name("WWW.EXAMPLE.COM.", 16, &origin_name));
+    // And it does not require the origin to be provided
+    EXPECT_NO_THROW(Name("www.example.com.", 16, NULL));
+}
+
+// Test the master-file constructor properly appends the origin when
+// the provided name is relative.
+TEST_F(NameTest, appendOrigin) {
+    EXPECT_EQ(example_name, Name("www", 3, &origin_name));
+    // Check the downcase works (if provided)
+    EXPECT_EQ(example_name, Name("WWW", 3, &origin_name, true));
+    EXPECT_EQ(example_name, Name("WWW", 3, &origin_name_upper, true));
+    EXPECT_EQ(example_name_upper, Name("WWW", 3, &origin_name_upper));
+    // Check we can prepend more than one label
+    EXPECT_EQ(Name("a.b.c.d.example.com."), Name("a.b.c.d", 7, &origin_name));
+    // When the name is relative, we throw.
+    EXPECT_THROW(Name("www", 3, NULL), MissingNameOrigin);
+}
+
+// When we don't provide the data, it throws
+TEST_F(NameTest, noDataProvided) {
+    EXPECT_THROW(Name(NULL, 10, NULL), isc::InvalidParameter);
+    EXPECT_THROW(Name(NULL, 10, &origin_name), isc::InvalidParameter);
+    EXPECT_THROW(Name("www", 0, NULL), isc::InvalidParameter);
+    EXPECT_THROW(Name("www", 0, &origin_name), isc::InvalidParameter);
+}
+
+// When we combine the first part and the origin together, the resulting name
+// is too long. It should throw. Other test checks this is valid when alone
+// (without the origin appended).
+TEST_F(NameTest, combinedTooLong) {
+    EXPECT_THROW(Name(max_len_str, strlen(max_len_str), &origin_name),
+                 TooLongName);
+    EXPECT_THROW(Name(max_labels_str, strlen(max_labels_str), &origin_name),
+                 TooLongName);
+    // Appending the root should be OK
+    EXPECT_NO_THROW(Name(max_len_str, strlen(max_len_str),
+                         &Name::ROOT_NAME()));
+    EXPECT_NO_THROW(Name(max_labels_str, strlen(max_labels_str),
+                         &Name::ROOT_NAME()));
+}
+
+// Test the handling of @ in the name. If it is alone, it is the origin (when
+// it exists) or the root. If it is somewhere else, it has no special meaning.
+TEST_F(NameTest, atSign) {
+    // If it is alone, it is the origin
+    EXPECT_EQ(origin_name, Name("@", 1, &origin_name));
+    EXPECT_THROW(Name("@", 1, NULL), MissingNameOrigin);
+    EXPECT_EQ(Name::ROOT_NAME(), Name("@"));
+
+    // It is not alone. It is taken verbatim. We check the name converted
+    // back to the textual form, since checking it agains other name object
+    // may be wrong -- if we create it wrong the same way as the tested
+    // object.
+    EXPECT_EQ("\\@.", Name("@.").toText());
+    EXPECT_EQ("\\@.", Name("@.", 2, NULL).toText());
+    EXPECT_EQ("\\@something.", Name("@something").toText());
+    EXPECT_EQ("something\\@.", Name("something@").toText());
+    EXPECT_EQ("\\@x.example.com.", Name("@x", 2, &origin_name).toText());
+    EXPECT_EQ("x\\@.example.com.", Name("x@", 2, &origin_name).toText());
+
+    // An escaped at-sign isn't active
+    EXPECT_EQ("\\@.", Name("\\@").toText());
+    EXPECT_EQ("\\@.example.com.", Name("\\@", 2, &origin_name).toText());
+}
+
 TEST_F(NameTest, fromWire) {
     //
     // test cases derived from BIND9 tests.
@@ -520,7 +617,6 @@ TEST_F(NameTest, downcase) {
     // confirm the calling object is actually modified
     example_name_upper.downcase();
     compareInWireFormat(example_name_upper, example_name);
-    
 }
 
 TEST_F(NameTest, at) {
diff --git a/src/lib/python/isc/sysinfo/sysinfo.py b/src/lib/python/isc/sysinfo/sysinfo.py
index 97b9e59..8e4610c 100644
--- a/src/lib/python/isc/sysinfo/sysinfo.py
+++ b/src/lib/python/isc/sysinfo/sysinfo.py
@@ -22,6 +22,7 @@ import subprocess
 import os.path
 import platform
 import time
+from datetime import timedelta
 
 class SysInfo:
     def __init__(self):
@@ -93,6 +94,18 @@ class SysInfo:
         """Returns the uptime in seconds."""
         return self._uptime
 
+    def get_uptime_desc(self):
+        """Returns the uptime in human readable form.
+
+        The format is the result of str() method of the standard library
+        datetime.timedelta class.  It returns None if _uptime is None.
+
+        """
+        if self._uptime is None:
+            return None
+
+        return str(timedelta(seconds=self._uptime))
+
     def get_loadavg(self):
         """Returns the load average as 3 floating point values in an array."""
         return self._loadavg
@@ -333,11 +346,11 @@ class SysInfoOpenBSD(SysInfoBSD):
             pass
 
         try:
+            # We use the size of free-list from the vmstat result.
             s = subprocess.check_output(['vmstat'])
             lines = s.decode('utf-8').split('\n')
             v = re.split('\s+', lines[2])
-            used = int(v[4]) * 1024
-            self._mem_free = self._mem_total - used
+            self._mem_free = int(v[5]) * 1024
         except (subprocess.CalledProcessError, OSError):
             pass
 
@@ -389,17 +402,27 @@ class SysInfoFreeBSD(SysInfoFreeBSDOSX):
         super().__init__()
 
         try:
-            s = subprocess.check_output(['sysctl', '-n', 'kern.smp.active'])
-            self._platform_is_smp = int(s.decode('utf-8').strip()) > 0
-        except (subprocess.CalledProcessError, OSError):
+            # There doesn't seem to be an easy way to reliably detect whether
+            # the kernel was built with SMP support on FreeBSD.  We use
+            # a sysctl variable that is only defined in SMP kernels.
+            # This assumption seems to hold for several recent versions of
+            # FreeBSD, but it may not always be so for future versions.
+            s = subprocess.check_output(['sysctl', '-n',
+                                         'kern.smp.forward_signal_enabled'])
+            self._platform_is_smp = True # the value doesn't matter
+        except subprocess.CalledProcessError:
+            # if this variable isn't defined we should see this exception.
+            # intepret it as an indication of non-SMP kernel.
+            self._platform_is_smp = False
+        except OSError:
             pass
 
         try:
+            # We use the size of free-list from the vmstat result.
             s = subprocess.check_output(['vmstat', '-H'])
             lines = s.decode('utf-8').split('\n')
             v = re.split('\s+', lines[2])
-            used = int(v[4]) * 1024
-            self._mem_free = self._mem_total - used
+            self._mem_free = int(v[5]) * 1024
         except (subprocess.CalledProcessError, OSError):
             pass
 
diff --git a/src/lib/python/isc/sysinfo/tests/sysinfo_test.py b/src/lib/python/isc/sysinfo/tests/sysinfo_test.py
index 0add036..8f11df0 100644
--- a/src/lib/python/isc/sysinfo/tests/sysinfo_test.py
+++ b/src/lib/python/isc/sysinfo/tests/sysinfo_test.py
@@ -49,7 +49,7 @@ class MyLinuxFile:
         elif self._filename == '/proc/version':
             return 'An SMP version string'
         elif self._filename == '/proc/uptime':
-            return '86400.75 139993.71'
+            return '172800.75 139993.71'
         elif self._filename == '/proc/loadavg':
             return '0.1 0.2 0.3 0.4'
         else:
@@ -171,26 +171,33 @@ def _my_freebsd_os_sysconf(key):
 def _my_freebsd_platform_uname():
     return ('FreeBSD', 'freebsd', '8.2-RELEASE', '', 'i386')
 
-def _my_freebsd_osx_subprocess_check_output(command):
+def _my_freebsd_osx_subprocess_check_output(command, faked_output={}):
     '''subprocess output shared for freebsd and osx'''
     assert type(command) == list, 'command argument is not a list'
     if command == ['sysctl', '-n', 'kern.boottime']:
-        return bytes('{ sec = ' + str(int(time.time() - 76632)) + ', usec = 0 }\n', 'utf-8')
+        if 'boottime-sysctl' in faked_output:
+            return faked_output['boottime-sysctl']
+        return bytes('{ sec = ' + str(int(time.time() - 76632)) +
+                     ', usec = 0 }\n', 'utf-8')
     elif command == ['sysctl', '-n', 'vm.loadavg']:
         return b'{ 0.2 0.4 0.6 }\n'
     else:
         return _my_bsd_subprocess_check_output(command)
 
-def _my_freebsd_subprocess_check_output(command):
+def _my_freebsd_subprocess_check_output(command, faked_output):
     assert type(command) == list, 'command argument is not a list'
-    if command == ['sysctl', '-n', 'kern.smp.active']:
-        return b'1\n'
+    if command == ['sysctl', '-n', 'kern.smp.forward_signal_enabled']:
+        output = faked_output['smp-sysctl']
+        if isinstance(output, Exception):
+            raise output
+        return output
     elif command == ['vmstat', '-H']:
         return b' procs    memory       page                    disks    traps          cpu\n r b w    avm     fre  flt  re  pi  po  fr  sr wd0 cd0  int   sys   cs us sy id\n 0 0 0   343434  123456   47   0   0   0   0   0   2   0    2    80   14  0  1 99\n'
     elif command == ['swapctl', '-s', '-k']:
         return b'Total:         1013216    0\n'
     else:
-        freebsd_osx_output = _my_freebsd_osx_subprocess_check_output(command)
+        freebsd_osx_output = \
+            _my_freebsd_osx_subprocess_check_output(command, faked_output)
         if freebsd_osx_output is not None:
             return freebsd_osx_output
         else:
@@ -252,6 +259,7 @@ class SysInfoTest(unittest.TestCase):
         self.assertEqual('Unknown', s.get_platform_machine())
         self.assertFalse(s.get_platform_is_smp())
         self.assertEqual(None, s.get_uptime())
+        self.assertEqual(None, s.get_uptime_desc())
         self.assertEqual(None, s.get_loadavg())
         self.assertEqual(None, s.get_mem_total())
         self.assertEqual(None, s.get_mem_free())
@@ -267,7 +275,11 @@ class SysInfoTest(unittest.TestCase):
 
     def test_sysinfo_factory(self):
         """Test that SysInfoFromFactory returns a valid system-specific
-        SysInfo implementation."""
+        SysInfo implementation.
+
+        See sysinfo.SysInfoTestcase() for some of the parameters.
+
+        """
 
         old_platform_system = platform.system
         platform.system = _my_testcase_platform_system
@@ -281,6 +293,8 @@ class SysInfoTest(unittest.TestCase):
         self.assertEqual('Unknown', s.get_platform_machine())
         self.assertFalse(s.get_platform_is_smp())
         self.assertEqual(131072, s.get_uptime())
+        # We check that we do NOT add 's' to 'day' (because it's singular):
+        self.assertEqual('1 day, 12:24:32', s.get_uptime_desc())
         self.assertEqual(None, s.get_loadavg())
         self.assertEqual(None, s.get_mem_total())
         self.assertEqual(None, s.get_mem_free())
@@ -312,7 +326,10 @@ class SysInfoTest(unittest.TestCase):
         self.assertEqual(NPROCESSORS_LINUX, s.get_num_processors())
         self.assertEqual('myhostname', s.get_platform_hostname())
         self.assertTrue(s.get_platform_is_smp())
-        self.assertEqual(86401, s.get_uptime())
+        self.assertEqual(172801, s.get_uptime())
+        # We check that we add 's' to 'day', and that the mm part has an
+        # additional 0, i.e., not '0:0' but '0:00':
+        self.assertEqual('2 days, 0:00:01', s.get_uptime_desc())
         self.assertEqual((0.1, 0.2, 0.3), s.get_loadavg())
         self.assertEqual(3157884928, s.get_mem_total())
         self.assertEqual(891383808, s.get_mem_free())
@@ -366,7 +383,7 @@ class SysInfoTest(unittest.TestCase):
         self.assertEqual((0.7, 0.9, 0.8), s.get_loadavg())
         self.assertFalse(s.get_platform_is_smp())
         self.assertEqual(543214321, s.get_mem_total())
-        self.assertEqual(543214321 - (121212 * 1024), s.get_mem_free())
+        self.assertEqual(123456 * 1024, s.get_mem_free())
         self.assertEqual(566791168, s.get_mem_swap_total())
         self.assertEqual(566789120, s.get_mem_swap_free())
 
@@ -379,18 +396,42 @@ class SysInfoTest(unittest.TestCase):
         # with mock ones for testing.
         platform.system = _my_freebsd_platform_system
         os.sysconf = _my_freebsd_os_sysconf
-        subprocess.check_output = _my_freebsd_subprocess_check_output
+
+        # We use a lambda object so we can tweak the subprocess output during
+        # the tests later.
+        faked_process_output = { 'smp-sysctl': b'1\n' }
+        subprocess.check_output = lambda command : \
+            _my_freebsd_subprocess_check_output(command, faked_process_output)
+
         os.uname = _my_freebsd_platform_uname
 
         s = SysInfoFromFactory()
         self.assertEqual(NPROCESSORS_FREEBSD, s.get_num_processors())
         self.assertTrue(s.get_platform_is_smp())
 
+        # We check the kernel SMP support by the availability of a sysctl
+        # variable.  The value (especially a 0 value) shouldn't matter.
+        faked_process_output['smp-sysctl'] = b'0\n'
+        s = SysInfoFromFactory()
+        self.assertTrue(s.get_platform_is_smp())
+
+        # if the sysctl raises CalledProcessError, we treat it as non-SMP
+        # kernel.
+        faked_process_output['smp-sysctl'] = \
+            subprocess.CalledProcessError(1, 'sysctl')
+        s = SysInfoFromFactory()
+        self.assertFalse(s.get_platform_is_smp())
+
+        # if it results in OSError, no SMP information will be provided.
+        faked_process_output['smp-sysctl'] = OSError()
+        s = SysInfoFromFactory()
+        self.assertIsNone(s.get_platform_is_smp())
+
         self.check_bsd_values(s)
 
         self.assertEqual((0.2, 0.4, 0.6), s.get_loadavg())
         self.assertEqual(543214321, s.get_mem_total())
-        self.assertEqual(543214321 - (343434 * 1024), s.get_mem_free())
+        self.assertEqual(123456 * 1024, s.get_mem_free())
         self.assertEqual(1037533184, s.get_mem_swap_total())
         self.assertEqual(1037533184, s.get_mem_swap_free())
 
diff --git a/src/lib/statistics/Makefile.am b/src/lib/statistics/Makefile.am
index 206b527..a395bf5 100644
--- a/src/lib/statistics/Makefile.am
+++ b/src/lib/statistics/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests
+SUBDIRS = tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
@@ -17,8 +17,9 @@ if USE_CLANGPP
 AM_CXXFLAGS += -Wno-unused-parameter
 endif
 
-lib_LTLIBRARIES = libb10-statistics.la
-libb10_statistics_la_SOURCES  = counter.h counter.cc
-libb10_statistics_la_SOURCES  += counter_dict.h counter_dict.cc
-
 CLEANFILES = *.gcno *.gcda
+
+# These are header-only shared classes and required to build BIND 10.
+# Include them in the distributed tarball with EXTRA_DIST (like as
+# external sources in ext/).
+EXTRA_DIST = counter.h counter_dict.h
diff --git a/src/lib/statistics/counter.cc b/src/lib/statistics/counter.cc
deleted file mode 100644
index 53dc58e..0000000
--- a/src/lib/statistics/counter.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.
-
-#include <vector>
-
-#include <boost/noncopyable.hpp>
-
-#include <statistics/counter.h>
-
-namespace {
-const unsigned int InitialValue = 0;
-} // namespace
-
-namespace isc {
-namespace statistics {
-
-class CounterImpl : boost::noncopyable {
-    private:
-        std::vector<Counter::Value> counters_;
-    public:
-        CounterImpl(const size_t nelements);
-        ~CounterImpl();
-        void inc(const Counter::Type&);
-        const Counter::Value& get(const Counter::Type&) const;
-};
-
-CounterImpl::CounterImpl(const size_t items) :
-    counters_(items, InitialValue)
-{
-    if (items == 0) {
-        isc_throw(isc::InvalidParameter, "Items must not be 0");
-    }
-}
-
-CounterImpl::~CounterImpl() {}
-
-void
-CounterImpl::inc(const Counter::Type& type) {
-    if(type >= counters_.size()) {
-        isc_throw(isc::OutOfRange, "Counter type is out of range");
-    }
-    ++counters_.at(type);
-    return;
-}
-
-const Counter::Value&
-CounterImpl::get(const Counter::Type& type) const {
-    if(type >= counters_.size()) {
-        isc_throw(isc::OutOfRange, "Counter type is out of range");
-    }
-    return (counters_.at(type));
-}
-
-Counter::Counter(const size_t items) : impl_(new CounterImpl(items))
-{}
-
-Counter::~Counter() {}
-
-void
-Counter::inc(const Type& type) {
-    impl_->inc(type);
-    return;
-}
-
-const Counter::Value&
-Counter::get(const Type& type) const {
-    return (impl_->get(type));
-}
-
-}   // namespace statistics
-}   // namespace isc
diff --git a/src/lib/statistics/counter.h b/src/lib/statistics/counter.h
index 9d67b1a..af52da4 100644
--- a/src/lib/statistics/counter.h
+++ b/src/lib/statistics/counter.h
@@ -15,24 +15,29 @@
 #ifndef COUNTER_H
 #define COUNTER_H 1
 
+#include <exceptions/exceptions.h>
+
 #include <boost/noncopyable.hpp>
 #include <boost/scoped_ptr.hpp>
 
-#include <exceptions/exceptions.h>
+#include <vector>
+
+namespace {
+const unsigned int InitialValue = 0;
+} // anonymous namespace
 
 namespace isc {
 namespace statistics {
 
-// forward declaration for pImpl idiom
-class CounterImpl;
-
 class Counter : boost::noncopyable {
-private:
-    boost::scoped_ptr<CounterImpl> impl_;
 public:
     typedef unsigned int Type;
     typedef unsigned int Value;
 
+private:
+    std::vector<Counter::Value> counters_;
+
+public:
     /// The constructor.
     ///
     /// This constructor is mostly exception free. But it may still throw
@@ -41,29 +46,46 @@ public:
     /// \param items A number of counter items to hold (greater than 0)
     ///
     /// \throw isc::InvalidParameter \a items is 0
-    Counter(const size_t items);
+    explicit Counter(const size_t items) :
+        counters_(items, InitialValue)
+    {
+        if (items == 0) {
+            isc_throw(isc::InvalidParameter, "Items must not be 0");
+        }
+    };
 
     /// The destructor.
     ///
     /// This method never throws an exception.
-    ~Counter();
+    ~Counter() {};
 
     /// \brief Increment a counter item specified with \a type.
     ///
     /// \param type %Counter item to increment
     ///
     /// \throw isc::OutOfRange \a type is invalid
-    void inc(const Type& type);
+    void inc(const Counter::Type type) {
+        if (type >= counters_.size()) {
+            isc_throw(isc::OutOfRange, "Counter type is out of range");
+        }
+        ++counters_.at(type);
+        return;
+    };
 
     /// \brief Get the value of a counter item specified with \a type.
     ///
     /// \param type %Counter item to get the value of
     ///
     /// \throw isc::OutOfRange \a type is invalid
-    const Value& get(const Type& type) const;
+    const Counter::Value& get(const Counter::Type type) const {
+        if (type >= counters_.size()) {
+            isc_throw(isc::OutOfRange, "Counter type is out of range");
+        }
+        return (counters_.at(type));
+    };
 };
 
 }   // namespace statistics
 }   // namespace isc
 
-#endif
+#endif // __COUNTER_H
diff --git a/src/lib/statistics/counter_dict.cc b/src/lib/statistics/counter_dict.cc
deleted file mode 100644
index 55353b2..0000000
--- a/src/lib/statistics/counter_dict.cc
+++ /dev/null
@@ -1,265 +0,0 @@
-// 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.
-
-#include <cassert>
-#include <stdexcept>
-#include <iterator>
-#include <map>
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <statistics/counter_dict.h>
-
-namespace {
-typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
-typedef std::map<std::string, CounterPtr> DictionaryMap;
-}
-
-namespace isc {
-namespace statistics {
-
-// Implementation detail class for CounterDictionary::ConstIterator
-class CounterDictionaryConstIteratorImpl;
-
-class CounterDictionaryImpl : boost::noncopyable {
-private:
-    DictionaryMap dictionary_;
-    std::vector<std::string> elements_;
-    const size_t items_;
-    // Default constructor is forbidden; number of counter items must be
-    // specified at the construction of this class.
-    CounterDictionaryImpl();
-public:
-    CounterDictionaryImpl(const size_t items);
-    ~CounterDictionaryImpl();
-    void addElement(const std::string& name);
-    void deleteElement(const std::string& name);
-    Counter& getElement(const std::string& name);
-public:
-    CounterDictionaryConstIteratorImpl begin() const;
-    CounterDictionaryConstIteratorImpl end() const;
-};
-
-// Constructor with number of items
-CounterDictionaryImpl::CounterDictionaryImpl(const size_t items) :
-    items_(items)
-{
-    // The number of items must not be 0
-    if (items == 0) {
-        isc_throw(isc::InvalidParameter, "Items must not be 0");
-    }
-}
-
-// Destructor
-CounterDictionaryImpl::~CounterDictionaryImpl() {}
-
-void
-CounterDictionaryImpl::addElement(const std::string& name) {
-    // throw if the element already exists
-    if (dictionary_.count(name) != 0) {
-        isc_throw(isc::InvalidParameter,
-                  "Element " << name << " already exists");
-    }
-    assert(items_ != 0);
-    // Create a new Counter and add to the map
-    dictionary_.insert(
-        DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
-}
-
-void
-CounterDictionaryImpl::deleteElement(const std::string& name) {
-    size_t result = dictionary_.erase(name);
-    if (result != 1) {
-        // If an element with specified name does not exist, throw
-        // isc::OutOfRange.
-        isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
-    }
-}
-
-Counter&
-CounterDictionaryImpl::getElement(const std::string& name) {
-    DictionaryMap::const_iterator i = dictionary_.find(name);
-    if (i != dictionary_.end()) {
-        // the key was found. return the element.
-        return (*(i->second));
-    } else {
-        // If an element with specified name does not exist, throw
-        // isc::OutOfRange.
-        isc_throw(isc::OutOfRange, "Element " << name << " does not exist");
-    }
-}
-
-// Constructor
-// Initialize impl_
-CounterDictionary::CounterDictionary(const size_t items) :
-    impl_(new CounterDictionaryImpl(items))
-{}
-
-// Destructor
-// impl_ will be freed automatically with scoped_ptr
-CounterDictionary::~CounterDictionary() {}
-
-void
-CounterDictionary::addElement(const std::string& name) {
-    impl_->addElement(name);
-}
-
-void
-CounterDictionary::deleteElement(const std::string& name) {
-    impl_->deleteElement(name);
-}
-
-Counter&
-CounterDictionary::getElement(const std::string& name) const {
-    return (impl_->getElement(name));
-}
-
-Counter&
-CounterDictionary::operator[](const std::string& name) const {
-    return (impl_->getElement(name));
-}
-
-// Implementation detail class for CounterDictionary::ConstIterator
-class CounterDictionaryConstIteratorImpl {
-    public:
-        CounterDictionaryConstIteratorImpl();
-        ~CounterDictionaryConstIteratorImpl();
-        CounterDictionaryConstIteratorImpl(
-            const CounterDictionaryConstIteratorImpl &other);
-        CounterDictionaryConstIteratorImpl &operator=(
-            const CounterDictionaryConstIteratorImpl &source);
-        CounterDictionaryConstIteratorImpl(
-            DictionaryMap::const_iterator iterator);
-    public:
-        void increment();
-        const CounterDictionary::ConstIterator::value_type&
-            dereference() const;
-        bool equal(const CounterDictionaryConstIteratorImpl& other) const;
-    private:
-        DictionaryMap::const_iterator iterator_;
-};
-
-CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl() {}
-
-CounterDictionaryConstIteratorImpl::~CounterDictionaryConstIteratorImpl() {}
-
-// Copy constructor: deep copy of iterator_
-CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
-    const CounterDictionaryConstIteratorImpl &other) :
-    iterator_(other.iterator_)
-{}
-
-// Assignment operator: deep copy of iterator_
-CounterDictionaryConstIteratorImpl &
-CounterDictionaryConstIteratorImpl::operator=(
-    const CounterDictionaryConstIteratorImpl &source)
-{
-    iterator_ = source.iterator_;
-    return (*this);
-}
-
-// Constructor from implementation detail DictionaryMap::const_iterator
-CounterDictionaryConstIteratorImpl::CounterDictionaryConstIteratorImpl(
-    DictionaryMap::const_iterator iterator) :
-    iterator_(iterator)
-{}
-
-CounterDictionaryConstIteratorImpl
-CounterDictionaryImpl::begin() const {
-    return (CounterDictionaryConstIteratorImpl(dictionary_.begin()));
-}
-
-CounterDictionaryConstIteratorImpl
-CounterDictionaryImpl::end() const {
-    return (CounterDictionaryConstIteratorImpl(dictionary_.end()));
-}
-
-void
-CounterDictionaryConstIteratorImpl::increment() {
-    ++iterator_;
-    return;
-}
-
-const CounterDictionary::ConstIterator::value_type&
-CounterDictionaryConstIteratorImpl::dereference() const {
-    return (iterator_->first);
-}
-
-bool
-CounterDictionaryConstIteratorImpl::equal(
-    const CounterDictionaryConstIteratorImpl& other) const
-{
-    return (iterator_ == other.iterator_);
-}
-
-CounterDictionary::ConstIterator
-CounterDictionary::begin() const {
-    return (CounterDictionary::ConstIterator(
-               CounterDictionaryConstIteratorImpl(impl_->begin())));
-}
-
-CounterDictionary::ConstIterator
-CounterDictionary::end() const {
-    return (CounterDictionary::ConstIterator(
-               CounterDictionaryConstIteratorImpl(impl_->end())));
-}
-
-CounterDictionary::ConstIterator::ConstIterator() :
-    impl_(new CounterDictionaryConstIteratorImpl())
-{}
-
-CounterDictionary::ConstIterator::~ConstIterator() {}
-
-// Copy constructor: deep copy of impl_
-CounterDictionary::ConstIterator::ConstIterator(
-    const CounterDictionary::ConstIterator& source) :
-    impl_(new CounterDictionaryConstIteratorImpl(*(source.impl_)))
-{}
-
-// Assignment operator: deep copy of impl_
-CounterDictionary::ConstIterator &
-CounterDictionary::ConstIterator::operator=(
-    const CounterDictionary::ConstIterator &source)
-{
-    *impl_ = *source.impl_;
-    return (*this);
-}
-
-// The constructor from implementation detail
-CounterDictionary::ConstIterator::ConstIterator(
-    const CounterDictionaryConstIteratorImpl& source) :
-    impl_(new CounterDictionaryConstIteratorImpl(source))
-{}
-
-const CounterDictionary::ConstIterator::value_type&
-CounterDictionary::ConstIterator::dereference() const
-{
-    return (impl_->dereference());
-}
-
-bool
-CounterDictionary::ConstIterator::equal(
-    CounterDictionary::ConstIterator const& other) const
-{
-    return (impl_->equal(*(other.impl_)));
-}
-
-void
-CounterDictionary::ConstIterator::increment() {
-    impl_->increment();
-    return;
-}
-
-}   // namespace statistics
-}   // namespace isc
diff --git a/src/lib/statistics/counter_dict.h b/src/lib/statistics/counter_dict.h
index 1beff44..e288dfe 100644
--- a/src/lib/statistics/counter_dict.h
+++ b/src/lib/statistics/counter_dict.h
@@ -15,67 +15,83 @@
 #ifndef COUNTER_DICT_H
 #define COUNTER_DICT_H 1
 
-#include <string>
-#include <vector>
-#include <utility>
+#include <statistics/counter.h>
+#include <exceptions/exceptions.h>
+
 #include <boost/noncopyable.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 #include <boost/iterator/iterator_facade.hpp>
 
-#include <exceptions/exceptions.h>
-#include <statistics/counter.h>
+#include <cassert>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <map>
+#include <iterator>
+#include <utility>
+
+namespace {
+typedef boost::shared_ptr<isc::statistics::Counter> CounterPtr;
+typedef std::map<std::string, CounterPtr> DictionaryMap;
+}
 
 namespace isc {
 namespace statistics {
 
-class CounterDictionaryImpl;
-class CounterDictionaryConstIteratorImpl;
-
 class CounterDictionary : boost::noncopyable {
 private:
-    boost::scoped_ptr<CounterDictionaryImpl> impl_;
+    DictionaryMap dictionary_;
+    std::vector<std::string> elements_;
+    const size_t items_;
     // Default constructor is forbidden; number of counter items must be
     // specified at the construction of this class.
     CounterDictionary();
 public:
-    /// The constructor.
-    /// This constructor is mostly exception free. But it may still throw
-    /// a standard exception if memory allocation fails inside the method.
-    ///
-    /// \param items A number of counter items to hold (greater than 0)
-    ///
-    /// \throw isc::InvalidParameter \a items is 0
-    CounterDictionary(const size_t items);
-
-    /// The destructor.
-    ///
-    /// This method never throws an exception.
-    ~CounterDictionary();
-
-    /// \brief Add an element
-    ///
-    /// \throw isc::InvalidParameter \a element already exists.
-    ///
-    /// \param name A name of the element to append
-    void addElement(const std::string& name);
-
-    /// \brief Delete
-    ///
-    /// \throw isc::OutOfRange \a element does not exist.
-    ///
-    /// \param name A name of the element to delete
-    void deleteElement(const std::string& name);
-
-    /// \brief Lookup
-    ///
-    /// \throw isc::OutOfRange \a element does not exist.
-    ///
-    /// \param name A name of the element to get the counters
-    Counter& getElement(const std::string &name) const;
-
-    /// Same as getElement()
-    Counter& operator[](const std::string &name) const;
-
+    explicit CounterDictionary(const size_t items) :
+        items_(items)
+    {
+        // The number of items must not be 0
+        if (items == 0) {
+            isc_throw(isc::InvalidParameter, "Items must not be 0");
+        }
+    };
+    ~CounterDictionary() {};
+    void addElement(const std::string& name) {
+        // throw if the element already exists
+        if (dictionary_.count(name) != 0) {
+            isc_throw(isc::InvalidParameter,
+                      "Element " << name << " already exists");
+        }
+        assert(items_ != 0);
+        // Create a new Counter and add to the map
+        dictionary_.insert(
+            DictionaryMap::value_type(name, CounterPtr(new Counter(items_))));
+    };
+    void deleteElement(const std::string& name) {
+        size_t result = dictionary_.erase(name);
+        if (result != 1) {
+            // If an element with specified name does not exist, throw
+            // isc::OutOfRange.
+            isc_throw(isc::OutOfRange,
+                      "Element " << name << " does not exist");
+        }
+    };
+    Counter& getElement(const std::string& name) {
+        DictionaryMap::const_iterator i = dictionary_.find(name);
+        if (i != dictionary_.end()) {
+            // the key was found. return the element.
+            return (*(i->second));
+        } else {
+            // If an element with specified name does not exist, throw
+            // isc::OutOfRange.
+            isc_throw(isc::OutOfRange,
+                      "Element " << name << " does not exist");
+        }
+    };
+    Counter& operator[](const std::string& name) {
+        return (getElement(name));
+    };
     /// \brief \c ConstIterator is a constant iterator that provides an
     /// interface for enumerating name of zones stored in CounterDictionary.
     ///
@@ -90,67 +106,53 @@ public:
                                 const std::string,
                                 boost::forward_traversal_tag>
     {
-        private:
-            boost::scoped_ptr<CounterDictionaryConstIteratorImpl> impl_;
         public:
             /// The constructor.
             ///
             /// This constructor is mostly exception free. But it may still
             /// throw a standard exception if memory allocation fails
             /// inside the method.
-            ConstIterator();
+            ConstIterator() {}
             /// The destructor.
             ///
             /// This method never throws an exception.
-            ~ConstIterator();
-            /// The assignment operator.
-            ///
-            /// This method is mostly exception free. But it may still
-            /// throw a standard exception if memory allocation fails
-            /// inside the method.
-            ConstIterator& operator=(const ConstIterator &source);
-            /// The copy constructor.
-            ///
-            /// This constructor is mostly exception free. But it may still
-            /// throw a standard exception if memory allocation fails
-            /// inside the method.
-            ConstIterator(const ConstIterator& source);
-            /// The constructor from implementation detail.
-            ///
-            /// This method is used to create an instance of ConstIterator
-            /// by CounterDict::begin() and CounterDict::end().
-            ///
-            /// This constructor is mostly exception free. But it may still
-            /// throw a standard exception if memory allocation fails
-            /// inside the method.
+            ~ConstIterator() {}
+            /// Constructor from implementation detail DictionaryMap::const_iterator
             ConstIterator(
-                const CounterDictionaryConstIteratorImpl& source);
+                DictionaryMap::const_iterator iterator) :
+                iterator_(iterator)
+            {}
+
         private:
             /// \brief An internal method to increment this iterator.
-            void increment();
+            void increment() {
+                ++iterator_;
+                return;
+            }
+
             /// \brief An internal method to check equality.
-            bool equal(const ConstIterator& other) const;
+            bool equal(const ConstIterator& other) const {
+                return (iterator_ == other.iterator_);
+            }
+
             /// \brief An internal method to dereference this iterator.
-            const value_type& dereference() const;
+            const value_type& dereference() const {
+                return (iterator_->first);
+            }
+
         private:
             friend class boost::iterator_core_access;
+            DictionaryMap::const_iterator iterator_;
     };
 
-    typedef ConstIterator const_iterator;
-
-    /// \brief Return an iterator corresponding to the beginning of the
-    /// elements stored in CounterDictionary.
-    ///
-    /// This method is mostly exception free. But it may still throw a
-    /// standard exception if memory allocation fails inside the method.
-    const_iterator begin() const;
+    ConstIterator begin() const {
+        return (CounterDictionary::ConstIterator(dictionary_.begin()));
+    };
+    ConstIterator end() const {
+        return (CounterDictionary::ConstIterator(dictionary_.end()));
+    };
 
-    /// \brief Return an iterator corresponding to the end of the elements
-    /// stored in CounterDictionary.
-    ///
-    /// This method is mostly exception free. But it may still throw a
-    /// standard exception if memory allocation fails inside the method.
-    const_iterator end() const;
+    typedef ConstIterator const_iterator;
 };
 
 }   // namespace statistics
diff --git a/src/lib/statistics/tests/Makefile.am b/src/lib/statistics/tests/Makefile.am
index 007c8b0..25a3db2 100644
--- a/src/lib/statistics/tests/Makefile.am
+++ b/src/lib/statistics/tests/Makefile.am
@@ -28,7 +28,6 @@ run_unittests_SOURCES += counter_dict_unittest.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 
 run_unittests_LDADD  = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libb10-statistics.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
diff --git a/tests/lettuce/features/ddns_system.feature b/tests/lettuce/features/ddns_system.feature
index ee3187c..8e279a7 100644
--- a/tests/lettuce/features/ddns_system.feature
+++ b/tests/lettuce/features/ddns_system.feature
@@ -39,7 +39,7 @@ Feature: DDNS System
         # Test 5
         When I use DDNS to set the SOA serial to 1237
         # also check if Auth server reloaded
-        And wait for new bind10 stderr message AUTH_LOAD_ZONE
+        And wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE
         The DDNS response should be SUCCESS
         And the SOA serial for example.org should be 1237
 
@@ -53,11 +53,12 @@ Feature: DDNS System
 
         # Test 8
         When I use DDNS to set the SOA serial to 1238
+        And wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE
         The DDNS response should be SUCCESS
         And the SOA serial for example.org should be 1238
 
         When I use DDNS to set the SOA serial to 1239
-        And wait for new bind10 stderr message AUTH_LOAD_ZONE
+        And wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE
         The DDNS response should be SUCCESS
         And the SOA serial for example.org should be 1239
 
@@ -69,7 +70,7 @@ Feature: DDNS System
 
         # Test 10
         When I use DDNS to set the SOA serial to 1240
-        And wait for new bind10 stderr message AUTH_LOAD_ZONE
+        And wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE
         The DDNS response should be SUCCESS
         And the SOA serial for example.org should be 1240
 
diff --git a/tests/lettuce/features/inmemory_over_sqlite3.feature b/tests/lettuce/features/inmemory_over_sqlite3.feature
index 851caca..7d8ad6d 100644
--- a/tests/lettuce/features/inmemory_over_sqlite3.feature
+++ b/tests/lettuce/features/inmemory_over_sqlite3.feature
@@ -33,7 +33,7 @@ Feature: In-memory zone using SQLite3 backend
         A query for mail.example.org to [::1]:47806 should have rcode NXDOMAIN
         When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
         Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
-        Then wait for new bind10 stderr message AUTH_LOAD_ZONE
+        Then wait for new bind10 stderr message AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE
 
         A query for www.example.org to [::1]:47807 should have rcode NOERROR
         The answer section of the last query response should be



More information about the bind10-changes mailing list