BIND 10 trac1899, updated. b65d84011aa2cc1ae7a5b4c6f6b51380963fe7bb Merge branch 'master' into trac1899

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Nov 6 09:29:55 UTC 2012


The branch, trac1899 has been updated
       via  b65d84011aa2cc1ae7a5b4c6f6b51380963fe7bb (commit)
       via  432064bc3ec7c67797898a182c52add8f6012232 (commit)
       via  1ab996e4c54f1449b8e72cce411331193868e78c (commit)
       via  bf260bbc80a813c30bb7499b8a085d7390d83e83 (commit)
       via  f8261d10f177e52b19af56343ddb5e7548f30195 (commit)
       via  7bcaf2b7903ea5a51c2049579f6dc4670746527f (commit)
       via  fbc8d38199a95cc51704c40269d0c4c6625ec6b0 (commit)
       via  93e596c474ed1c637da7d6653eaa8c499b0b6a6d (commit)
       via  f037ebb23cb966f86d11c07bb0f4a80b6043c9b0 (commit)
       via  3a8ea5fd501d131e99ec02c4be10a874a94f2d83 (commit)
       via  86b1a2387a10ff8e5e646b117c289ebb2e9e57ec (commit)
       via  1d3b7eabbb5fa3b6674789428b36d7501768b635 (commit)
       via  41538b7ce9d6ab4131b572b79d84292884cf4a11 (commit)
       via  78dfd7dcd6d76cb75d288a3887e8b099c12f3080 (commit)
       via  b1d4ff896a7781d98513fab8d171090ac3d6a1e4 (commit)
       via  47c013b000db7235e1ab798eadebc3b42984b06e (commit)
       via  3d68f767731a682d4a80e9ba74d953502c2ada7d (commit)
       via  f802291f6461f3fa7e13fcf3364bb39b4be42c06 (commit)
       via  fa51dffbe444e5d2ac6a768da001b60ad16d0341 (commit)
       via  9cfcbb9475e1f42b5c8953a5a037b833edc5aa69 (commit)
       via  0c38d26f6acc7d156dc6c6871b4fa332e30728ee (commit)
       via  b89e44112425a8d5ab44c26ce988cfb08087ff7e (commit)
       via  98692b1e0663b4d167670b7808f26b1bb718851c (commit)
       via  1886c9f3083ce3e04d42b8a91e46838ee5a1e774 (commit)
       via  37fd8a72b131d5de9a6fbe612d3dc3d19bafdf83 (commit)
       via  1fb24f540ade83926d311316e77e1a6c8bef8432 (commit)
       via  c4c3946bad5bf1e9be0f375ac1029e1f95e94d94 (commit)
       via  5c975417200358c916a62415caf51a1a73e0d470 (commit)
       via  538a160ef48f7dbe927f75cafc335e470a82db33 (commit)
       via  e45115237b2c8c12f0c2fd3c62ae0aff01248cc9 (commit)
       via  a072ee8db55261a67dc7041c7c17d0536f8def0b (commit)
       via  473e340929d0869bf71d972e1a1843dc81b0fbfa (commit)
       via  94656c03d42d0339c79d10be8ccedaea6da55028 (commit)
       via  a6748eee324ebb4cf4ba99aa225df51d71458c81 (commit)
       via  5acf0ff36e8503de33ce04fe16e279ec6ee871f1 (commit)
       via  14680316801f9274e5b4d099a4dfbf63d7cb110b (commit)
       via  d25bb8d27cf235603cb8a1d789066b2aa993eccd (commit)
       via  f2b62e24805e97f66aeac4f9ab3db2d9778c9314 (commit)
       via  e0ce6d2f3010bfc5998e6c7da202258cbe28c6c6 (commit)
       via  d73842ed35d5aea29466e25a9a6b2dfb4e550e97 (commit)
       via  7eaa1760f3ed6cd57cf1b8458516fa3d457371d7 (commit)
       via  b7bff77c9dfefe339a386afcaeeb79b4927cfda3 (commit)
       via  ff7d15330f2a74d983972efd1872d1af7d2cd958 (commit)
       via  fc7462103f1fb344bf91707a1e2de99534e237b9 (commit)
       via  eace2551ac4260065fbd7d900d157e77dccc6e26 (commit)
       via  1ade0a0c0c8ead227aa7f8d374a1748de2535b15 (commit)
       via  e00795f47f7d88084973ff831278e4cf4004bfad (commit)
       via  9047de5e8f973e12e536f7180738e6b515439448 (commit)
       via  ae8740e0bf3dc8daf25323d442ef5ba135817caa (commit)
       via  ed5f58850ca6c58778ec3241968ada3bbca67096 (commit)
       via  26e55ce110123f7557732ecaeaaad4816cd0375b (commit)
       via  397769f9a994bdf1a2716ee89d6746769c11c13d (commit)
       via  b9b899343170f79cd26929245180180c850d5594 (commit)
       via  84d2f3799c493862a22e7b873352da9db766a19d (commit)
       via  a2bcf6e5b22df3c9bed473d342485548c60c8773 (commit)
       via  408728e4512f08c8e50e3f627cc46eb6ad38a315 (commit)
       via  2bed6220f561fa39358fb81fde8a2ee0a1b85c38 (commit)
       via  1943079713265d5ad64762b107288b6e501c70cf (commit)
       via  7a0a89d0cab063bb69e4ec4fede497f9d8d329ba (commit)
       via  f5811cdff640338babcf1ca0b4cfc7a06580a4f5 (commit)
       via  723885f4ab0096dbea3a21f63badb18d79f28814 (commit)
       via  76fa5523da1da9778a686b39dbba5759dcea34da (commit)
       via  0ca39bfe35f856eb54ff77c50f5b2230ed20313b (commit)
       via  52d245f139122e86c07a4336fbb2495bdb6499d9 (commit)
       via  8bd4cd42e061d49d098cb1387b3614226a161f6d (commit)
       via  dd5bb15644a8e6d0df49d93ee8e982d2d0d463ea (commit)
       via  09e793e3c71fbbc12f8f8461ceafbabc2edcbc9e (commit)
       via  936498565a5249df01ad44a808bb447952240c90 (commit)
       via  c74022e2d7acf6b77979e9c8b646d5da95419ed3 (commit)
       via  310150445a17a5043cb5e93ce5ecaddd0336b196 (commit)
       via  c4e44c1a57326915e7291e982e2271b6574f73fc (commit)
       via  6f29861b92742da34be9ae76968e82222b5bfd7d (commit)
       via  da7375d619b7ba63b77c8430f8d67799d8f26026 (commit)
       via  1ab6aafab6660d60a5f2e7cf7125d59d5807f2e5 (commit)
       via  ae20aca3d9324f9439baaf692f42db76ab15912f (commit)
       via  0f0501530b755d42670e7e8d9a044027c20e1f27 (commit)
       via  eff1c6bcb840aa9a8736c6c29a1eeb68eef30279 (commit)
       via  92daff7ee6bbd0a41b9a993a1d5d2b151984233a (commit)
       via  003b2878529f02062656b84187edb800bc292c35 (commit)
       via  362430efc09897c1e7d7bdf2bf7b4cffd9fd2e3b (commit)
       via  dfa822ebc063f5ae636e55e4f999bce3096483e1 (commit)
       via  26cd9b7104c48ecce553cab9c8e7de6f57c8e88f (commit)
       via  971e8bc5f615ebf009a4b1a60998151054ca34a5 (commit)
       via  dcde0c0b983dd0ce788d7273f0cf7e02b37a711d (commit)
       via  46c77cb971eeb52802fba8091529c9947bfda05c (commit)
       via  af3d7616a9f65bf3effb25d2432d0955dcb5d03b (commit)
       via  9b730d026d634dbefdc50eea9d63df77201e30f4 (commit)
       via  6bd433b573a427c85cbbd7c75dd1bb08f672d678 (commit)
       via  96da4b2e1aa8c82b1f33103a89d7bd8be8ca390b (commit)
       via  fa10a89c1e4b03da98f9926f31e1680cf5d4ef5d (commit)
       via  da0b581e22f7a10931767f4d535cfda1554cc525 (commit)
       via  e5bf564f734555173b5cae04757bc3f870f3463a (commit)
       via  6fad0e38082ed1078acb7add13321212dccdcf88 (commit)
       via  ac07f31fa2182962f4e958330fc460abeac350d5 (commit)
       via  34a2c9541c16578175eb928b9ea3f3c7755965d3 (commit)
       via  73b258b1b4284e1017aedacee9ed75301be23e79 (commit)
       via  8e51d2165fb35be71aee1a0c381fc71f08a821b5 (commit)
       via  718888317b9fb6b24c20d8d417ea540ff23257c5 (commit)
       via  ae65cad3fbe923329f674694614e0af560c37018 (commit)
       via  6de9ca4708fe4e56e17aedab38b89d7f76bf2515 (commit)
       via  354ae8184c3927c7afa0aef59297d1d03561d640 (commit)
       via  f3ab6e90b9c941913a2241c6ad78d943471a33d6 (commit)
       via  40c6c15c3f5a820c8d304d4a2b3529645d2cb368 (commit)
       via  07d2d30929b0734618e7c41ca27c8074cde51770 (commit)
       via  60b9a13fc0a03e38d198bf4e5491075792528aff (commit)
       via  8e0d333e76d72abef126f197a6caa00d6b7a10e3 (commit)
       via  c0672d18dc8709e15a175e781a5d690a281f6d19 (commit)
       via  28c929309ce6200a388e97b5cb89e81f2e061a6f (commit)
       via  a3a63ba5ca940d188dc28a4cd2db63d908ec3e1b (commit)
       via  7298821ace8f33cd683dc52856e7272e52eef589 (commit)
       via  8660a77ac739583b013c502a903ea9cdd979ae7e (commit)
       via  7e2522e4177ca26bd511395be02f35c96335bcd0 (commit)
       via  59c096cda4f3fbe9f3744c6cbe01497c909b4f06 (commit)
       via  d0a83e52167113c55010a44d69e8b540d9bbd00b (commit)
       via  6a0721cb9647ea09a7ca40174dee6de870829cc5 (commit)
       via  4735853efc9554536e607a3431631348ea2d0c49 (commit)
       via  f9a376ab22abb84377f24846f66b49c58469fe17 (commit)
       via  f569d3269831e8ad74e8f5c4d2e84d38e524c979 (commit)
       via  30ab0e64b78a7e634d037c7a885546ebce010da1 (commit)
       via  77b10c708212584290b7505d90cdcd110fc1637d (commit)
       via  46109eae81cb9768ea339ebd3bd53006ad276d56 (commit)
       via  f142802acfd65bb9cd993a254a99ff34ef731573 (commit)
       via  48b6e91386b46eed383126ad98dddfafc9f7e75e (commit)
       via  bfdb265f3ac48ec543de081bb02b79e78b8d7f9e (commit)
       via  2adcf03734b609514a665ce58315e71ddb110eb5 (commit)
       via  1edc3624622f3ca9f88c8b09e11d257e769b3172 (commit)
       via  c2a303e6d227217d56d249e132b873b37c36daa7 (commit)
       via  52177bb31f5fb8e134aecb9fd039c368684ad2df (commit)
       via  dfc79b64ed8fdf77c46ebea1e025e229b7497a60 (commit)
       via  c714ebe32337bd89ba5209547fc759810c15dd19 (commit)
       via  c7060ccf5e4ec41ee12e08e66f410fe14199c545 (commit)
       via  10da91d4dcb3baf1b040864bf60cd89d82a80e8f (commit)
       via  730fd2f2de14300227720977b581c421088dfec7 (commit)
       via  55f7ce059f925a531c9f0fe9adad18c1a941dec6 (commit)
       via  64e40094a730e99da4b24bbe97cb346af7b8fbcd (commit)
       via  42dc5ad3e7eed5f53d2d38b6683b7a9b427c1a1d (commit)
       via  09ccd7c1ec01fd8d30270b2d8ee37a4e5e4962b1 (commit)
       via  050a4704b903ba8e7d0b2e4736f4d1972f0319b4 (commit)
       via  8ad666fca65695efbcb90d656e089aa3ae314590 (commit)
       via  e5222941c0cc25a5580eab58390dcf62ea2278a8 (commit)
       via  51cbca9e606137fcdad96a99824f27809701afcc (commit)
       via  12d5f67f3e58ab09f8369b4218baa2c8a40e2b9a (commit)
       via  1830f45cda873932566b1eacfb15f5f6170cf916 (commit)
       via  25aae9d23312a6c808bbbbfcefbafe3dc31cba3f (commit)
       via  593cc7dda0802634b12373f0215b6033e1791118 (commit)
       via  736bcacc37bde8182160fbf5b4c8191a1846373e (commit)
       via  e7d7b0180edf1cf28316bfb0b2e2ccb8e8593eb9 (commit)
       via  06651f1b45157e097a20de4ad5139652d6f388c9 (commit)
       via  8c2ea7566498c8b3f826a85316ae63f0d57e80b0 (commit)
       via  babcf09173d27ad28f53d911d1908c7375fd4e22 (commit)
       via  ab51eb2f65a390b98b5601f8e7bf5bd6634e4ad6 (commit)
       via  493a2b3d0b3c494aced54e2778595618ec6f5100 (commit)
       via  02dd6ed0dd458e7627e06bcab6a5168354e929d6 (commit)
       via  bbf34f3ba16d91fabcfa53a5b989e999d9c19875 (commit)
       via  237def8395cbe9f2d1193e804443c403294b8c6f (commit)
       via  1a7e6f8b2d18a98bfac5f4a47f55f8f288e4d0ab (commit)
       via  6e3d1a58e4ec1857fc0264e8070ce67f569b7e42 (commit)
       via  076e5cff040e4da53df8dd60cee37a06e4e557c9 (commit)
       via  ac98e5dbf31b54eb74917d03e53ac071e8c8c92e (commit)
       via  79848f3dcca94147df133b8969c5c1aa0fddc299 (commit)
       via  022f55634bf4865a315797abafedfbe43d54603b (commit)
       via  6c192e5c0903f349b4d80cf2bb6cd964040ae7da (commit)
       via  89476cd22065c79ba1c7dd48a29f0fc958917b39 (commit)
       via  00bbd50bb77e798708891f177003ad1d01bf9dbe (commit)
       via  e016cdf94cb3377f4e186e4f175ef89eb382089f (commit)
       via  4a8a4d185ddee401aeed95f9f51d443d725c4d5f (commit)
       via  117a2ea8230671c17c8011cdd2f0b747eadbec01 (commit)
       via  f759086adfb6c899be7a2201f063af49e88f60b7 (commit)
       via  c60790ce9bd68bb155ea77f5f6629f9f754ef9c7 (commit)
       via  07b53c2297ed4bbb167fda55b43b70575208f0d3 (commit)
       via  e13c2404bea730239b16f5b74142e7364fa739aa (commit)
       via  5c7990cd039e4350cf2823e8edc10b509d6d8ba7 (commit)
       via  0b9f5f6d4afd45a3482ca0d4aec22f5d00df1865 (commit)
       via  360b20fadb0530d2cb069f428c09fa6320d941ad (commit)
       via  6c447559f19adc0e040a6a6f44c06dffcf3ce0ff (commit)
       via  6978e7fbdfb9c036e1f0fedf04828d6edc731d9e (commit)
       via  b3419fdfa017ef3fd88aae1fc800ebb72ceaad02 (commit)
       via  d570aa48262648cfccdf96e01b1c7c66a9d0839d (commit)
       via  85c7fb2e4627e84c2d17c59432509a221034e6ea (commit)
       via  40d4887c7d706c0a27b1aef720f2a8870d26d5bb (commit)
       via  58388f2e53b8428f0ecb5936d9d6c076b2003203 (commit)
       via  208dbbaf77e781a19d6efdaca32d423ea266a6f1 (commit)
       via  f406d515e8ee18165740fbba5db95789e2cd2703 (commit)
       via  26f07019946b185e191ce7490f4c398b830f8420 (commit)
       via  5633225648b2a4a5582327131f9ca7a7d83b340f (commit)
       via  8a1be35939e817930570e4afc76af240e78c2602 (commit)
       via  d98f7060780fc89f05c1610d3e88403969dfc819 (commit)
       via  d0a5d03e219ccb645894c8afe41e1bd548ff83e1 (commit)
       via  08162d5b7ef6b101030b71ac0553b237a7c64bd9 (commit)
       via  1f486ccd690d8267b0a4410d8163298c5f5cf1ef (commit)
       via  6881cd3cc4fe062b1a067d0481c986eb6603a689 (commit)
       via  91311bdbfea95f65c5e8bd8294ba08fac12405f1 (commit)
       via  0f3f6b1f3cdc54176c1abf4b22bbdfe4dbae0a5c (commit)
       via  de626b3c2aec2bd7896e75c5ae14337b03342729 (commit)
       via  56aa815ad1200fb8b2901634b6004bbcd0eb5331 (commit)
       via  825d75f5c66467173d0c0f7333bed1ba47857823 (commit)
       via  d863d2f26c9e2fb9a866d56de15723cfd5a9e58b (commit)
       via  efeb8e81af78b9bca08785646948da46aec3d1c8 (commit)
       via  eb5ace77a9c1ee6ce93d827080624d566eaf572c (commit)
       via  f6f8e26e272ee0d94c36bf2be23e005bd74c55ea (commit)
       via  5b6cccfc2e34d0319e8c5358391de6fe9a5dcdf4 (commit)
       via  56a18b6b8dc444efeec0052eefc4539633e9a762 (commit)
       via  21f612a9f5ec269d4ec4b1103b17857924cb2055 (commit)
       via  aa53f0f7d8c06ea9dd7082971d080895a56d6c14 (commit)
       via  c13d8c76021cb27bf9d5f0bf899b267ca04caa21 (commit)
       via  6a1a234386523e5be05adf0a6ac51df29bf3b7a6 (commit)
       via  d82d52518ab96e3c7aece6c2c4ebcb599b993770 (commit)
       via  1841fbb5bc6e588c311f22360bb59462a244c644 (commit)
       via  37038754b11cb07ea6dfc8291586a5a5e6c4d5f8 (commit)
       via  2538cdc2251cc27c38193680af90f2e7e94ffb57 (commit)
       via  9b8b55c00ba7bba8e4f95336eb2bb34c0b151efb (commit)
       via  0a72b7f58d8d2ab06e4290443cf6adb4f4eef540 (commit)
       via  e0ffa11d49ab949ee5a4ffe7682b0e6906667baa (commit)
       via  25a4afa2395d6d9263b385a73cec2919c4a21830 (commit)
       via  e5f7c1b89ed635155aa022f6a14285657b152a1c (commit)
       via  e05f1d87c3764033b920fa8a227cfd05dc3ca3b8 (commit)
       via  5fe90d260706fdac0ed0678233e116213520c575 (commit)
       via  559395e392e41dd929368d820f83f58555c5e319 (commit)
       via  5f39840cf5f96ee91f826aa801d934864a55d444 (commit)
       via  0fc1237c7ba1edd9128387ba1db1bae9e7aacc27 (commit)
       via  d1d08475aec2b0f3c45fa35db5ad629ab14dfb26 (commit)
       via  45e74198ebeee9d0e2eee780185d91c20fa63513 (commit)
       via  c200bbcb68071fb2148344d629c5605498bf0656 (commit)
       via  5b1952368e65ab130340c46d5e0f3bb72a343993 (commit)
       via  48df8a1486f00e27636febfa3487ddab9a051ca6 (commit)
       via  668cdb357976bea8bc0b775459324b68dd2906b9 (commit)
       via  179247735be1594337d07f0761b5fa8af2f44dc2 (commit)
       via  af6832161c3fc10de0239da3ddb47e7261413615 (commit)
       via  704457924e2a892e012b7c750bfe08fe56da7128 (commit)
       via  2fee008d989b5c60c13dbe86729e8a1ee8ac6456 (commit)
       via  396bb7832e661dc597853e9f062e7094a209baf0 (commit)
       via  c33327c47f10065f09cc32bcd93b1e7f5612e5d8 (commit)
       via  e7bd7d6ceafd9ba44c953835b6bea33d5f54b32c (commit)
       via  55eaa4303d7bc462286b4a6b7f262976f1a346bb (commit)
       via  f4663e8cab5485f19069ddb80fef073a6705c105 (commit)
       via  ca9f73e098180f9d5d188f1d1bd46031f9b81ce3 (commit)
       via  055a8352580a73069b0745d3fbddec1c488f9e59 (commit)
       via  3aeb78fd7a34c736a4ba3b985ef6a0f8432bfafa (commit)
       via  01244a662e1ad9387e27c2f5e1723c954d47a12b (commit)
       via  97e21bca2ddfc0ceb7350b741efae2c4199383ad (commit)
       via  82225f72c3af83d8ca5e2b33b0e93d32b1e2fdbf (commit)
       via  a61af89b9799857a955e5e0b7b04b1f273dd63ab (commit)
       via  6f25929f1cfee95e92142cdf8ce42fe5f2a8aae6 (commit)
       via  b4913f7bf1a1b4c11c0b614d9a7516d4ab629776 (commit)
       via  6bf4ff4eb4a3a8095bdd5d622cab24f46a0d9834 (commit)
       via  2fdacd86e25c075a059e81274a1531ed6f0b787c (commit)
       via  4f202471b02965f0bd3c402d1bffa503c5ee6527 (commit)
       via  7f5343e069bf811dc88bea5fad759bbe7fe4f78c (commit)
       via  6ec1f3e61319b6da0ed8567bc53c1c5cf7455c9a (commit)
       via  f4c07bf162d7ed4495496f7574242ccd2add5926 (commit)
       via  40ee3e7167ec6660de797955af57ec9f5ec67c79 (commit)
       via  3690baaf0660af752b00a370fadcd60859c50ca4 (commit)
       via  704ca46ac3aa9c93c9964eb3495bacd4050f042e (commit)
       via  3d13f1625ffb635f629944e8a1c0e793804d0112 (commit)
       via  055d0ff12778c3b37b2060900cfacf59dcec2767 (commit)
       via  483011e9e126f02863fc10c69278f55f204b5f17 (commit)
       via  4de75117f84ad23f96392677338e733d6e1e87bd (commit)
       via  1ed45f9349d76439f61d9ab88c0ce1ea84cc6711 (commit)
       via  5a0aa214c3e6b52ef738ca574105bf0120ac844b (commit)
       via  b97e136dad599abde963d6971494d5838e1c6fbd (commit)
       via  7ca65cb9ec528118f370142d7e7b792fcc31c9cf (commit)
       via  280d0604dbf569d0aeb3065cae12cd38876e3e5c (commit)
       via  5b3b1ee480cb8d47477972118f7ce03751699359 (commit)
       via  ee538e573d559fc8912465fa8161cd975287e807 (commit)
       via  6419f929bd1e7eb951fe1c0c0debc7e8ae30a59a (commit)
       via  e9c984fb457ca225f77f8ce466e129670109169c (commit)
       via  d26a99607b8d1e1b861de188c321e869ebb92cd1 (commit)
       via  163a1a31471bea170a85b2e158f66c9f439bb2e4 (commit)
       via  f802f3ba317e49c1c1d16134855eb1b3c1aaeec8 (commit)
       via  1b10623a2bdc55e002defbd36eeba3f0d6f9d656 (commit)
       via  fa04b8777b196e3094f8b351c7f3d1ebf56fdd7e (commit)
       via  3ef4699944709ab91f856396c76b90f3c97df3ac (commit)
       via  950c72ccede294b7692bb7bdb20ef1bc28c3cc76 (commit)
       via  eee7c05de1c33b0d932ebbaca4370129d315e0ba (commit)
       via  5ba139c6882f7399449eafdefb2202578fbe2903 (commit)
       via  e5ad751d8372bb0306d4eb5b8311a7b9cb7c7c32 (commit)
       via  a9fd37df7cba2f48a2f4e42cc0d1f26b991b5636 (commit)
       via  fd8264e864303a947addc8f788b31ae7c7ea4c12 (commit)
       via  65253b21b0d15b2608655e90a129efbe1de24d07 (commit)
       via  5261f939f525186b89099aae541f7042e42c54ba (commit)
       via  a16ecb53a5c69269e749845e708215d5d3c75c31 (commit)
       via  c2a477d54ed891c84fb2218831d0d7950a91fdb4 (commit)
       via  56df3cb883631fc189fde26781f5d01695fcd386 (commit)
       via  6b8528fd0d34b49c44f9da2b2ce2127399fcca34 (commit)
       via  cef74673072e543a06bdc487afcad8787a811908 (commit)
       via  1b35c2aba35663034479b293e238deeff068dced (commit)
       via  8b356c0c7202c02151a882cc7676803bcaf90b75 (commit)
       via  f985b514e1b9a338b3940928415396c6a87dbce1 (commit)
       via  e1ae6846339d8ce57f61a56d1fccf00d92ef910b (commit)
       via  ceeaa8ae96db786b65715e0cb440a411fbab8b2c (commit)
       via  8d093113af5d33b4c7841ef7bc003d29c5d1268e (commit)
       via  4ff65533b470925688c8c5b35af7afcacbf0cdf6 (commit)
       via  af808fad3a5d1de608b799a0e7491ac8eacac8ff (commit)
       via  d2a8a10ffc18ab3c57e4008d72fe505b62c25432 (commit)
       via  3148cf2b018b222e263894b80cd7e92b20c86ceb (commit)
       via  5c5f0831bee890e2245339d6f29b03ff33f94f9f (commit)
       via  d26707201f5c67cae4b3439ef63809da41e8d166 (commit)
       via  3dc84c3cc22866efa60bb7a2701c50b05049971d (commit)
       via  7feae4486ab8b722cdf897bcf629391e7c15fcf1 (commit)
       via  7bedb1f498bf26780a3262a47b7850f9b92288bc (commit)
       via  9a655fdd02d00a22003c6f7c9902ef80072a2332 (commit)
       via  a258bfbbb515f7bd3d336d14e89f2dd58412e807 (commit)
       via  8015bd0bde0d4adccf7f4fc66423156cc9504011 (commit)
       via  f92a3289255ec12a39e9d720398b72ee97197991 (commit)
       via  e294cb85d5bf15e7036a0b1c4ae659513be6a101 (commit)
       via  4b3d83b7b5cf42576081426ab8aaeb7e545640a0 (commit)
       via  8ab3a360bb68b257c5a9bd363ce28343e409684a (commit)
       via  1c9fa8a0b9ea4d328bbb6325e94ce1af95d6e99d (commit)
       via  8bc72c0e77a786901f61f3fab7aa0074aa1c5b70 (commit)
       via  583710591b610e704fd5739be4a4106917e35e9e (commit)
       via  94f1fcdbcfb0cfceaff0e850df3bdd190efb73ad (commit)
       via  54ad8dd54acc965dc75513b739d604e74b4a11ee (commit)
       via  64ce8ec302fd2f7afb3ece6b74e4a9b4ce127a7d (commit)
       via  14727f8945873937f353915eac0e918cdb58721f (commit)
       via  8240c04e9bf91ce4d26f8f55d23216f7e48b3fd8 (commit)
       via  40fe836dcfb4fb1b37152bf9078be0351e6eea21 (commit)
       via  a4eaf309a64c5eb108653efe6796af54f4da7337 (commit)
       via  b117859663685e898c085750bc7a0908dc42e489 (commit)
       via  5784101c08e8318a6e88bea598cf2a7ac5a389c3 (commit)
       via  b8f538b9fcf70394e5737269d4bed660721c245d (commit)
       via  63e83a8f9c1ab9f7b32b76f80e65a771ee302cee (commit)
       via  328b5fc4dbbafe7a5286d857581505a7cf55ea0e (commit)
       via  10018747ad68736c31da97ec1ad7c5eef94489a0 (commit)
       via  d0a412aeddad9a64649eb28a3a5ad1003aa97d94 (commit)
       via  9ec629f3465d8651ceaf5b766fc91598e26f7023 (commit)
       via  d06e139eb2d03ef254ab8b2f3f56083addcfe756 (commit)
       via  b169803e955bf7c5647f4c67bd851b2a9a03630c (commit)
       via  d047c08a96270d4d757dc4c0bcd4dfece3bf586a (commit)
       via  60c7cde711da05825672e5b8601ff596993a030b (commit)
       via  88871270308be3aaaeb8918c29c2b2c3ca4286e5 (commit)
       via  fee99a5bb5b064ee62866f93b75b22c99905dc05 (commit)
       via  436f507ae4e387f7fe5e242864500a6839bfc32b (commit)
       via  054b2a99a4e527a15739c0e6a9a0247bb23a5d1a (commit)
       via  9eeacfa616c813189a516f343c320d65ed9bfc7d (commit)
       via  9d974a35bb76a63c7d8749d5728612ab0ef83c98 (commit)
       via  71bfb14495829b1e03c79a03e6a1cd44c4d3a1b9 (commit)
       via  f0d4fcf168a840786ff9172ae23402920c85f3de (commit)
       via  dfc2f2b4dd181378d493aad1c53c8fcc53ba8152 (commit)
       via  5a493720575e76a80ed6bba5cfcd2fac01364ce2 (commit)
       via  869d1d20492f5221714d2fbf7c39bef800a28e60 (commit)
       via  a65bda660f985ff422b1528b1f7654ac254c6a79 (commit)
       via  5ffdbdff90641201be36ab0ee65773df2ef6e0dd (commit)
       via  b90b048ba805efc450026c5192bb09aa7668d52f (commit)
       via  eb64f1c38d521886fef599582b4c80e892e31341 (commit)
       via  6f4b4413fad5433ff41cd2b51f4784cbedfa859b (commit)
       via  55f047a3e275e54e45a027631d1bd7e900b661f7 (commit)
       via  7e5dbd9146f8c9a27937b733a63116d25865cf45 (commit)
       via  4a78f86fffe147322f65b581a89d834b50999f39 (commit)
       via  ac96bd4de08738f779e79cc9cd15c88a63fb32bf (commit)
       via  91a32d4c23fdb0780a78cae0fe34cec2ef64cffb (commit)
       via  38081a3eb213dad9b2233f37e8c8f499a9e50f1a (commit)
       via  350fa8a59935fac90f02e86a28d81b4941cc7ee3 (commit)
       via  06bf41c4a9c79e450e78f3eecac362dd0b18d06c (commit)
       via  467078e47174e5a3d20caa8b1a1c8c0b35b0876f (commit)
       via  faa94e73318b57601aa9bf857387863c9be72a6b (commit)
       via  f8b99e43fcc928eccd9b30fd187d91e92782ee02 (commit)
       via  4c9e89ec998fb375031f14929775074aa1d9c903 (commit)
       via  e7ecb231083afee21757b37c4128fd222a6e93a5 (commit)
       via  a96a4dc64fe455a2f2e8a1fd2f3a66e251d6c1a3 (commit)
       via  68621232d6840227c4309c1fd1ee9e1a8b26120f (commit)
       via  c9bae4745f58fc361ebeb0f91bb15b288e96e5cf (commit)
       via  beccbfe1acb5e6e309d5dee9cbc4461434091520 (commit)
       via  daf7bbcc05e24e2e6894ac2cf59fa738cfb199bc (commit)
       via  7230b23ea3aa18eb74d7f3eb705ef1f9ff0cee01 (commit)
       via  d1c7c9057bb24595f9dcefe226dc7f326f390486 (commit)
       via  a7a5b8a6e3605e3a3788f6920a45e9fa3c215be0 (commit)
       via  eda12494283d717f4fb095be4ff57608f4003502 (commit)
       via  a66bc5d487b4b095bb9e322e33ed31c55d8af339 (commit)
       via  c16a30cbaf082a05c0fbd5c929bf0be6f007503f (commit)
       via  5da8f0ce275404a225759f9673daffa4d9da386a (commit)
       via  b1b7f7957f6ae64bf1621368548127a4ccf5acc3 (commit)
       via  e9498651c8bc59ba17f353f196c0675dd7b8e2ff (commit)
       via  9967f37261be7acaecff0dc8d3fe6b1fc49fc4d0 (commit)
       via  7ece465effe5156585f24bb60e836211c8aacab0 (commit)
       via  6517287182efdd74a30184c4806634b0de5ef049 (commit)
       via  f13266d9802fa12a8bf223d94b6fe97923ca384a (commit)
       via  861b38913123f90f4ab68d105d1111b2e42a6481 (commit)
       via  f07fdbfa1a7d0f592c0b3d9e1a10e287dc608dd1 (commit)
       via  a897e5cf38d32a41107f537bc8dace96813b06d8 (commit)
       via  c0d2f860c8125033c4433b801559b363702f231d (commit)
       via  f1f86f353832e37b7987756bcba5071a5ef7101e (commit)
       via  e8a8a442cba80d9c9f2c0a7df1db981ccd195791 (commit)
       via  803c67a839d109dea0b040ab2471a27ad995b419 (commit)
       via  7028610344ad69d3e5da57575da3e2e85b257d0e (commit)
       via  6f8add1167cb4781cc2f84d8c65ee5db73846ab8 (commit)
       via  c3c1915586223bb1db2114ad5b2bd2189f632517 (commit)
       via  abd88f16af1295f2ff67f5e9593e61d1539ef6e4 (commit)
       via  1f5426f7eddcaea4742fe9537fa957534b254fe6 (commit)
       via  8bed689cafee235c4204c3ddeb980251bf6a9898 (commit)
       via  dbe150944902bc1de9f1aae199e7d55866d96d29 (commit)
       via  40e0500ef5b5b39e698bf7c0ab6f8872cfdf1d07 (commit)
       via  a3e7cc6cf85981ed5a8a5e63589dfe1767b01e64 (commit)
       via  382204ee795971bf6daeebb95f6134bab10ca18d (commit)
       via  e25a0b2202650ef749340a1993b427bf99d962e5 (commit)
       via  7cd33029c63a0cd0889dba94709cf6cadaea48ab (commit)
       via  c0128e80648ec83fb9f4e3cbf3bb7bba2ef0bbb5 (commit)
       via  b52c3d66c1462da3733725d2baf40a21b8f521d6 (commit)
       via  10a4833ef74f0cf9aebc3861aa69cae6a25ce1cb (commit)
       via  35d60f15a86733bb3bc9db77b3d8c7713d83d70d (commit)
       via  ff7d2f5f77fab130de8db0d59ce48a483a0a8b45 (commit)
       via  99767bbe7b2a278ffa88086c24dcd6363d2ed0e5 (commit)
       via  3559ddcbe67e91941139c8c74ed77bb9be501dc4 (commit)
       via  53ea5695fbd2a596ffda7c9b7bf0dd503b101462 (commit)
       via  ec9e57a32cefe7db94c2b8b8bebc782b8e872fa9 (commit)
       via  88b2af9980fb431eb97dc72cb39214d7e1700ab7 (commit)
      from  b0817d56bbf3bee5c476d83bd7c5695128e94fdc (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 b65d84011aa2cc1ae7a5b4c6f6b51380963fe7bb
Merge: b0817d5 432064b
Author: Mukund Sivaraman <muks at isc.org>
Date:   Mon Oct 8 22:56:20 2012 +0530

    Merge branch 'master' into trac1899
    
    Conflicts:
    	src/lib/datasrc/sqlite3_accessor.cc
    	src/lib/datasrc/sqlite3_datasrc.cc

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

Summary of changes:
 ChangeLog                                          |   90 +-
 INSTALL                                            |    4 +
 configure.ac                                       |   40 +-
 doc/Doxyfile                                       |    5 +-
 doc/guide/bind10-guide.xml                         |  112 +-
 src/bin/auth/Makefile.am                           |    7 +-
 src/bin/auth/auth_srv.cc                           |   31 +-
 src/bin/auth/auth_srv.h                            |   35 +
 src/bin/auth/benchmarks/Makefile.am                |    7 +-
 src/bin/auth/command.cc                            |    4 +
 src/bin/auth/datasrc_configurator.h                |    3 +
 src/bin/auth/tests/Makefile.am                     |    8 +-
 src/bin/auth/tests/auth_srv_unittest.cc            |   41 +-
 src/bin/auth/tests/command_unittest.cc             |   76 +-
 .../auth/tests/datasrc_configurator_unittest.cc    |    5 +
 src/bin/auth/tests/query_unittest.cc               |   17 +-
 src/bin/bindctl/bindcmd.py                         |  131 +-
 src/bin/bindctl/bindctl_main.py.in                 |   49 +-
 src/bin/bindctl/cmdparse.py                        |   36 +-
 src/bin/bindctl/tests/bindctl_test.py              |  210 +-
 src/bin/dhcp4/dhcp4_messages.mes                   |   10 +
 src/bin/dhcp4/dhcp4_srv.cc                         |   14 +-
 src/bin/dhcp6/dhcp6_messages.mes                   |   10 +
 src/bin/dhcp6/dhcp6_srv.cc                         |   14 +-
 src/bin/msgq/msgq.py.in                            |    4 +-
 src/bin/stats/tests/test_utils.py                  |   13 +-
 src/bin/xfrin/tests/xfrin_test.py                  |   48 +-
 src/bin/xfrin/xfrin.py.in                          |   25 +-
 src/bin/xfrout/b10-xfrout.xml                      |   26 +-
 src/bin/xfrout/tests/xfrout_test.py.in             |  211 +-
 src/bin/xfrout/xfrout.py.in                        |  119 +-
 src/bin/xfrout/xfrout.spec.pre.in                  |   16 +
 src/cppcheck-suppress.lst                          |   15 +
 src/lib/acl/dns.cc                                 |   23 +-
 src/lib/acl/dns.h                                  |    4 -
 src/lib/acl/tests/dns_test.cc                      |   14 +-
 src/lib/asiolink/io_address.h                      |   55 +-
 src/lib/asiolink/tests/io_address_unittest.cc      |   32 +
 src/lib/cache/tests/rrset_entry_unittest.cc        |    3 -
 src/lib/cc/data.cc                                 |    8 +-
 src/lib/config/tests/testdata/Makefile.am          |    1 +
 src/lib/config/tests/testdata/spec32.spec          |   16 +
 .../tests/testdata/{spec24.spec => spec42.spec}    |    5 +-
 src/lib/datasrc/Makefile.am                        |   13 +-
 src/lib/datasrc/cache.cc                           |  396 ---
 src/lib/datasrc/cache.h                            |  223 --
 src/lib/datasrc/client_list.cc                     |   67 +-
 src/lib/datasrc/client_list.h                      |   58 +-
 src/lib/datasrc/data_source.cc                     | 1434 --------
 src/lib/datasrc/data_source.h                      |  362 --
 src/lib/datasrc/database.cc                        |   16 +-
 src/lib/datasrc/memory/Makefile.am                 |    4 +-
 src/lib/datasrc/memory/domaintree.h                |  118 +-
 src/lib/datasrc/memory/memory_client.cc            |  421 ++-
 src/lib/datasrc/memory/memory_client.h             |   27 +-
 .../testdata/example.org-rrsig-name-unmatched.zone |    6 -
 .../testdata/example.org-rrsig-type-unmatched.zone |    6 -
 src/lib/datasrc/memory/treenode_rrset.cc           |   39 +-
 src/lib/datasrc/memory/treenode_rrset.h            |    2 -
 src/lib/datasrc/memory/zone_finder.cc              |  597 +++-
 src/lib/datasrc/memory/zone_finder.h               |   38 +-
 .../memory/zone_table_segment.cc}                  |   44 +-
 src/lib/datasrc/memory/zone_table_segment.h        |  110 +
 .../memory/zone_table_segment_local.cc}            |   47 +-
 src/lib/datasrc/memory/zone_table_segment_local.h  |   66 +
 src/lib/datasrc/memory_datasrc.cc                  |   11 +-
 src/lib/datasrc/query.cc                           |  116 -
 src/lib/datasrc/query.h                            |  255 --
 src/lib/datasrc/sqlite3_accessor.cc                |    7 +-
 src/lib/datasrc/sqlite3_datasrc.cc                 |  919 -----
 src/lib/datasrc/sqlite3_datasrc.h                  |  130 -
 src/lib/datasrc/static_datasrc.cc                  |  275 --
 src/lib/datasrc/static_datasrc.h                   |   95 -
 src/lib/datasrc/tests/Makefile.am                  |   19 +-
 src/lib/datasrc/tests/cache_unittest.cc            |  340 --
 src/lib/datasrc/tests/client_list_unittest.cc      |  151 +-
 src/lib/datasrc/tests/datasrc_unittest.cc          | 1209 -------
 src/lib/datasrc/tests/faked_nsec3.cc               |   17 +
 src/lib/datasrc/tests/faked_nsec3.h                |    4 +
 .../{memory/tests => tests/memory}/.gitignore      |    0
 .../{memory/tests => tests/memory}/Makefile.am     |    5 +-
 .../tests => tests/memory}/domaintree_unittest.cc  |   62 +
 .../memory}/memory_client_unittest.cc              |  197 +-
 .../tests => tests/memory}/memory_segment_test.h   |    0
 .../memory}/rdata_serialization_unittest.cc        |    2 +-
 .../tests => tests/memory}/rdataset_unittest.cc    |    0
 .../tests => tests/memory}/run_unittests.cc        |    0
 .../memory}/segment_object_holder_unittest.cc      |    0
 .../tests => tests/memory}/testdata/Makefile.am    |    3 +-
 .../tests => tests/memory}/testdata/empty.zone     |    0
 .../memory}/testdata/example.org-broken1.zone      |    0
 .../memory}/testdata/example.org-broken2.zone      |    0
 .../testdata/example.org-cname-and-not-nsec-1.zone |    0
 .../testdata/example.org-cname-and-not-nsec-2.zone |    0
 .../testdata/example.org-dname-ns-apex-1.zone      |    0
 .../testdata/example.org-dname-ns-apex-2.zone      |    0
 .../testdata/example.org-dname-ns-nonapex-1.zone   |    0
 .../testdata/example.org-dname-ns-nonapex-2.zone   |    0
 .../testdata/example.org-duplicate-type-bad.zone   |    0
 .../testdata/example.org-duplicate-type.zone       |    0
 .../memory}/testdata/example.org-empty.zone        |    0
 .../testdata/example.org-multiple-cname.zone       |    0
 .../testdata/example.org-multiple-dname.zone       |    0
 .../testdata/example.org-multiple-nsec3.zone       |    0
 .../testdata/example.org-multiple-nsec3param.zone  |    0
 .../memory}/testdata/example.org-multiple.zone     |    0
 .../testdata/example.org-nsec3-empty-salt.zone}    |    8 +-
 .../testdata/example.org-nsec3-fewer-labels.zone   |    0
 .../testdata/example.org-nsec3-more-labels.zone    |    0
 .../example.org-nsec3-signed-no-param.zone         |    0
 .../memory}/testdata/example.org-nsec3-signed.zone |    0
 .../memory}/testdata/example.org-out-of-zone.zone  |    0
 .../example.org-rrsig-follows-nothing.zone         |    0
 .../memory}/testdata/example.org-rrsigs.zone       |    0
 .../testdata/example.org-wildcard-dname.zone       |    0
 .../memory}/testdata/example.org-wildcard-ns.zone  |    0
 .../testdata/example.org-wildcard-nsec3.zone       |    0
 .../memory}/testdata/example.org.zone              |    0
 .../memory}/treenode_rrset_unittest.cc             |   58 +-
 .../tests => tests/memory}/zone_data_unittest.cc   |    0
 .../tests => tests/memory}/zone_finder_unittest.cc |  351 +-
 .../tests/memory/zone_table_segment_unittest.cc    |   83 +
 .../tests => tests/memory}/zone_table_unittest.cc  |    0
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |   66 -
 src/lib/datasrc/tests/query_unittest.cc            |   71 -
 src/lib/datasrc/tests/sqlite3_accessor_unittest.cc |   10 +-
 src/lib/datasrc/tests/sqlite3_unittest.cc          |  950 ------
 src/lib/datasrc/tests/static_unittest.cc           |  422 ---
 src/lib/datasrc/tests/test_datasrc.cc              |  657 ----
 src/lib/datasrc/tests/test_datasrc.h               |  111 -
 src/lib/datasrc/tests/testdata/contexttest.zone    |    6 +-
 .../datasrc/tests/zone_finder_context_unittest.cc  |   39 +-
 src/lib/datasrc/zone.h                             |  130 +-
 src/lib/datasrc/zone_finder_context.cc             |   17 +-
 src/lib/dhcp/Makefile.am                           |   14 +-
 src/lib/dhcp/README                                |   14 +-
 src/lib/dhcp/addr_utilities.cc                     |   96 +
 src/lib/dhcp/addr_utilities.h                      |   53 +
 src/lib/dhcp/cfgmgr.cc                             |   78 +
 src/lib/dhcp/cfgmgr.h                              |  126 +
 src/lib/dhcp/dhcp4.h                               |    3 +
 src/lib/dhcp/iface_mgr.cc                          |  236 +-
 src/lib/dhcp/iface_mgr.h                           |   40 +
 src/lib/dhcp/iface_mgr_linux.cc                    |    9 +-
 src/lib/dhcp/libdhcp++.cc                          |   17 +-
 src/lib/dhcp/pkt4.cc                               |    2 -
 src/lib/dhcp/pkt6.cc                               |   10 +-
 src/lib/dhcp/pool.cc                               |   87 +
 src/lib/dhcp/pool.h                                |  155 +
 src/lib/dhcp/subnet.cc                             |   91 +
 src/lib/dhcp/subnet.h                              |  161 +
 src/lib/dhcp/tests/Makefile.am                     |   21 +-
 src/lib/dhcp/tests/addr_utilities_unittest.cc      |   93 +
 src/lib/dhcp/tests/cfgmgr_unittest.cc              |   63 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |   87 +-
 src/lib/dhcp/tests/pool_unittest.cc                |  109 +
 src/lib/dhcp/tests/run_unittests.cc                |    1 -
 src/lib/dhcp/tests/subnet_unittest.cc              |  112 +
 src/lib/dhcp/tests/triplet_unittest.cc             |  104 +
 src/lib/dhcp/triplet.h                             |  110 +
 src/lib/dns/benchmarks/message_renderer_bench.cc   |    2 +-
 src/lib/dns/benchmarks/rdatarender_bench.cc        |    5 +-
 src/lib/dns/labelsequence.cc                       |    3 +-
 src/lib/dns/message.cc                             |    9 +
 src/lib/dns/message.h                              |    8 +
 src/lib/dns/nsec3hash.cc                           |   58 +-
 src/lib/dns/nsec3hash.h                            |   34 +-
 src/lib/dns/python/edns_python.cc                  |    3 +-
 src/lib/dns/python/tests/message_python_test.py    |   14 +-
 src/lib/dns/python/tests/tsig_python_test.py       |   17 +-
 src/lib/dns/python/tsig_python.cc                  |   24 +
 src/lib/dns/rdata/generic/afsdb_18.cc              |    1 -
 src/lib/dns/tests/labelsequence_unittest.cc        |   41 +-
 src/lib/dns/tests/message_unittest.cc              |    4 +-
 src/lib/dns/tests/nsec3hash_unittest.cc            |   24 +-
 src/lib/dns/tests/rrparamregistry_unittest.cc      |    5 +-
 src/lib/dns/tests/tsig_unittest.cc                 |  218 +-
 src/lib/dns/tsig.cc                                |   58 +-
 src/lib/dns/tsig.h                                 |   27 +-
 src/lib/log/compiler/message.cc                    |   10 +-
 src/lib/log/tests/logger_example.cc                |    1 -
 src/lib/python/isc/acl/tests/dns_test.py           |    4 -
 src/lib/python/isc/config/ccsession.py             |    8 +-
 src/lib/python/isc/config/config_data.py           |   99 +-
 src/lib/python/isc/config/tests/ccsession_test.py  |   16 +-
 .../python/isc/config/tests/config_data_test.py    |   99 +-
 .../python/isc/datasrc/tests/clientlist_test.py    |   93 +-
 src/lib/python/isc/ddns/session.py                 |    2 -
 src/lib/python/isc/testutils/tsigctx_mock.py       |    3 +
 src/lib/resolve/recursive_query.cc                 |   40 +-
 src/lib/resolve/tests/recursive_query_unittest.cc  |   39 +-
 .../resolve/tests/recursive_query_unittest_2.cc    |   13 +-
 .../resolve/tests/recursive_query_unittest_3.cc    |   15 +-
 .../server_common/tests/socket_requestor_test.cc   |    4 +-
 src/lib/util/Makefile.am                           |    2 +-
 src/lib/util/hash/sha1.cc                          |    4 +-
 src/lib/util/io/fd_share.cc                        |    1 +
 src/lib/util/tests/fd_tests.cc                     |    4 +-
 src/lib/util/tests/lru_list_unittest.cc            |    6 +-
 src/lib/util/threads/Makefile.am                   |   12 +
 src/lib/util/threads/lock.cc                       |  138 +
 src/lib/util/threads/lock.h                        |  128 +
 src/lib/util/threads/tests/Makefile.am             |   36 +
 src/lib/util/threads/tests/lock_unittest.cc        |  116 +
 src/lib/util/{ => threads}/tests/run_unittests.cc  |    2 +-
 src/lib/util/threads/tests/thread_unittest.cc      |   98 +
 src/lib/util/threads/thread.cc                     |  172 +
 src/lib/util/threads/thread.h                      |  112 +
 .../xfrin/retransfer_master.conf.orig              |    3 +
 tests/lettuce/features/terrain/bind10_control.py   |   25 +-
 .../lettuce/features/xfrin_notify_handling.feature |   70 +-
 tests/tools/dhcp-ubench/dhcp-perf-guide.xml        |   18 +-
 tests/tools/perfdhcp/Makefile.am                   |   35 +-
 tests/tools/perfdhcp/command_options.cc            |  463 +--
 tests/tools/perfdhcp/command_options.h             |  160 +-
 tests/tools/perfdhcp/main.cc                       |   28 +-
 tests/tools/perfdhcp/perfdhcp.c                    | 3559 --------------------
 tests/tools/perfdhcp/stats_mgr.h                   |  136 +-
 tests/tools/perfdhcp/templates/Makefile.am         |    4 +-
 tests/tools/perfdhcp/test_control.cc               |  513 ++-
 tests/tools/perfdhcp/test_control.h                |  259 +-
 .../tools/perfdhcp/tests/command_options_helper.h  |   21 +-
 .../perfdhcp/tests/command_options_unittest.cc     |   84 +-
 tests/tools/perfdhcp/tests/stats_mgr_unittest.cc   |    3 +-
 .../tools/perfdhcp/tests/test_control_unittest.cc  |   64 +-
 225 files changed, 7707 insertions(+), 13949 deletions(-)
 copy src/lib/config/tests/testdata/{spec24.spec => spec42.spec} (82%)
 delete mode 100644 src/lib/datasrc/cache.cc
 delete mode 100644 src/lib/datasrc/cache.h
 delete mode 100644 src/lib/datasrc/data_source.cc
 delete mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
 delete mode 100644 src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
 copy src/lib/{python/isc/datasrc/configurableclientlist_python.h => datasrc/memory/zone_table_segment.cc} (60%)
 create mode 100644 src/lib/datasrc/memory/zone_table_segment.h
 copy src/lib/{python/isc/datasrc/configurableclientlist_python.h => datasrc/memory/zone_table_segment_local.cc} (60%)
 create mode 100644 src/lib/datasrc/memory/zone_table_segment_local.h
 delete mode 100644 src/lib/datasrc/query.cc
 delete mode 100644 src/lib/datasrc/query.h
 delete mode 100644 src/lib/datasrc/sqlite3_datasrc.cc
 delete mode 100644 src/lib/datasrc/sqlite3_datasrc.h
 delete mode 100644 src/lib/datasrc/static_datasrc.cc
 delete mode 100644 src/lib/datasrc/static_datasrc.h
 delete mode 100644 src/lib/datasrc/tests/cache_unittest.cc
 delete mode 100644 src/lib/datasrc/tests/datasrc_unittest.cc
 rename src/lib/datasrc/{memory/tests => tests/memory}/.gitignore (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/Makefile.am (90%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/domaintree_unittest.cc (95%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/memory_client_unittest.cc (80%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/memory_segment_test.h (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/rdata_serialization_unittest.cc (99%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/rdataset_unittest.cc (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/run_unittests.cc (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/segment_object_holder_unittest.cc (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/Makefile.am (92%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/empty.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-broken1.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-broken2.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-cname-and-not-nsec-1.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-cname-and-not-nsec-2.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-apex-1.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-apex-2.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-nonapex-1.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-dname-ns-nonapex-2.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-duplicate-type-bad.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-duplicate-type.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-empty.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-cname.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-dname.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-nsec3.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple-nsec3param.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-multiple.zone (100%)
 copy src/lib/datasrc/tests/{testdata/example.org.nsec3-signed => memory/testdata/example.org-nsec3-empty-salt.zone} (91%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-fewer-labels.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-more-labels.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-signed-no-param.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-nsec3-signed.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-out-of-zone.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-rrsig-follows-nothing.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-rrsigs.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-wildcard-dname.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-wildcard-ns.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org-wildcard-nsec3.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/testdata/example.org.zone (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/treenode_rrset_unittest.cc (91%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/zone_data_unittest.cc (100%)
 rename src/lib/datasrc/{memory/tests => tests/memory}/zone_finder_unittest.cc (83%)
 create mode 100644 src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
 rename src/lib/datasrc/{memory/tests => tests/memory}/zone_table_unittest.cc (100%)
 delete mode 100644 src/lib/datasrc/tests/query_unittest.cc
 delete mode 100644 src/lib/datasrc/tests/sqlite3_unittest.cc
 delete mode 100644 src/lib/datasrc/tests/static_unittest.cc
 delete mode 100644 src/lib/datasrc/tests/test_datasrc.cc
 delete mode 100644 src/lib/datasrc/tests/test_datasrc.h
 create mode 100644 src/lib/dhcp/addr_utilities.cc
 create mode 100644 src/lib/dhcp/addr_utilities.h
 create mode 100644 src/lib/dhcp/cfgmgr.cc
 create mode 100644 src/lib/dhcp/cfgmgr.h
 create mode 100644 src/lib/dhcp/pool.cc
 create mode 100644 src/lib/dhcp/pool.h
 create mode 100644 src/lib/dhcp/subnet.cc
 create mode 100644 src/lib/dhcp/subnet.h
 create mode 100644 src/lib/dhcp/tests/addr_utilities_unittest.cc
 create mode 100644 src/lib/dhcp/tests/cfgmgr_unittest.cc
 create mode 100644 src/lib/dhcp/tests/pool_unittest.cc
 create mode 100644 src/lib/dhcp/tests/subnet_unittest.cc
 create mode 100644 src/lib/dhcp/tests/triplet_unittest.cc
 create mode 100644 src/lib/dhcp/triplet.h
 create mode 100644 src/lib/util/threads/Makefile.am
 create mode 100644 src/lib/util/threads/lock.cc
 create mode 100644 src/lib/util/threads/lock.h
 create mode 100644 src/lib/util/threads/tests/Makefile.am
 create mode 100644 src/lib/util/threads/tests/lock_unittest.cc
 copy src/lib/util/{ => threads}/tests/run_unittests.cc (93%)
 create mode 100644 src/lib/util/threads/tests/thread_unittest.cc
 create mode 100644 src/lib/util/threads/thread.cc
 create mode 100644 src/lib/util/threads/thread.h
 delete mode 100644 tests/tools/perfdhcp/perfdhcp.c

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 730ceb9..bad54da 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,89 @@
+485.	[bug]		jelte
+	Several bugs have been fixed in bindctl; tab-completion now works
+	within configuration lists, the problem where sometimes the
+	completion added a part twice has been solved, and it no longer
+	suggests the confusing value 'argument' as a completion-hint for
+	configuration items. Additionally, bindctl no longer crashes upon
+	input like 'config remove Boss'.
+	(Trac #2254, git 9047de5e8f973e12e536f7180738e6b515439448)
+
+484.	[func]		tomek
+	A new library (libb10-dhcpsrv) has been created. At present, it
+	only holds the code for the DHCP Configuration Manager. Currently
+	this object only supports basic configuration storage for the DHCPv6
+	server,	but that capability will be expanded.
+	(Trac #2238, git 6f29861b92742da34be9ae76968e82222b5bfd7d)
+
+bind10-devel-20120927 released on September 27, 2012
+
+483.	[func]		marcin
+	libdhcp++: Added new parameter to define sub-second timeout
+	for DHCP packet reception. The total timeout is now specified
+	by two parameters:  first specifies integral number of
+	seconds, second (which defaults to 0) specifies fractional
+	seconds with microsecond resolution.
+	(Trac #2231, git 15560cac16e4c52129322e3cb1787e0f47cf7850)
+
+482.	[func]		team
+	Memory footprint of the in-memory data source has been
+	substantially improved.  For example, b10-auth now requires much
+	less memory than BIND 9 named for loading and serving the same
+	zone in-memory.  This is a transparent change in terms of user
+	operation; there's no need to update or change the configuration
+	to enable this feature.
+	Notes: multiple instances of b10-auth still make separate copies
+	of the memory image.  Also, loading zones in memory still suspends
+	query processing, so manual reloading or reloading after incoming
+	transfer may cause service disruption for huge zones.
+	(Multiple Trac tickets, Summarized in Trac #2101)
+
+481.	[bug]		vorner
+	The abbreviated form of IP addresses in ACLs is accepted
+	(eg. "from": ["127.0.0.1", "::1"] now works).
+	(Trac #2191, git 48b6e91386b46eed383126ad98dddfafc9f7e75e)
+
+480.	[doc]		vorner
+	Added documentation about global TSIG key ring to the Guide.
+	(Trac #2189, git 52177bb31f5fb8e134aecb9fd039c368684ad2df)
+
+479.	[func]		marcin
+	Refactored perfdhcp tool to C++, added missing unit tests and removed
+	the old code. The new code uses libdhcp++ (src/lib/dhcp) for DHCP
+	packet management, network interface management and packet
+	transmission.
+	(Trac #1954, git 8d56105742f3043ed4b561f26241f3e4331f51dc)
+	(Trac #1955, git 6f914bb2c388eb4dd3e5c55297f8988ab9529b3f)
+	(Trac #1956, git 6f914bb2c388eb4dd3e5c55297f8988ab9529b3f)
+	(Trac #1957, git 7fca81716ad3a755bf5744e88c3adeef15b04450)
+	(Trac #1958, git 94e17184270cda58f55e6da62e845695117fede3)
+	(Trac #1959, git a8cf043db8f44604c7773e047a9dc2861e58462a)
+	(Trac #1960, git 6c192e5c0903f349b4d80cf2bb6cd964040ae7da)
+
+478.	[func]		naokikambe
+	New statistics items added into b10-xfrout: ixfr_running and
+	axfr_running.  Their values can be obtained by invoking "Stats show
+	Xfrout" via bindctl while b10-xfrout is running.
+	(Trac #2222, git 91311bdbfea95f65c5e8bd8294ba08fac12405f1)
+
+477.	[bug]		jelte
+	Fixed a problem with b10-msgq on OSX when using a custom Python
+	installation, that offers an unreliable select.poll() interface.
+	(Trac #2190, git e0ffa11d49ab949ee5a4ffe7682b0e6906667baa)
+
+476.	[bug]		vorner
+	The Xfrin now accepts transfers with some TSIG signatures omitted, as
+	allowed per RFC2845, section 4.4. This solves a compatibility
+	issues with Knot and NSD.
+	(Trac #1357, git 7ca65cb9ec528118f370142d7e7b792fcc31c9cf)
+
 475.	[func]		naokikambe
-	Added Xfrout statistics counters: notifyoutv4, notifyoutv6, xfrrej, and
-	xfrreqdone. These are per-zone type counters. The value of these
-	counters can be seen with zone name by invoking "Stats show Xfrout" via
-	bindctl.
+	Added Xfrout statistics counters: notifyoutv4, notifyoutv6,
+	xfrrej, and xfrreqdone. These are per-zone type counters.
+	The value of these counters can be seen with zone name by
+	invoking "Stats show Xfrout" via bindctl.
 	(Trac #2158, git e68c127fed52e6034ab5309ddd506da03c37a08a)
 
-474.	[func]      stephen
+474.	[func]		stephen
 	DHCP servers now use the BIND 10 logging system for messages.
 	(Trac #1545, git de69a92613b36bd3944cb061e1b7c611c3c85506)
 
@@ -24,7 +102,7 @@
 	contain the generated documentation.
 	(Trac #1687, git 2d4063b1a354f5048ca9dfb195e8e169650f43d0)
 
-471.    [bug]		vorner
+471.	[bug]		vorner
 	Fixed a problem when b10-loadzone tried to tread semicolon
 	in string data as start of comment, which caused invalid
 	data being loaded.
diff --git a/INSTALL b/INSTALL
index 839f120..944cb25 100644
--- a/INSTALL
+++ b/INSTALL
@@ -7,3 +7,7 @@ To then build from source:
 
 For detailed installation directions, see the guide
 at doc/guide/bind10-guide.txt or doc/guide/bind10-guide.html.
+
+You can find user-contributed OS-specific build/installation
+instructions on the BIND 10 wiki:
+http://bind10.isc.org/wiki/SystemSpecificNotes
diff --git a/configure.ac b/configure.ac
index 9d7b7ae..c6a2f01 100644
--- a/configure.ac
+++ b/configure.ac
@@ -990,20 +990,6 @@ CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
 # Check for functions that are not available on all platforms
 AC_CHECK_FUNCS([pselect])
 
-# perfdhcp: If the clock_gettime() function does not exist on the system,
-# use an alternative supplied in the code based on gettimeofday().
-CLOCK_GETTIME_LDFLAGS=
-AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_GETTIME_LDFLAGS=-lrt], [])
-AC_SUBST([CLOCK_GETTIME_LDFLAGS])
-
-# perfdhcp: if getifaddrs() does not exist, have the code output a message
-# that it can't be run on this version of the operating system.  For the
-# systems on which BIND 10 is built, this means Solaris 10. (Replacements
-# for this function are long and involved, and the function is reported present
-# on Solaris 11 and later, either in the libsocket or libnsl libraries.)
-AC_SEARCH_LIBS([getifaddrs], [socket nsl],
-               [AC_DEFINE([HAVE_GETIFADDRS], [1], [getifaddrs() present])])
-
 # /dev/poll issue: ASIO uses /dev/poll by default if it's available (generally
 # the case with Solaris).  Unfortunately its /dev/poll specific code would
 # trigger the gcc's "missing-field-initializers" warning, which would
@@ -1074,6 +1060,26 @@ if test "x$VALGRIND" != "xno"; then
    found_valgrind="found"
 fi
 
+# Check for optreset in unistd.h. On BSD systems the optreset is
+# used to reset the state of getopt() function. Resetting its state
+# is required if command line arguments are parsed multiple times
+# during a program. On Linux this variable will not exist because
+# getopt() reset is performed by setting optind = 0. On Operating
+# Systems where optreset is defined use optreset = 1 and optind = 1
+# to reset internal state of getopt(). Failing to do so will result
+# in unpredictable output from getopt().
+AC_MSG_CHECKING([whether optreset variable is defined in unistd.h])
+AC_TRY_LINK(
+    [#include <unistd.h>],
+    [extern int optreset; optreset=1;],
+    [ AC_MSG_RESULT(yes)
+      var_optreset_exists=yes],
+    [ AC_MSG_RESULT(no)
+      var_optreset_exists=no]
+)
+AM_CONDITIONAL(HAVE_OPTRESET, test "x$var_optreset_exists" != "xno")
+AM_COND_IF([HAVE_OPTRESET], [AC_DEFINE([HAVE_OPTRESET], [1], [Check for optreset?])])
+
 AC_CONFIG_FILES([Makefile
                  doc/Makefile
                  doc/guide/Makefile
@@ -1187,11 +1193,11 @@ AC_CONFIG_FILES([Makefile
                  src/lib/exceptions/tests/Makefile
                  src/lib/datasrc/Makefile
                  src/lib/datasrc/memory/Makefile
-                 src/lib/datasrc/memory/tests/Makefile
-                 src/lib/datasrc/memory/tests/testdata/Makefile
                  src/lib/datasrc/memory/benchmarks/Makefile
                  src/lib/datasrc/tests/Makefile
                  src/lib/datasrc/tests/testdata/Makefile
+                 src/lib/datasrc/tests/memory/Makefile
+                 src/lib/datasrc/tests/memory/testdata/Makefile
                  src/lib/xfr/Makefile
                  src/lib/xfr/tests/Makefile
                  src/lib/log/Makefile
@@ -1209,6 +1215,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/server_common/tests/Makefile
                  src/lib/util/Makefile
                  src/lib/util/io/Makefile
+                 src/lib/util/threads/Makefile
+                 src/lib/util/threads/tests/Makefile
                  src/lib/util/unittests/Makefile
                  src/lib/util/python/Makefile
                  src/lib/util/pyunittests/Makefile
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 7e122e9..f6b9fa0 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -579,8 +579,9 @@ INPUT                  = ../src/lib/exceptions ../src/lib/cc \
     ../src/lib/log/compiler ../src/lib/asiolink/ ../src/lib/nsas \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
     ../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
-    ../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp \
-    ../src/bin/dhcp4 ../tests/tools/perfdhcp devel
+    ../src/lib/util/threads/ ../src/lib/resolve ../src/lib/acl \
+    ../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
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index a95b0f5..2e66ad5 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -1310,6 +1310,89 @@ TODO
       many modules. So we show them here in one place.
     </para>
 
+    <section id='common-tsig'>
+      <title>TSIG keys</title>
+
+      <para>
+        TSIG is a way to sign requests and responses in DNS. It is defined in
+        RFC 2845 and uses symmetric cryptography to sign the DNS messages. If
+        you want to make any use of TSIG (to authenticate transfers or DDNS,
+        for example), you need to set up shared secrets between the endpoints.
+      </para>
+
+      <para>
+        BIND 10 uses a global key ring for the secrets. It doesn't currently
+        mean they would be stored differently, they are just in one place of
+        the configuration.
+      </para>
+
+      <section id='tsig-key-syntax'>
+        <title>Key anatomy and syntax</title>
+
+        <para>
+          Each key has three attributes. One is a name by which it is referred
+          both in DNS packets and the rest of the configuration. Another is the
+          algorithm used to compute the signature. And the last part is a
+          base64 encoded secret, which might be any blob of data.
+        </para>
+
+        <para>
+          The parts are written into a string, concatenated together by colons.
+          So if you wanted to have a key called "example.key", used as a
+          HMAC-MD5 key with secret "secret", you'd write it as:
+<screen>"example.key.:c2VjcmV0:hmac-md5"</screen>
+        </para>
+
+        <para>
+          The HMAC-MD5 algorithm is the default, so you can omit it. You could
+          write the same key as:
+<screen>"example.key.:c2VjcmV0"</screen>
+        </para>
+
+        <para>
+          You can also use these algorithms (which may not be omitted from the
+          key definition if used):
+          <itemizedlist>
+            <listitem>hmac-sha1</listitem>
+            <listitem>hmac-sha224</listitem>
+            <listitem>hmac-sha256</listitem>
+            <listitem>hmac-sha384</listitem>
+            <listitem>hmac-sha512</listitem>
+          </itemizedlist>
+        </para>
+
+        <para>
+          The name of the key must be a valid DNS name.
+        </para>
+      </section>
+
+      <section id='tsig-key-ring'>
+        <title>Key ring</title>
+        <para>
+          The key ring lives in the configuration in "tsig_keys/keys". Most of
+          the system uses the keys from there — ACLs, authoritative server to
+          sign responses to signed queries, and <command>b10-xfrout</command>
+          to sign transfers. The <command>b10-xfrin</command> uses its own
+          configuration for keys, but that will be fixed in Trac ticket
+          <ulink url="http://bind10.isc.org/ticket/1351">#1351</ulink>.
+        </para>
+
+        <para>
+          The key ring is just a list of strings, each describing one key. So,
+          to add a new key, you can do this:
+          <screen>> <userinput>config add tsig_keys/keys "example.key.:c2VjcmV0"</userinput>
+> <userinput>config show tsig_keys/keys</userinput>
+tsig_keys/keys[0]   "example.key.:c2VjcmV0" string  (modified)
+> <userinput>config commit</userinput></screen>
+        </para>
+
+        <para>
+          You can keep as many keys as you want in the key ring, but each must
+          have a different name.
+        </para>
+      </section>
+    </section>
+
     <section id='common-acl'>
       <title>ACLs</title>
 
@@ -1375,9 +1458,9 @@ AND_MATCH := "ALL": [ RULE_RAW, RULE_RAW, ... ]
         <para>
           The other is TSIG key by which the message was signed. The ACL
           contains only the name (under the name "key"), the key itself
-          must be stored in the global keyring. This property is applicable only
-          to the DNS context.
-<!-- TODO: Section for the keyring and link to it.-->
+	  must be stored in the global key ring (see <xref
+	  linkend="tsig-key-ring"/>).
+          This property is applicable only to the DNS context.
         </para>
 
         <para>
@@ -1403,18 +1486,6 @@ AND_MATCH := "ALL": [ RULE_RAW, RULE_RAW, ... ]
           will work in a similar way.
         </para>
 
-        <note>
-          <simpara>
-	    The list form is currently rejected due to an
-	    implementation bug.  There is a plan to fix it relatively
-	    soon, so the syntax is kept here, but note that it won't
-	    work until the bug is fixed.  To keep track of the status
-	    of the issue, see
-	    <ulink url="http://bind10.isc.org/ticket/2191">Trac #2191</ulink>.
-	    Until then, the value must be a single string.
-          </simpara>
-        </note>
-
         <para>
           If that is not enough, you can compose the matching conditions
           to logical expressions. They are called "ANY", "ALL" and "NOT".
@@ -1827,7 +1898,7 @@ can use various data source backends.
           operator, the authoritative server needs to be told to reload it, by
           <screen>> <userinput>Auth loadzone example.org</userinput></screen>
           You don't need to do this when the zone is modified by
-          XfrIn, it does so automatically.
+          <command>b10-xfrin</command>; it does so automatically.
         </para>
 
         <para>
@@ -2151,7 +2222,7 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</screen>
 
     <para>
       If you want to require TSIG in access control, a system wide TSIG
-      "key ring" must be configured.
+      key ring must be configured (see <xref linkend="tsig-key-ring"/>).
       In this example, we allow client matching both the IP address
       and key.
     </para>
@@ -2161,7 +2232,7 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</screen>
 > <userinput>config commit</userinput></screen>
 
     <para>Both <command>b10-xfrout</command> and <command>b10-auth</command>
-      will use the system wide keyring to check
+      will use the system wide key ring to check
       TSIGs in the incoming messages and to sign responses.</para>
 
     <para>
@@ -2371,11 +2442,12 @@ what is XfroutClient xfr_client??
 > <userinput>config commit</userinput>
 </screen>
       The TSIG key must be configured system wide
-      (see <xref linkend="xfrout"/>.)
+      (see <xref linkend="common-tsig"/>).
       </para>
 
       <para>
-        Full description of ACLs can be found in <xref linkend="common-acl" />.
+	The full description of ACLs can be found in <xref
+	linkend="common-acl" />.
       </para>
 
       <note><simpara>
diff --git a/src/bin/auth/Makefile.am b/src/bin/auth/Makefile.am
index 9e24433..6128a4c 100644
--- a/src/bin/auth/Makefile.am
+++ b/src/bin/auth/Makefile.am
@@ -57,12 +57,6 @@ b10_auth_SOURCES += common.h common.cc
 b10_auth_SOURCES += statistics.cc statistics.h
 b10_auth_SOURCES += datasrc_configurator.h
 b10_auth_SOURCES += main.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. This should've been moot after #1207, but there is still
-# one dependency; the in-memory-specific zone loader call is still in
-# auth.
-b10_auth_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
 
 nodist_b10_auth_SOURCES = auth_messages.h auth_messages.cc
 EXTRA_DIST += auth_messages.mes
@@ -80,6 +74,7 @@ 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)
 
 # TODO: config.h.in is wrong because doesn't honor pkgdatadir
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index ddb7466..b870ae2 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -26,6 +26,7 @@
 #include <exceptions/exceptions.h>
 
 #include <util/buffer.h>
+#include <util/threads/lock.h>
 
 #include <dns/edns.h>
 #include <dns/exceptions.h>
@@ -41,10 +42,7 @@
 
 #include <asiodns/dns_service.h>
 
-#include <datasrc/query.h>
 #include <datasrc/data_source.h>
-#include <datasrc/static_datasrc.h>
-#include <datasrc/sqlite3_datasrc.h>
 #include <datasrc/client_list.h>
 
 #include <xfr/xfrout_client.h>
@@ -275,6 +273,10 @@ public:
     boost::shared_ptr<ConfigurableClientList> getClientList(const RRClass&
                                                             rrclass)
     {
+        // TODO: Debug-build only check
+        if (!mutex_.locked()) {
+            isc_throw(isc::Unexpected, "Not locked!");
+        }
         const std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
             const_iterator it(client_lists_.find(rrclass));
         if (it == client_lists_.end()) {
@@ -312,6 +314,8 @@ public:
                       isc::dns::Message& message,
                       bool done);
 
+    mutable util::thread::Mutex mutex_;
+
 private:
     bool xfrout_connected_;
     AbstractXfroutClient& xfrout_client_;
@@ -639,6 +643,10 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
         local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
         message.setEDNS(local_edns);
     }
+    // Lock the client lists and keep them under the lock until the processing
+    // and rendering is done (this is the same mutex as from
+    // AuthSrv::getClientListMutex()).
+    isc::util::thread::Mutex::Locker locker(mutex_);
 
     try {
         const ConstQuestionPtr question = *message.beginQuestion();
@@ -670,6 +678,8 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
     LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
               .arg(renderer_.getLength()).arg(message);
     return (true);
+    // The message can contain some data from the locked resource. But outside
+    // this method, we touch only the RCode of it, so it should be safe.
 }
 
 bool
@@ -923,6 +933,11 @@ AuthSrv::destroyDDNSForwarder() {
 void
 AuthSrv::setClientList(const RRClass& rrclass,
                        const boost::shared_ptr<ConfigurableClientList>& list) {
+    // TODO: Debug-build only check
+    if (!impl_->mutex_.locked()) {
+        isc_throw(isc::Unexpected, "Not locked");
+    }
+
     if (list) {
         impl_->client_lists_[rrclass] = list;
     } else {
@@ -936,6 +951,11 @@ AuthSrv::getClientList(const RRClass& rrclass) {
 
 vector<RRClass>
 AuthSrv::getClientListClasses() const {
+    // TODO: Debug-build only check
+    if (!impl_->mutex_.locked()) {
+        isc_throw(isc::Unexpected, "Not locked");
+    }
+
     vector<RRClass> result;
     for (std::map<RRClass, boost::shared_ptr<ConfigurableClientList> >::
          const_iterator it(impl_->client_lists_.begin());
@@ -945,6 +965,11 @@ AuthSrv::getClientListClasses() const {
     return (result);
 }
 
+util::thread::Mutex&
+AuthSrv::getClientListMutex() const {
+    return (impl_->mutex_);
+}
+
 void
 AuthSrv::setTCPRecvTimeout(size_t timeout) {
     dnss_->setTCPRecvTimeout(timeout);
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index e2ffd71..ee7bd52 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -40,6 +40,9 @@ namespace util {
 namespace io {
 class BaseSocketSessionForwarder;
 }
+namespace thread {
+class Mutex;
+}
 }
 namespace datasrc {
 class ConfigurableClientList;
@@ -319,6 +322,38 @@ public:
     ///     has been set by setClientList.
     std::vector<isc::dns::RRClass> getClientListClasses() const;
 
+    /// \brief Return a mutex for the client lists.
+    ///
+    /// Background loading of data uses threads. Therefore we need to protect
+    /// the client lists by a mutex, so they don't change (or get destroyed)
+    /// during query processing. Get (and lock) this mutex whenever you do
+    /// something with the lists and keep it locked until you finish. This
+    /// is correct:
+    /// \code
+    /// {
+    ///  Mutex::Locker locker(auth->getClientListMutex());
+    ///  boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+    ///    list(auth->getClientList(RRClass::IN()));
+    ///  // Do some processing here
+    /// }
+    /// \endcode
+    ///
+    /// But this is not (it releases the mutex too soon):
+    /// \code
+    /// boost::shared_ptr<isc::datasrc::ConfigurableClientList> list;
+    /// {
+    ///     Mutex::Locker locker(auth->getClientListMutex());
+    ///     list = auth->getClientList(RRClass::IN()));
+    /// }
+    /// // Do some processing here
+    /// \endcode
+    ///
+    /// \note This method is const even if you are allowed to modify
+    ///    (lock) the mutex. It's because locking of the mutex is not really
+    ///    a modification of the server object and it is needed to protect the
+    ///    lists even on read-only operations.
+    isc::util::thread::Mutex& getClientListMutex() const;
+
     /// \brief Sets the timeout for incoming TCP connections
     ///
     /// Incoming TCP connections that have not sent their data
diff --git a/src/bin/auth/benchmarks/Makefile.am b/src/bin/auth/benchmarks/Makefile.am
index c09230a..48e552a 100644
--- a/src/bin/auth/benchmarks/Makefile.am
+++ b/src/bin/auth/benchmarks/Makefile.am
@@ -17,12 +17,6 @@ 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 += ../auth_log.h ../auth_log.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. When #1207 is done this becomes moot anyway, and the
-# specific workaround is not needed anymore, so we can then remove this
-# line again.
-query_bench_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
 
 nodist_query_bench_SOURCES = ../auth_messages.h ../auth_messages.cc
 
@@ -40,5 +34,6 @@ 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 43b2422..448a31b 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -21,6 +21,7 @@
 #include <config/ccsession.h>
 #include <exceptions/exceptions.h>
 #include <dns/rrclass.h>
+#include <util/threads/lock.h>
 
 #include <string>
 
@@ -189,6 +190,9 @@ public:
         }
         Name origin(origin_elem->stringValue());
 
+        // We're going to work with the client lists. They may be used
+        // from a different thread too, protect them.
+        isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
         const boost::shared_ptr<isc::datasrc::ConfigurableClientList>
             list(server.getClientList(zone_class));
 
diff --git a/src/bin/auth/datasrc_configurator.h b/src/bin/auth/datasrc_configurator.h
index f305a0d..6b2f582 100644
--- a/src/bin/auth/datasrc_configurator.h
+++ b/src/bin/auth/datasrc_configurator.h
@@ -20,6 +20,7 @@
 #include <datasrc/client_list.h>
 #include <config/ccsession.h>
 #include <cc/data.h>
+#include <util/threads/lock.h>
 
 #include <set>
 
@@ -119,6 +120,8 @@ public:
             isc_throw(isc::InvalidOperation,
                       "Can't reconfigure while not initialized by init()");
         }
+        // Lock the client lists, we're going to manipulate them.
+        isc::util::thread::Mutex::Locker locker(server_->getClientListMutex());
         typedef std::map<std::string, isc::data::ConstElementPtr> Map;
         typedef std::pair<isc::dns::RRClass, ListPtr> RollbackPair;
         typedef std::pair<isc::dns::RRClass, isc::data::ConstElementPtr>
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index f87ed4c..7c01a6b 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -52,13 +52,6 @@ run_unittests_SOURCES += query_unittest.cc
 run_unittests_SOURCES += statistics_unittest.cc
 run_unittests_SOURCES += datasrc_configurator_unittest.cc
 run_unittests_SOURCES += run_unittests.cc
-# This is a temporary workaround for #1206, where the InMemoryClient has been
-# moved to an ldopened library. We could add that library to LDADD, but that
-# is nonportable. This should've been moot after #1207, but there is still
-# one dependency; the in-memory-specific zone loader call is still in
-# auth.
-run_unittests_SOURCES += ${top_srcdir}/src/lib/datasrc/memory_datasrc.cc
-
 
 nodist_run_unittests_SOURCES = ../auth_messages.h ../auth_messages.cc
 
@@ -80,6 +73,7 @@ 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)
 run_unittests_LDADD += $(SQLITE_LIBS)
 
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index e86cca4..3197e7e 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -15,6 +15,7 @@
 #include <config.h>
 
 #include <util/io/sockaddr_util.h>
+#include <util/memory_segment_local.h>
 
 #include <dns/message.h>
 #include <dns/messagerenderer.h>
@@ -38,6 +39,7 @@
 #include <auth/datasrc_configurator.h>
 
 #include <util/unittests/mock_socketsession.h>
+#include <util/threads/lock.h>
 #include <dns/tests/unittest_util.h>
 #include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
@@ -1285,7 +1287,7 @@ public:
         if (fake_rrset_ && fake_rrset_->getName() == name &&
             fake_rrset_->getType() == type)
         {
-            return (ZoneFinderContextPtr(new ZoneFinder::Context(
+            return (ZoneFinderContextPtr(new ZoneFinder::GenericContext(
                                              *this, options,
                                              ResultContext(SUCCESS,
                                                            fake_rrset_))));
@@ -1393,16 +1395,19 @@ public:
              const isc::datasrc::DataSourceClientPtr
                  client(new FakeClient(info.data_src_client_ != NULL ?
                                        info.data_src_client_ :
-                                       info.cache_.get(),
+                                       info.getCacheClient(),
                                        throw_when, isc_exception, fake_rrset));
              clients_.push_back(client);
-             data_sources_.push_back(DataSourceInfo(client.get(),
-                 isc::datasrc::DataSourceClientContainerPtr(), false));
+             data_sources_.push_back(
+                 DataSourceInfo(client.get(),
+                                isc::datasrc::DataSourceClientContainerPtr(),
+                                false, RRClass::IN(), mem_sgmt_));
         }
     }
 private:
     const boost::shared_ptr<isc::datasrc::ConfigurableClientList> real_;
     vector<isc::datasrc::DataSourceClientPtr> clients_;
+    MemorySegmentLocal mem_sgmt_;
 };
 
 } // end anonymous namespace for throwing proxy classes
@@ -1421,10 +1426,13 @@ TEST_F(AuthSrvTest,
 {
     // Set real inmem client to proxy
     updateInMemory(&server, "example.", CONFIG_INMEMORY_EXAMPLE);
-    boost::shared_ptr<isc::datasrc::ConfigurableClientList>
-        list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
-                          false));
-    server.setClientList(RRClass::IN(), list);
+    {
+        isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
+        boost::shared_ptr<isc::datasrc::ConfigurableClientList>
+            list(new FakeList(server.getClientList(RRClass::IN()), THROW_NEVER,
+                              false));
+        server.setClientList(RRClass::IN(), list);
+    }
 
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
     server.processMessage(*io_message, *parse_message, *response_obuffer,
@@ -1447,6 +1455,7 @@ setupThrow(AuthSrv* server, ThrowWhen throw_when, bool isc_exception,
 {
     updateInMemory(server, "example.", CONFIG_INMEMORY_EXAMPLE);
 
+    isc::util::thread::Mutex::Locker locker(server->getClientListMutex());
     boost::shared_ptr<isc::datasrc::ConfigurableClientList>
         list(new FakeList(server->getClientList(RRClass::IN()), throw_when,
                           isc_exception, rrset));
@@ -1759,6 +1768,10 @@ TEST_F(AuthSrvTest, DDNSForwardCreateDestroy) {
 
 // Check the client list accessors
 TEST_F(AuthSrvTest, clientList) {
+    // We need to lock the mutex to make the (get|set)ClientList happy.
+    // There's a debug-build only check in them to make sure everything
+    // locks them and we call them directly here.
+    isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
     // The lists don't exist. Therefore, the list of RRClasses is empty.
     // We also have no IN list.
     EXPECT_TRUE(server.getClientListClasses().empty());
@@ -1789,4 +1802,16 @@ TEST_F(AuthSrvTest, clientList) {
     EXPECT_EQ(list, server.getClientList(RRClass::IN()));
 }
 
+// We just test the mutex can be locked (exactly once).
+TEST_F(AuthSrvTest, mutex) {
+    isc::util::thread::Mutex::Locker l1(server.getClientListMutex());
+    // TODO: Once we have non-debug build, this one will not work, since
+    // we currently use the fact that we can't lock twice from the same
+    // thread. In the non-debug mode, this would deadlock.
+    // Skip then.
+    EXPECT_THROW({
+        isc::util::thread::Mutex::Locker l2(server.getClientListMutex());
+    }, isc::InvalidOperation);
+}
+
 }
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index a22725e..e57de93 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -174,6 +174,7 @@ TEST_F(AuthCommandTest, shutdownIncorrectPID) {
 // zones, and checks the zones are correctly loaded.
 void
 zoneChecks(AuthSrv& server) {
+    isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
               find(Name("ns.test1.example")).finder_->
               find(Name("ns.test1.example"), RRType::A())->code);
@@ -214,6 +215,7 @@ configureZones(AuthSrv& server) {
 
 void
 newZoneChecks(AuthSrv& server) {
+    isc::util::thread::Mutex::Locker locker(server.getClientListMutex());
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getClientList(RRClass::IN())->
               find(Name("ns.test1.example")).finder_->
               find(Name("ns.test1.example"), RRType::A())->code);
@@ -271,30 +273,33 @@ TEST_F(AuthCommandTest,
         "}]}"));
     DataSourceConfigurator::testReconfigure(&server_, config);
 
-    // Check that the A record at www.example.org does not exist
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(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, server_.getClientList(RRClass::IN())->
-              find(Name("example.org")).finder_->
-              find(Name("www.example.org"), RRType::A())->code);
+    {
+        isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+        // Check that the A record at www.example.org does not exist
+        EXPECT_EQ(ZoneFinder::NXDOMAIN, server_.getClientList(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, server_.getClientList(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",
@@ -302,20 +307,26 @@ TEST_F(AuthCommandTest,
                                         "{\"origin\": \"example.org\"}"));
     checkAnswer(0, "Successful load");
 
-    // And now it should be present too.
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
-              find(Name("example.org")).finder_->
-              find(Name("www.example.org"), RRType::A())->code);
+    {
+        isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+        // And now it should be present too.
+        EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(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");
 
-    // The previous zone is not hurt in any way
-    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
-              find(Name("example.org")).finder_->
-              find(Name("example.org"), RRType::SOA())->code);
+    {
+        isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
+        // The previous zone is not hurt in any way
+        EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
+                  find(Name("example.org")).finder_->
+                  find(Name("example.org"), RRType::SOA())->code);
+    }
 
     const ConstElementPtr config2(Element::fromJSON("{"
         "\"IN\": [{"
@@ -331,6 +342,7 @@ TEST_F(AuthCommandTest,
         Element::fromJSON("{\"origin\": \"example.com\"}"));
     checkAnswer(1, "Unreadable");
 
+    isc::util::thread::Mutex::Locker locker(server_.getClientListMutex());
     // The previous zone is not hurt in any way
     EXPECT_EQ(ZoneFinder::SUCCESS, server_.getClientList(RRClass::IN())->
               find(Name("example.org")).finder_->
diff --git a/src/bin/auth/tests/datasrc_configurator_unittest.cc b/src/bin/auth/tests/datasrc_configurator_unittest.cc
index 78e3688..254b238 100644
--- a/src/bin/auth/tests/datasrc_configurator_unittest.cc
+++ b/src/bin/auth/tests/datasrc_configurator_unittest.cc
@@ -16,6 +16,7 @@
 
 #include <config/tests/fake_session.h>
 #include <config/ccsession.h>
+#include <util/threads/lock.h>
 
 #include <gtest/gtest.h>
 #include <memory>
@@ -81,6 +82,9 @@ public:
         }
         return (result);
     }
+    isc::util::thread::Mutex& getClientListMutex() const {
+        return (mutex_);
+    }
 protected:
     DatasrcConfiguratorTest() :
         session(ElementPtr(new ListElement), ElementPtr(new ListElement),
@@ -137,6 +141,7 @@ protected:
     const string specfile;
     std::map<RRClass, ListPtr> lists_;
     string log_;
+    mutable isc::util::thread::Mutex mutex_;
 };
 
 // Check the initialization (and cleanup)
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 603bf5c..84b7f8a 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -442,10 +442,9 @@ public:
                        ConstRRsetPtr rrset)
     {
         nsec_name_ = nsec_name;
-        nsec_context_.reset(new Context(*this,
-                                        FIND_DEFAULT, // a fake value
-                                        ResultContext(code, rrset,
-                                                      RESULT_NSEC_SIGNED)));
+        nsec_context_.reset(
+            new GenericContext(*this, FIND_DEFAULT, // a fake value
+                               ResultContext(code, rrset, RESULT_NSEC_SIGNED)));
     }
 
     // Once called, the findNSEC3 will return the provided result for the next
@@ -487,8 +486,8 @@ protected:
     {
         ConstRRsetPtr rp = stripRRsigs(rrset, options);
         return (ZoneFinderContextPtr(
-                    new Context(*this, options,
-                                ResultContext(code, rp, flags))));
+                    new GenericContext(*this, options,
+                                       ResultContext(code, rp, flags))));
     }
 
 private:
@@ -604,9 +603,9 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                 target.push_back(stripRRsigs(found_rrset->second, options));
             }
             return (ZoneFinderContextPtr(
-                        new Context(*this, options,
-                                    ResultContext(SUCCESS, RRsetPtr()),
-                                    target)));
+                        new GenericContext(*this, options,
+                                           ResultContext(SUCCESS, RRsetPtr()),
+                                           target)));
         }
     }
 
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index f1a622e..b4e71bf 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -22,7 +22,7 @@ import sys
 from cmd import Cmd
 from bindctl.exception import *
 from bindctl.moduleinfo import *
-from bindctl.cmdparse import BindCmdParse
+from bindctl.cmdparse import BindCmdParser
 from bindctl import command_sets
 from xml.dom import minidom
 import isc
@@ -48,20 +48,21 @@ except ImportError:
 # if we have readline support, use that, otherwise use normal stdio
 try:
     import readline
-    # This is a fix for the problem described in
-    # http://bind10.isc.org/ticket/1345
-    # If '-' is seen as a word-boundary, the final completion-step
-    # (as handled by the cmd module, and hence outside our reach) can
-    # mistakenly add data twice, resulting in wrong completion results
-    # The solution is to remove it.
-    delims = readline.get_completer_delims()
-    delims = delims.replace('-', '')
-    readline.set_completer_delims(delims)
+    # Only consider spaces as word boundaries; identifiers can contain
+    # '/' and '[]', and configuration item names can in theory use any
+    # printable  character. See the discussion in tickets #1345 and
+    # #2254 for more information.
+    readline.set_completer_delims(' ')
 
     my_readline = readline.get_line_buffer
 except ImportError:
     my_readline = sys.stdin.readline
 
+# Used for tab-completion of 'identifiers' (i.e. config values)
+# If a command parameter has this name, the tab completion hints
+# are derived from config data
+CFGITEM_IDENTIFIER_PARAM = 'identifier'
+
 CSV_FILE_NAME = 'default_user.csv'
 CONFIG_MODULE_NAME = 'config'
 CONST_BINDCTL_HELP = """
@@ -463,41 +464,101 @@ class BindCmdInterpreter(Cmd):
 
         Cmd.onecmd(self, line)
 
-    def remove_prefix(self, list, prefix):
-        """Removes the prefix already entered, and all elements from the
-           list that don't match it"""
-        if prefix.startswith('/'):
-            prefix = prefix[1:]
-
-        new_list = []
-        for val in list:
-            if val.startswith(prefix):
-                new_val = val[len(prefix):]
-                if new_val.startswith("/"):
-                    new_val = new_val[1:]
-                new_list.append(new_val)
-        return new_list
+    def _get_identifier_startswith(self, id_text):
+        """Return the tab-completion hints for identifiers starting with
+           id_text.
+
+           Parameters:
+           id_text (string): the currently entered identifier part, which
+           is to be completed.
+        """
+        # Strip starting "/" from id_text
+        if id_text.startswith('/'):
+            id_text = id_text[1:]
+        # Get all items from the given module (up to the first /)
+        list = self.config_data.get_config_item_list(
+                        id_text.rpartition("/")[0], recurse=True)
+        # filter out all possibilities that don't match currently entered
+        # text part
+        hints = [val for val in list if val.startswith(id_text)]
+        return hints
+
+    def _cmd_has_identifier_param(self, cmd):
+        """
+        Returns True if the given (parsed) command is known and has a
+        parameter which points to a config data identifier
+
+        Parameters:
+        cmd (cmdparse.BindCmdParser): command context, including given params
+
+        """
+        if cmd.module not in self.modules:
+            return False
+        command = self.modules[cmd.module].get_command_with_name(cmd.command)
+        return command.has_param_with_name(CFGITEM_IDENTIFIER_PARAM)
 
     def complete(self, text, state):
-        if 0 == state:
+        """
+        Returns tab-completion hints. See the python documentation of the
+        readline and Cmd modules for more information.
+
+        The first time this is called (within one 'completer' action), it
+        has state 0, and a list of possible completions is made. This list
+        is stored; complete() will then be called with increasing values of
+        state, until it returns None. For each call it returns the state'th
+        element of the hints it collected in the first call.
+
+        The hints list contents depend on which part of the full command
+        line; if no module is given yet, it will list all modules. If a
+        module is given, but no command, it will complete with module
+        commands. If both have been given, it will create the hints based on
+        the command parameters.
+
+        If module and command have already been specified, and the command
+        has a parameter 'identifier', the configuration data is used to
+        create the hints list.
+
+        Parameters:
+        text (string): The text entered so far in the 'current' part of
+                       the command (module, command, parameters)
+        state (int): state used in the readline tab-completion logic;
+                     0 on first call, increasing by one until there are
+                     no (more) hints to return.
+
+        Returns the string value of the hints list with index 'state',
+        or None if no (more) hints are available.
+        """
+        if state == 0:
             self._update_all_modules_info()
             text = text.strip()
             hints = []
             cur_line = my_readline()
             try:
-                cmd = BindCmdParse(cur_line)
+                cmd = BindCmdParser(cur_line)
                 if not cmd.params and text:
                     hints = self._get_command_startswith(cmd.module, text)
+                elif self._cmd_has_identifier_param(cmd):
+                    # If the command has an argument that is a configuration
+                    # identifier (currently, this is only a subset of
+                    # the config commands), then don't tab-complete with
+                    # hints derived from command parameters, but from
+                    # possible configuration identifiers.
+                    #
+                    # This solves the issue reported in #2254, where
+                    # there were hints such as 'argument' and 'identifier'.
+                    #
+                    # Since they are replaced, the tab-completion no longer
+                    # adds 'help' as an option (but it still works)
+                    #
+                    # Also, currently, tab-completion does not work
+                    # together with 'config go' (it does not take 'current
+                    # position' into account). But config go currently has
+                    # problems by itself, unrelated to completion.
+                    hints = self._get_identifier_startswith(text)
                 else:
                     hints = self._get_param_startswith(cmd.module, cmd.command,
                                                        text)
-                    if cmd.module == CONFIG_MODULE_NAME:
-                        # grm text has been stripped of slashes...
-                        my_text = self.location + "/" + cur_line.rpartition(" ")[2]
-                        list = self.config_data.get_config_item_list(my_text.rpartition("/")[0], True)
-                        hints.extend([val for val in list if val.startswith(my_text[1:])])
-                        # remove the common prefix from the hints so we don't get it twice
-                        hints = self.remove_prefix(hints, my_text.rpartition("/")[0])
+
             except CmdModuleNameFormatError:
                 if not text:
                     hints = self.get_module_names()
@@ -562,7 +623,7 @@ class BindCmdInterpreter(Cmd):
 
     def _parse_cmd(self, line):
         try:
-            cmd = BindCmdParse(line)
+            cmd = BindCmdParser(line)
             self._validate_cmd(cmd)
             self._handle_cmd(cmd)
         except (IOError, http.client.HTTPException) as err:
@@ -794,7 +855,7 @@ class BindCmdInterpreter(Cmd):
                     else:
                         print("Warning: ignoring unknown directive: " + line)
                 else:
-                    cmd = BindCmdParse(line)
+                    cmd = BindCmdParser(line)
                     self._validate_cmd(cmd)
                     self._handle_cmd(cmd)
         except (isc.config.ModuleCCSessionError,
diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in
index 1685355..546ecc0 100755
--- a/src/bin/bindctl/bindctl_main.py.in
+++ b/src/bin/bindctl/bindctl_main.py.in
@@ -42,16 +42,19 @@ def prepare_config_commands(tool):
     cmd = CommandInfo(name = "show", desc = "Show configuration.")
     param = ParamInfo(name = "argument", type = "string", optional=True, desc = "If you specify the argument 'all' (before the identifier), recursively show all child elements for the given identifier.")
     cmd.add_param(param)
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "show_json", desc = "Show full configuration in JSON format.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd = CommandInfo(name="show_json",
+                      desc="Show full configuration in JSON format.")
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "add", desc =
+    cmd = CommandInfo(name="add", desc=
         "Add an entry to configuration list or a named set. "
         "When adding to a list, the command has one optional argument, "
         "a value to add to the list. The value must be in correct JSON "
@@ -60,45 +63,53 @@ def prepare_config_commands(tool):
         "parameter value, similar to when adding to a list. "
         "In either case, when no value is given, an entry will be "
         "constructed with default values.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value_or_name", type = "string", optional=True, desc = "Specifies a value to add to the list, or the name when adding to a named set. It must be in correct JSON format and complete.")
+    param = ParamInfo(name="value_or_name", type="string", optional=True,
+                      desc="Specifies a value to add to the list, or the name when adding to a named set. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
-    param = ParamInfo(name = "value_for_set", type = "string", optional=True, desc = "Specifies an optional value to add to the named map. It must be in correct JSON format and complete.")
+    param = ParamInfo(name="value_for_set", type="string", optional=True,
+                      desc="Specifies an optional value to add to the named map. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list or named set.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd = CommandInfo(name="remove", desc="Remove entry from configuration list or named set.")
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     param = ParamInfo(name = "value", type = "string", optional=True, desc = "When identifier is a list, specifies a value to remove from the list. It must be in correct JSON format and complete. When it is a named set, specifies the name to remove.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "set", desc = "Set a configuration value.")
-    param = ParamInfo(name = "identifier", type = "string", optional=True, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd = CommandInfo(name="set", desc="Set a configuration value.")
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=True, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
-    param = ParamInfo(name = "value", type = "string", optional=False, desc = "Specifies a value to set. It must be in correct JSON format and complete.")
+    param = ParamInfo(name="value", type="string", optional=False,
+                      desc="Specifies a value to set. It must be in correct JSON format and complete.")
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value (i.e. revert to the default, if any).")
-    param = ParamInfo(name = "identifier", type = "string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd = CommandInfo(name="unset", desc="Unset a configuration value (i.e. revert to the default, if any).")
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=False, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "diff", desc = "Show all local changes that have not been committed.")
+    cmd = CommandInfo(name="diff", desc="Show all local changes that have not been committed.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "revert", desc = "Revert all local changes.")
+    cmd = CommandInfo(name="revert", desc="Revert all local changes.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "commit", desc = "Commit all local changes.")
+    cmd = CommandInfo(name="commit", desc="Commit all local changes.")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part.")
-    param = ParamInfo(name = "identifier", type="string", optional=False, desc = DEFAULT_IDENTIFIER_DESC)
+    cmd = CommandInfo(name="go", desc="Go to a specific configuration part.")
+    param = ParamInfo(name=CFGITEM_IDENTIFIER_PARAM, type="string",
+                      optional=False, desc=DEFAULT_IDENTIFIER_DESC)
     cmd.add_param(param)
     module.add_command(cmd)
 
diff --git a/src/bin/bindctl/cmdparse.py b/src/bin/bindctl/cmdparse.py
index c624cba..30909a3 100644
--- a/src/bin/bindctl/cmdparse.py
+++ b/src/bin/bindctl/cmdparse.py
@@ -25,7 +25,7 @@ except ImportError:
 
 param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*"
 
-# The value string can be a sequence without space or comma 
+# The value string can be a sequence without space or comma
 # characters, or a string surroundedby quotation marks(such marks
 # can be part of string in an escaped form)
 #param_value_str  = "(?P<param_value>[\"\'].+?(?<!\\\)[\"\']|[^\'\"][^, ]+)"
@@ -34,8 +34,8 @@ param_value_with_quota_str  = "[\"\'](?P<param_value>.+?)(?<!\\\)[\"\']"
 next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
 
 
-PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + 
-                                      param_value_with_quota_str + 
+PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str +
+                                      param_value_with_quota_str +
                                       next_params_str)
 PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str)
 # Used for module and command name
@@ -83,52 +83,52 @@ def _remove_list_and_map_whitespace(text):
                 if map_count == 0:
                     result.append(_remove_unquoted_whitespace(text[cur_start_map_pos:pos + 1]))
                     start_pos = pos + 1
-        
+
 
         pos = pos + 1
     if start_pos <= len(text):
         result.append(text[start_pos:len(text)])
     return "".join(result)
-    
-    
-class BindCmdParse:
+
+
+class BindCmdParser:
     """ This class will parse the command line user input into three parts:
     module name, command, parameters
-    the first two parts are strings and parameter is one hash, 
+    the first two parts are strings and parameter is one hash,
     parameters part is optional
-    
-    Example: zone reload, zone_name=example.com 
+
+    Example: zone reload, zone_name=example.com
     module == zone
     command == reload
     params == [zone_name = 'example.com']
     """
-    
+
     def __init__(self, cmd):
         self.params = OrderedDict()
         self.module = ''
         self.command = ''
         self._parse_cmd(cmd)
 
-    def _parse_cmd(self, text_str):    
+    def _parse_cmd(self, text_str):
         '''Parse command line. '''
         # Get module name
         groups = NAME_PATTERN.match(text_str)
         if not groups:
             raise CmdModuleNameFormatError
-        
+
         self.module = groups.group('name')
         cmd_str = groups.group('others')
         if cmd_str:
             if not groups.group('blank'):
                 raise CmdModuleNameFormatError
-        else:            
+        else:
             raise CmdMissCommandNameFormatError(self.module)
-            
+
         # Get command name
         groups = NAME_PATTERN.match(cmd_str)
         if (not groups):
             raise CmdCommandNameFormatError(self.module)
-        
+
         self.command = groups.group('name')
         param_str = groups.group('others')
         if param_str:
@@ -143,7 +143,7 @@ class BindCmdParse:
     def _parse_params(self, param_text):
         """convert a=b,c=d into one hash """
         param_text = _remove_list_and_map_whitespace(param_text)
-        
+
         # Check parameter name "help"
         param = NAME_PATTERN.match(param_text)
         if param and param.group('name') == "help":
@@ -153,7 +153,7 @@ class BindCmdParse:
         while True:
             if not param_text.strip():
                 break
-                
+
             groups = PARAM_PATTERN.match(param_text) or \
                      PARAM_WITH_QUOTA_PATTERN.match(param_text)
             if not groups:
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index bcfb6c5..f598472 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -40,14 +40,14 @@ except ImportError:
 class TestCmdLex(unittest.TestCase):
 
     def my_assert_raise(self, exception_type, cmd_line):
-        self.assertRaises(exception_type, cmdparse.BindCmdParse, cmd_line)
+        self.assertRaises(exception_type, cmdparse.BindCmdParser, cmd_line)
 
 
     def testCommandWithoutParameter(self):
-        cmd = cmdparse.BindCmdParse("zone add")
-        assert cmd.module == "zone"
-        assert cmd.command == "add"
-        self.assertEqual(len(cmd.params), 0)
+        cmd_parser = cmdparse.BindCmdParser("zone add")
+        assert cmd_parser.module == "zone"
+        assert cmd_parser.command == "add"
+        self.assertEqual(len(cmd_parser.params), 0)
 
 
     def testCommandWithParameters(self):
@@ -56,45 +56,51 @@ class TestCmdLex(unittest.TestCase):
                  "zone add zone_name = 'cnnic.cn\", file ='cnnic.cn.file' master=1.1.1.1, " }
 
         for cmd_line in lines:
-            cmd = cmdparse.BindCmdParse(cmd_line)
-            assert cmd.module == "zone"
-            assert cmd.command == "add"
-            assert cmd.params["zone_name"] == "cnnic.cn"
-            assert cmd.params["file"] == "cnnic.cn.file"
-            assert cmd.params["master"] == '1.1.1.1'
+            cmd_parser = cmdparse.BindCmdParser(cmd_line)
+            assert cmd_parser.module == "zone"
+            assert cmd_parser.command == "add"
+            assert cmd_parser.params["zone_name"] == "cnnic.cn"
+            assert cmd_parser.params["file"] == "cnnic.cn.file"
+            assert cmd_parser.params["master"] == '1.1.1.1'
 
     def testCommandWithParamters_2(self):
         '''Test whether the parameters in key=value can be parsed properly.'''
-        cmd = cmdparse.BindCmdParse('zone cmd name = 1:34::2')
-        self.assertEqual(cmd.params['name'], '1:34::2')
-
-        cmd = cmdparse.BindCmdParse('zone cmd name = 1\"\'34**&2 value=44\"\'\"')
-        self.assertEqual(cmd.params['name'], '1\"\'34**&2')
-        self.assertEqual(cmd.params['value'], '44\"\'\"')
-
-        cmd = cmdparse.BindCmdParse('zone cmd name = 1\"\'34**&2 ,value=  44\"\'\"')
-        self.assertEqual(cmd.params['name'], '1\"\'34**&2')
-        self.assertEqual(cmd.params['value'], '44\"\'\"')
-
-        cmd = cmdparse.BindCmdParse('zone cmd name =  1\'34**&2value=44\"\'\" value = \"==============\'')
-        self.assertEqual(cmd.params['name'], '1\'34**&2value=44\"\'\"')
-        self.assertEqual(cmd.params['value'], '==============')
-
-        cmd = cmdparse.BindCmdParse('zone cmd name =    \"1234, 567890 \" value ==&*/')
-        self.assertEqual(cmd.params['name'], '1234, 567890 ')
-        self.assertEqual(cmd.params['value'], '=&*/')
+        cmd_parser = cmdparse.BindCmdParser('zone cmd name = 1:34::2')
+        self.assertEqual(cmd_parser.params['name'], '1:34::2')
+
+        cmd_parser = cmdparse.BindCmdParser('zone cmd name = 1\"\'34**&2'
+                                            ' value=44\"\'\"')
+        self.assertEqual(cmd_parser.params['name'], '1\"\'34**&2')
+        self.assertEqual(cmd_parser.params['value'], '44\"\'\"')
+
+        cmd_parser = cmdparse.BindCmdParser('zone cmd name = 1\"\'34**&2'
+                                            ',value=  44\"\'\"')
+        self.assertEqual(cmd_parser.params['name'], '1\"\'34**&2')
+        self.assertEqual(cmd_parser.params['value'], '44\"\'\"')
+
+        cmd_parser = cmdparse.BindCmdParser('zone cmd name =  1\'34**&2'
+                                            'value=44\"\'\" value = '
+                                            '\"==============\'')
+        self.assertEqual(cmd_parser.params['name'], '1\'34**&2value=44\"\'\"')
+        self.assertEqual(cmd_parser.params['value'], '==============')
+
+        cmd_parser = cmdparse.BindCmdParser('zone cmd name =    \"1234, '
+                                            '567890 \" value ==&*/')
+        self.assertEqual(cmd_parser.params['name'], '1234, 567890 ')
+        self.assertEqual(cmd_parser.params['value'], '=&*/')
 
     def testCommandWithListParam(self):
-        cmd = cmdparse.BindCmdParse("zone set zone_name='cnnic.cn', master='1.1.1.1, 2.2.2.2'")
-        assert cmd.params["master"] == '1.1.1.1, 2.2.2.2'
+        cmd_parser = cmdparse.BindCmdParser("zone set zone_name='cnnic.cn', "
+                                            "master='1.1.1.1, 2.2.2.2'")
+        assert cmd_parser.params["master"] == '1.1.1.1, 2.2.2.2'
 
     def testCommandWithHelpParam(self):
-        cmd = cmdparse.BindCmdParse("zone add help")
-        assert cmd.params["help"] == "help"
+        cmd_parser = cmdparse.BindCmdParser("zone add help")
+        assert cmd_parser.params["help"] == "help"
 
-        cmd = cmdparse.BindCmdParse("zone add help *&)&)*&&$#$^%")
-        assert cmd.params["help"] == "help"
-        self.assertEqual(len(cmd.params), 1)
+        cmd_parser = cmdparse.BindCmdParser("zone add help *&)&)*&&$#$^%")
+        assert cmd_parser.params["help"] == "help"
+        self.assertEqual(len(cmd_parser.params), 1)
 
 
     def testCmdModuleNameFormatError(self):
@@ -130,15 +136,20 @@ class TestCmdSyntax(unittest.TestCase):
         int_spec = { 'item_type' : 'integer',
                        'item_optional' : False,
                        'item_default' : 10}
-        zone_file_param = ParamInfo(name = "zone_file", param_spec = string_spec)
+        zone_file_param = ParamInfo(name = "zone_file",
+                                    param_spec = string_spec)
         zone_name = ParamInfo(name = 'zone_name', param_spec = string_spec)
         load_cmd = CommandInfo(name = "load")
         load_cmd.add_param(zone_file_param)
         load_cmd.add_param(zone_name)
 
-        param_master = ParamInfo(name = "master", optional = True, param_spec = string_spec)
-        param_master = ParamInfo(name = "port", optional = True, param_spec = int_spec)
-        param_allow_update = ParamInfo(name = "allow_update", optional = True, param_spec = string_spec)
+        param_master = ParamInfo(name = "master", optional = True,
+                                 param_spec = string_spec)
+        param_master = ParamInfo(name = "port", optional = True,
+                                 param_spec = int_spec)
+        param_allow_update = ParamInfo(name = "allow_update",
+                                       optional = True,
+                                       param_spec = string_spec)
         set_cmd = CommandInfo(name = "set")
         set_cmd.add_param(param_master)
         set_cmd.add_param(param_allow_update)
@@ -160,13 +171,14 @@ class TestCmdSyntax(unittest.TestCase):
 
 
     def no_assert_raise(self, cmd_line):
-        cmd = cmdparse.BindCmdParse(cmd_line)
-        self.bindcmd._validate_cmd(cmd)
+        cmd_parser = cmdparse.BindCmdParser(cmd_line)
+        self.bindcmd._validate_cmd(cmd_parser)
 
 
     def my_assert_raise(self, exception_type, cmd_line):
-        cmd = cmdparse.BindCmdParse(cmd_line)
-        self.assertRaises(exception_type, self.bindcmd._validate_cmd, cmd)
+        cmd_parser = cmdparse.BindCmdParser(cmd_line)
+        self.assertRaises(exception_type, self.bindcmd._validate_cmd,
+                          cmd_parser)
 
 
     def testValidateSuccess(self):
@@ -177,7 +189,8 @@ class TestCmdSyntax(unittest.TestCase):
         self.no_assert_raise("zone help help='dd' ")
         self.no_assert_raise("zone set allow_update='1.1.1.1' zone_name='cn'")
         self.no_assert_raise("zone set zone_name='cn'")
-        self.my_assert_raise(isc.cc.data.DataTypeError, "zone set zone_name ='cn', port='cn'")
+        self.my_assert_raise(isc.cc.data.DataTypeError,
+                             "zone set zone_name ='cn', port='cn'")
         self.no_assert_raise("zone reload_all")
 
     def testCmdUnknownModuleSyntaxError(self):
@@ -188,15 +201,22 @@ class TestCmdSyntax(unittest.TestCase):
         self.my_assert_raise(CmdUnknownCmdSyntaxError, "zone dd")
 
     def testCmdMissParamSyntaxError(self):
-        self.my_assert_raise(CmdMissParamSyntaxError, "zone load zone_file='cn'")
-        self.my_assert_raise(CmdMissParamSyntaxError, "zone load zone_name='cn'")
-        self.my_assert_raise(CmdMissParamSyntaxError, "zone set allow_update='1.1.1.1'")
-        self.my_assert_raise(CmdMissParamSyntaxError, "zone set ")
+        self.my_assert_raise(CmdMissParamSyntaxError,
+                             "zone load zone_file='cn'")
+        self.my_assert_raise(CmdMissParamSyntaxError,
+                             "zone load zone_name='cn'")
+        self.my_assert_raise(CmdMissParamSyntaxError,
+                             "zone set allow_update='1.1.1.1'")
+        self.my_assert_raise(CmdMissParamSyntaxError,
+                             "zone set ")
 
     def testCmdUnknownParamSyntaxError(self):
-        self.my_assert_raise(CmdUnknownParamSyntaxError, "zone load zone_d='cn'")
-        self.my_assert_raise(CmdUnknownParamSyntaxError, "zone reload_all zone_name = 'cn'")
-        self.my_assert_raise(CmdUnknownParamSyntaxError, "zone help a b c")
+        self.my_assert_raise(CmdUnknownParamSyntaxError,
+                             "zone load zone_d='cn'")
+        self.my_assert_raise(CmdUnknownParamSyntaxError,
+                             "zone reload_all zone_name = 'cn'")
+        self.my_assert_raise(CmdUnknownParamSyntaxError,
+                             "zone help a b c")
 
 class TestModuleInfo(unittest.TestCase):
 
@@ -233,7 +253,8 @@ class TestNameSequence(unittest.TestCase):
             self.tool.add_module_info(ModuleInfo(name = random_str))
 
     def setUp(self):
-        self.random_names = ['1erdfeDDWsd', '3fe', '2009erd', 'Fe231', 'tere142', 'rei8WD']
+        self.random_names = ['1erdfeDDWsd', '3fe', '2009erd',
+                             'Fe231', 'tere142', 'rei8WD']
         self._create_bindcmd()
 
     def testSequence(self):
@@ -321,7 +342,8 @@ class TestConfigCommands(unittest.TestCase):
         def precmd(line):
             self.tool.precmd(line)
         self.tool._update_all_modules_info = update_all_modules_info
-        # If line is equals to 'EOF', _update_all_modules_info() shouldn't be called
+        # If line is equals to 'EOF', _update_all_modules_info()
+        # shouldn't be called
         precmd('EOF')
         self.assertRaises(socket.error, precmd, 'continue')
 
@@ -360,34 +382,41 @@ class TestConfigCommands(unittest.TestCase):
         self.assertEqual((1, MultiConfigData.DEFAULT),
                          self.tool.config_data.get_value("/foo/an_int"))
 
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/an_int\" value=\"5\"")
-        self.tool.apply_config_cmd(cmd)
+        cmd_parser = cmdparse.BindCmdParser('config set identifier='
+                                            '"foo/an_int" value="5"')
+        self.tool.apply_config_cmd(cmd_parser)
         self.assertEqual((5, MultiConfigData.LOCAL),
                          self.tool.config_data.get_value("/foo/an_int"))
 
-        cmd = cmdparse.BindCmdParse("config unset identifier=\"foo/an_int\"")
-        self.tool.apply_config_cmd(cmd)
+        cmd_parser = cmdparse.BindCmdParser('config unset identifier='
+                                            '"foo/an_int"')
+        self.tool.apply_config_cmd(cmd_parser)
 
         self.assertEqual((1, MultiConfigData.DEFAULT),
                          self.tool.config_data.get_value("/foo/an_int"))
 
         # this should raise a NotFoundError
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/bar\" value=\"[]\"")
-        self.assertRaises(isc.cc.data.DataNotFoundError, self.tool.apply_config_cmd, cmd)
+        cmd_parser = cmdparse.BindCmdParser('config set identifier='
+                                            '"foo/bar" value="[]"')
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.tool.apply_config_cmd, cmd_parser)
 
-        cmd = cmdparse.BindCmdParse("config unset identifier=\"foo/bar\"")
+        cmd_parser = cmdparse.BindCmdParser('config unset identifier='
+                                            '"foo/bar"')
         self.assertRaises(isc.cc.data.DataNotFoundError,
-                          self.tool.apply_config_cmd, cmd)
+                          self.tool.apply_config_cmd, cmd_parser)
 
         # this should raise a TypeError
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/an_int\" value=\"[]\"")
-        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+        cmd_parser = cmdparse.BindCmdParser('config set identifier='
+                                            '"foo/an_int" value="[]"')
+        self.assertRaises(isc.cc.data.DataTypeError,
+                          self.tool.apply_config_cmd, cmd_parser)
 
     # this is a very specific one for use with a set of list tests
     # to try out the flexibility of the parser (only in the next test)
     def clt(self, full_cmd_string, item_value):
-        cmd = cmdparse.BindCmdParse(full_cmd_string)
-        self.tool.apply_config_cmd(cmd)
+        cmd_parser = cmdparse.BindCmdParser(full_cmd_string)
+        self.tool.apply_config_cmd(cmd_parser)
         self.assertEqual(([item_value], MultiConfigData.LOCAL),
                          self.tool.config_data.get_value("/foo/a_list"))
 
@@ -410,15 +439,56 @@ class TestConfigCommands(unittest.TestCase):
         self.clt("config set identifier = \"foo/a_list\" value=[ \"k\" ]", "k")
 
         # this should raise a TypeError
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=\"a\"")
-        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+        cmd_parser = cmdparse.BindCmdParser('config set identifier='
+                                            '"foo/a_list" value="a"')
+        self.assertRaises(isc.cc.data.DataTypeError,
+                          self.tool.apply_config_cmd, cmd_parser)
 
-        cmd = cmdparse.BindCmdParse("config set identifier=\"foo/a_list\" value=[1]")
-        self.assertRaises(isc.cc.data.DataTypeError, self.tool.apply_config_cmd, cmd)
+        cmd_parser = cmdparse.BindCmdParser('config set identifier='
+                                            '"foo/a_list" value=[1]')
+        self.assertRaises(isc.cc.data.DataTypeError,
+                          self.tool.apply_config_cmd, cmd_parser)
 
     def tearDown(self):
         sys.stdout = self.stdout_backup
 
+    def test_cmd_has_identifier_param(self):
+        module = ModuleInfo(name="test_module")
+
+        cmd = CommandInfo(name="command_with_identifier")
+        param = ParamInfo(name=bindcmd.CFGITEM_IDENTIFIER_PARAM)
+        cmd.add_param(param)
+        module.add_command(cmd)
+
+        cmd = CommandInfo(name="command_without_identifier")
+        param = ParamInfo(name="some_argument")
+        cmd.add_param(param)
+        module.add_command(cmd)
+
+        self.tool.add_module_info(module)
+
+        cmd_parser = cmdparse.BindCmdParser('test_module '
+                                            'command_with_identifier')
+        self.assertTrue(self.tool._cmd_has_identifier_param(cmd_parser))
+
+        cmd_parser = cmdparse.BindCmdParser('test_module '
+                                            'command_without_identifier')
+        self.assertFalse(self.tool._cmd_has_identifier_param(cmd_parser))
+
+        cmd_parser = cmdparse.BindCmdParser('badmodule '
+                                            'command_without_identifier')
+        self.assertFalse(self.tool._cmd_has_identifier_param(cmd_parser))
+
+    def test_get_identifier_startswith(self):
+        hints = self.tool._get_identifier_startswith("/")
+        self.assertEqual(['foo/an_int', 'foo/a_list'], hints)
+
+        hints = self.tool._get_identifier_startswith("/foo/an")
+        self.assertEqual(['foo/an_int'], hints)
+
+        hints = self.tool._get_identifier_startswith("/bar")
+        self.assertEqual([], hints)
+
 class FakeBindCmdInterpreter(bindcmd.BindCmdInterpreter):
     def __init__(self):
         pass
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 98e402d..63ddbfc 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -42,12 +42,22 @@ server is about to open sockets on the specified port.
 The IPv4 DHCP server has received a packet that it is unable to
 interpret. The reason why the packet is invalid is included in the message.
 
+% DHCP4_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
+The IPv4 DHCP server tried to receive a packet but an error
+occured during this attempt. The reason for the error is included in
+the message.
+
 % DHCP4_PACKET_RECEIVED %1 (type %2) packet received on interface %3
 A debug message noting that the server has received the specified type of
 packet on the specified interface.  Note that a packet marked as UNKNOWN
 may well be a valid DHCP packet, just a type not expected by the server
 (e.g. it will report a received OFFER packet as UNKNOWN).
 
+% DHCP4_PACKET_SEND_FAIL failed to send DHCPv4 packet: %1
+This error is output if the IPv4 DHCP server fails to send an assembled
+DHCP message to a client. The reason for the error is included in the
+message.
+
 % DHCP4_PACK_FAIL failed to assemble response correctly
 This error is output if the server failed to assemble the data to be
 returned to the client into a valid packet.  The cause is most likely
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 2bbc075..67943c5 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -73,9 +73,15 @@ Dhcpv4Srv::run() {
         int timeout = 1000;
 
         // client's message and server's response
-        Pkt4Ptr query = IfaceMgr::instance().receive4(timeout);
+        Pkt4Ptr query;
         Pkt4Ptr rsp;
 
+        try {
+            query = IfaceMgr::instance().receive4(timeout);
+        } catch (const std::exception& e) {
+            LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
+        }
+
         if (query) {
             try {
                 query->unpack();
@@ -141,7 +147,11 @@ Dhcpv4Srv::run() {
                           .arg(rsp->getType()).arg(rsp->toText());
 
                 if (rsp->pack()) {
-                    IfaceMgr::instance().send(rsp);
+                    try {
+                        IfaceMgr::instance().send(rsp);
+                    } catch (const std::exception& e) {
+                        LOG_ERROR(dhcp4_logger, DHCP4_PACKET_SEND_FAIL).arg(e.what());
+                    }
                 } else {
                     LOG_ERROR(dhcp4_logger, DHCP4_PACK_FAIL);
                 }
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index a361531..1564940 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -45,12 +45,22 @@ server is about to open sockets on the specified port.
 % DHCP6_PACKET_PARSE_FAIL failed to parse incoming packet
 The IPv6 DHCP server has received a packet that it is unable to interpret.
 
+% DHCP6_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
+The IPv6 DHCP server tried to receive a packet but an error
+occured during this attempt. The reason for the error is included in
+the message.
+
 % DHCP6_PACKET_RECEIVED %1 (type %2) packet received
 A debug message noting that the server has received the specified type
 of packet.  Note that a packet marked as UNKNOWN may well be a valid
 DHCP packet, just a type not expected by the server (e.g. it will report
 a received OFFER packet as UNKNOWN).
 
+% DHCP6_PACKET_SEND_FAIL failed to send DHCPv6 packet: %1
+This error is output if the IPv6 DHCP server fails to send an assembled
+DHCP message to a client. The reason for the error is included in the
+message.
+
 % DHCP6_PACK_FAIL failed to assemble response correctly
 This error is output if the server failed to assemble the data to be
 returned to the client into a valid packet.  The reason is most likely
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 7c21941..54fa2b5 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -84,9 +84,15 @@ bool Dhcpv6Srv::run() {
         int timeout = 1000;
 
         // client's message and server's response
-        Pkt6Ptr query = IfaceMgr::instance().receive6(timeout);
+        Pkt6Ptr query;
         Pkt6Ptr rsp;
 
+        try {
+            query = IfaceMgr::instance().receive6(timeout);
+        } catch (const std::exception& e) {
+            LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
+        }
+
         if (query) {
             if (!query->unpack()) {
                 LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL,
@@ -154,7 +160,11 @@ bool Dhcpv6Srv::run() {
                           .arg(rsp->getType()).arg(rsp->toText());
 
                 if (rsp->pack()) {
-                    IfaceMgr::instance().send(rsp);
+                    try {
+                        IfaceMgr::instance().send(rsp);
+                    } catch (const std::exception& e) {
+                        LOG_ERROR(dhcp6_logger, DHCP6_PACKET_SEND_FAIL).arg(e.what());
+                    }
                 } else {
                     LOG_ERROR(dhcp6_logger, DHCP6_PACK_FAIL);
                 }
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index 58b1d87..010a1a5 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -131,9 +131,9 @@ class MsgQ:
     def setup_poller(self):
         """Set up the poll thing.  Internal function."""
         try:
-            self.poller = select.poll()
-        except AttributeError:
             self.kqueue = select.kqueue()
+        except AttributeError:
+            self.poller = select.poll()
 
     def add_kqueue_socket(self, socket, write_filter=False):
         """Add a kquque filter for a socket.  By default the read
diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py
index f8abb57..55b08cb 100644
--- a/src/bin/stats/tests/test_utils.py
+++ b/src/bin/stats/tests/test_utils.py
@@ -107,19 +107,10 @@ class MockMsgq:
 
     def run(self):
         self._started.set()
-        try:
-            self.msgq.run()
-        except Exception:
-            pass
-        finally:
-            # explicitly shut down the socket of the msgq before
-            # shutting down the msgq
-            self.msgq.listen_socket.shutdown(msgq.socket.SHUT_RDWR)
-            self.msgq.shutdown()
+        self.msgq.run()
 
     def shutdown(self):
-        # do nothing
-        pass
+        self.msgq.shutdown()
 
 class MockCfgmgr:
     def __init__(self):
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index a3818a6..7b9100e 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -564,6 +564,28 @@ class TestXfrinIXFRAdd(TestXfrinState):
         self.assertEqual(type(XfrinIXFRDeleteSOA()),
                          type(self.conn.get_xfrstate()))
 
+    def test_handle_new_delete_missing_sig(self):
+        self.conn._end_serial = isc.dns.Serial(1234)
+        # SOA RR whose serial is the current one means we are going to a new
+        # difference, starting with removing that SOA.
+        self.conn._diff.add_data(self.ns_rrset) # put some dummy change
+        self.conn._tsig_ctx = MockTSIGContext(TSIG_KEY)
+        self.conn._tsig_ctx.last_has_signature = lambda: False
+        # First, push a starting SOA inside. This should be OK, nothing checked
+        # yet.
+        self.state.handle_rr(self.conn, self.begin_soa)
+        end_soa_rdata = Rdata(RRType.SOA(), TEST_RRCLASS,
+                              'm. r. 1234 0 0 0 0')
+        end_soa_rrset = RRset(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA(),
+                                RRTTL(3600))
+        end_soa_rrset.add_rdata(end_soa_rdata)
+        # This would try to finish up. But the TSIG pretends not everything is
+        # signed, rejecting it.
+        self.assertRaises(xfrin.XfrinProtocolError, self.state.handle_rr,
+                          self.conn, end_soa_rrset)
+        # No diffs were commited
+        self.assertEqual([], self.conn._datasrc_client.committed_diffs)
+
     def test_handle_out_of_sync(self):
         # getting SOA with an inconsistent serial.  This is an error.
         self.conn._end_serial = isc.dns.Serial(1235)
@@ -792,12 +814,14 @@ class TestAXFR(TestXfrinConnection):
     def tearDown(self):
         time.time = self.orig_time_time
 
-    def __create_mock_tsig(self, key, error):
+    def __create_mock_tsig(self, key, error, has_last_signature=True):
         # This helper function creates a MockTSIGContext for a given key
         # and TSIG error to be used as a result of verify (normally faked
         # one)
         mock_ctx = MockTSIGContext(key)
         mock_ctx.error = error
+        if not has_last_signature:
+            mock_ctx.last_has_signature = lambda: False
         return mock_ctx
 
     def __match_exception(self, expected_exception, expected_msg, expression):
@@ -1379,6 +1403,16 @@ class TestAXFR(TestXfrinConnection):
         self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
         self.assertEqual(1, self.conn._tsig_ctx.verify_called)
 
+    def test_do_xfrin_without_last_tsig(self):
+        # TSIG verify will succeed, but it will pretend the last message is
+        # not signed.
+        self.conn._tsig_key = TSIG_KEY
+        self.conn._tsig_ctx_creator = \
+            lambda key: self.__create_mock_tsig(key, TSIGError.NOERROR, False)
+        self.conn.response_generator = self._create_normal_response_data
+        self.assertEqual(self.conn.do_xfrin(False), XFRIN_FAIL)
+        self.assertEqual(2, self.conn._tsig_ctx.verify_called)
+
     def test_do_xfrin_with_tsig_fail_for_second_message(self):
         # Similar to the previous test, but first verify succeeds.  There
         # should be a second verify attempt, which will fail, which should
@@ -1553,16 +1587,18 @@ class TestIXFRResponse(TestXfrinConnection):
         self.conn._handle_xfrin_responses()
         self.assertEqual(type(XfrinIXFREnd()), type(self.conn.get_xfrstate()))
         self.assertEqual([], self.conn._datasrc_client.diffs)
+        # Everything is committed as one bunch, currently we commit at the very
+        # end.
         check_diffs(self.assertEqual,
                     [[('delete', begin_soa_rrset),
                       ('delete', self._create_a('192.0.2.1')),
                       ('add', self._create_soa('1231')),
-                      ('add', self._create_a('192.0.2.2'))],
-                     [('delete', self._create_soa('1231')),
+                      ('add', self._create_a('192.0.2.2')),
+                      ('delete', self._create_soa('1231')),
                       ('delete', self._create_a('192.0.2.3')),
                       ('add', self._create_soa('1232')),
-                      ('add', self._create_a('192.0.2.4'))],
-                     [('delete', self._create_soa('1232')),
+                      ('add', self._create_a('192.0.2.4')),
+                      ('delete', self._create_soa('1232')),
                       ('delete', self._create_a('192.0.2.5')),
                       ('add', soa_rrset),
                       ('add', self._create_a('192.0.2.6'))]],
@@ -2924,7 +2960,7 @@ class TestFormatting(unittest.TestCase):
         self.assertEqual("example.org/IN",
                          format_zone_str(isc.dns.Name("example.org"),
                          isc.dns.RRClass("IN")))
-    
+
     def test_format_addrinfo(self):
         # This test may need to be updated if the input type is changed,
         # right now it is a nested tuple:
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index b4a4cf9..2b65311 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -362,6 +362,7 @@ class XfrinFirstData(XfrinState):
                 conn._request_serial == get_soa_serial(rr.get_rdata()[0]):
             logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_INCREMENTAL_RESP,
                          conn.zone_str())
+            conn._diff = None # Will be created on-demand
             self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
         else:
             logger.debug(DBG_XFRIN_TRACE, XFRIN_GOT_NONINCREMENTAL_RESP,
@@ -380,11 +381,13 @@ class XfrinIXFRDeleteSOA(XfrinState):
             raise XfrinException(rr.get_type().to_text() +
                                  ' RR is given in IXFRDeleteSOA state')
         # This is the beginning state of one difference sequence (changes
-        # for one SOA update).  We need to create a new Diff object now.
+        # for one SOA update).  We may need to create a new Diff object now.
         # Note also that we (unconditionally) enable journaling here.  The
         # Diff constructor may internally disable it, however, if the
         # underlying data source doesn't support journaling.
-        conn._diff = Diff(conn._datasrc_client, conn._zone_name, False, True)
+        if conn._diff is None:
+            conn._diff = Diff(conn._datasrc_client, conn._zone_name, False,
+                              True)
         conn._diff.delete_data(rr)
         self.set_xfrstate(conn, XfrinIXFRDelete())
         conn.get_transfer_stats().ixfr_deletion_count += 1
@@ -420,6 +423,9 @@ class XfrinIXFRAdd(XfrinState):
             conn.get_transfer_stats().ixfr_changeset_count += 1
             soa_serial = get_soa_serial(rr.get_rdata()[0])
             if soa_serial == conn._end_serial:
+                # The final part is there. Check all was signed
+                # and commit it to the database.
+                conn._check_response_tsig_last()
                 conn._diff.commit()
                 self.set_xfrstate(conn, XfrinIXFREnd())
                 return True
@@ -429,7 +435,10 @@ class XfrinIXFRAdd(XfrinState):
                                          str(conn._current_serial) +
                                          ', got ' + str(soa_serial))
             else:
-                conn._diff.commit()
+                # Apply a change to the database. But don't commit it yet,
+                # we can't know if the message is/will be properly signed.
+                # A complete commit will happen after the last bit.
+                conn._diff.apply()
                 self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
                 return False
         conn._diff.add_data(rr)
@@ -494,6 +503,7 @@ class XfrinAXFREnd(XfrinState):
         indicating there will be no more message to receive.
 
         """
+        conn._check_response_tsig_last()
         conn._diff.commit()
         return False
 
@@ -782,6 +792,15 @@ class XfrinConnection(asyncore.dispatcher):
             # strict.
             raise XfrinProtocolError('Unexpected TSIG in response')
 
+    def _check_response_tsig_last(self):
+        """
+        Check there's a signature at the last message.
+        """
+        if self._tsig_ctx is not None:
+            if not self._tsig_ctx.last_has_signature():
+                raise XfrinProtocolError('TSIG verify fail: no TSIG on last '+
+                                         'message')
+
     def __parse_soa_response(self, msg, response_data):
         '''Parse a response to SOA query and extract the SOA from answer.
 
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 4415cff..8b616d2 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -1,6 +1,6 @@
 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
-	       [<!ENTITY mdash "—">]>
+               [<!ENTITY mdash "—">]>
 <!--
  - Copyright (C) 2010-2012  Internet Systems Consortium, Inc. ("ISC")
  -
@@ -166,15 +166,15 @@
       <varlistentry>
         <term>notifyoutv4</term>
         <listitem><simpara>
-	 Number of IPv4 notifies per zone name sent out from Xfrout
-	</simpara></listitem>
+         Number of IPv4 notifies per zone name sent out from Xfrout
+        </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
         <term>notifyoutv6</term>
         <listitem><simpara>
-	 Number of IPv6 notifies per zone name sent out from Xfrout
-	</simpara></listitem>
+         Number of IPv6 notifies per zone name sent out from Xfrout
+        </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -187,7 +187,21 @@
       <varlistentry>
         <term>xfrreqdone</term>
         <listitem><simpara>
-	 Number of requested zone transfers per zone name completed
+         Number of requested zone transfers per zone name completed
+        </simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>ixfr_running</term>
+        <listitem><simpara>
+         Number of IXFRs in progress
+        </simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>axfr_running</term>
+        <listitem><simpara>
+         Number of AXFRs in progress
         </simpara></listitem>
       </varlistentry>
 
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 9e07527..6e3f4a8 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -1,4 +1,4 @@
-# Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010-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
@@ -270,6 +270,7 @@ class TestXfroutSessionBase(unittest.TestCase):
 
     def setUp(self):
         self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
+        self.setup_counters()
         self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(),
                                        TSIGKeyRing(),
                                        (socket.AF_INET, socket.SOCK_STREAM,
@@ -278,22 +279,54 @@ class TestXfroutSessionBase(unittest.TestCase):
                                        isc.acl.dns.REQUEST_LOADER.load(
                                            [{"action": "ACCEPT"}]),
                                        {},
-                                       counter_xfrrej=self._counter_xfrrej,
-                                       counter_xfrreqdone=self._counter_xfrreqdone)
+                                       **self._counters)
         self.set_request_type(RRType.AXFR()) # test AXFR by default
         self.mdata = self.create_request_data()
         self.soa_rrset = create_soa(SOA_CURRENT_VERSION)
         # some test replaces a module-wide function.  We should ensure the
         # original is used elsewhere.
         self.orig_get_rrset_len = xfrout.get_rrset_len
-        self._zone_name_xfrrej = None
-        self._zone_name_xfrreqdone = None
 
-    def _counter_xfrrej(self, zone_name):
-        self._zone_name_xfrrej = zone_name
+    def setup_counters(self):
+        self._statistics_data = {
+            'zones' : {
+                TEST_ZONE_NAME_STR : {
+                    'xfrrej': 0,
+                    'xfrreqdone': 0
+                    }
+                },
+            'axfr_started': 0,
+            'ixfr_started': 0,
+            'axfr_ended': 0,
+            'ixfr_ended': 0
+            }
+        def _counter_xfrrej(zone_name):
+            self._statistics_data['zones'][zone_name]['xfrrej'] += 1
+        def _counter_xfrreqdone(zone_name):
+            self._statistics_data['zones'][zone_name]['xfrreqdone'] += 1
+        def _inc_ixfr_running():
+            self._statistics_data['ixfr_started'] += 1
+        def _dec_ixfr_running():
+            self._statistics_data['ixfr_ended'] += 1
+        def _inc_axfr_running():
+            self._statistics_data['axfr_started'] += 1
+        def _dec_axfr_running():
+            self._statistics_data['axfr_ended'] += 1
+        self._counters = {
+            'counter_xfrrej': _counter_xfrrej,
+            'counter_xfrreqdone': _counter_xfrreqdone,
+            'inc_ixfr_running': _inc_ixfr_running,
+            'dec_ixfr_running': _dec_ixfr_running,
+            'inc_axfr_running': _inc_axfr_running,
+            'dec_axfr_running': _dec_axfr_running
+            }
 
-    def _counter_xfrreqdone(self, zone_name):
-        self._zone_name_xfrreqdone = zone_name
+    def get_counter(self, name):
+        if name.find('ixfr_') == 0 or name.find('axfr_') == 0:
+            return self._statistics_data[name]
+        else:
+            return \
+                self._statistics_data['zones'][TEST_ZONE_NAME_STR][name]
 
     def tearDown(self):
         xfrout.get_rrset_len = self.orig_get_rrset_len
@@ -386,6 +419,8 @@ class TestXfroutSession(TestXfroutSessionBase):
                 "action": "DROP"
             }
         ]))
+        # check the 'xfrrej' counter initially
+        self.assertEqual(self.get_counter('xfrrej'), 0)
         # Localhost (the default in this test) is accepted
         rcode, msg = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(rcode.to_text(), "NOERROR")
@@ -399,6 +434,8 @@ class TestXfroutSession(TestXfroutSessionBase):
                                 ('192.0.2.2', 12345))
         rcode, msg = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(rcode.to_text(), "REFUSED")
+        # check the 'xfrrej' counter after incrementing
+        self.assertEqual(self.get_counter('xfrrej'), 1)
 
         # TSIG signed request
         request_data = self.create_request_data(with_tsig=True)
@@ -427,6 +464,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         ]))
         [rcode, msg] = self.xfrsess._parse_query_message(request_data)
         self.assertEqual(rcode.to_text(), "REFUSED")
+        # check the 'xfrrej' counter after incrementing
+        self.assertEqual(self.get_counter('xfrrej'), 2)
 
         # ACL using TSIG: no TSIG; should be rejected
         acl_setter(isc.acl.dns.REQUEST_LOADER.load([
@@ -434,6 +473,8 @@ class TestXfroutSession(TestXfroutSessionBase):
         ]))
         [rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(rcode.to_text(), "REFUSED")
+        # check the 'xfrrej' counter after incrementing
+        self.assertEqual(self.get_counter('xfrrej'), 3)
 
         #
         # ACL using IP + TSIG: both should match
@@ -453,34 +494,28 @@ class TestXfroutSession(TestXfroutSessionBase):
                                 ('192.0.2.2', 12345))
         [rcode, msg] = self.xfrsess._parse_query_message(request_data)
         self.assertEqual(rcode.to_text(), "REFUSED")
+        # check the 'xfrrej' counter after incrementing
+        self.assertEqual(self.get_counter('xfrrej'), 4)
         # Address matches, but TSIG doesn't (not included)
         self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
                                 ('192.0.2.1', 12345))
         [rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(rcode.to_text(), "REFUSED")
+        # check the 'xfrrej' counter after incrementing
+        self.assertEqual(self.get_counter('xfrrej'), 5)
         # Neither address nor TSIG matches
         self.xfrsess._remote = (socket.AF_INET, socket.SOCK_STREAM,
                                 ('192.0.2.2', 12345))
         [rcode, msg] = self.xfrsess._parse_query_message(self.mdata)
         self.assertEqual(rcode.to_text(), "REFUSED")
+        # check the 'xfrrej' counter after incrementing
+        self.assertEqual(self.get_counter('xfrrej'), 6)
 
     def test_transfer_acl(self):
         # ACL checks only with the default ACL
         def acl_setter(acl):
             self.xfrsess._acl = acl
-        self.assertIsNone(self._zone_name_xfrrej)
-        self.check_transfer_acl(acl_setter)
-        self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR)
-
-    def test_transfer_acl_with_nonetype_xfrrej(self):
-        # ACL checks only with the default ACL and NoneType xfrrej
-        # counter
-        def acl_setter(acl):
-            self.xfrsess._acl = acl
-        self.xfrsess._counter_xfrrej = None
-        self.assertIsNone(self._zone_name_xfrrej)
         self.check_transfer_acl(acl_setter)
-        self.assertIsNone(self._zone_name_xfrrej)
 
     def test_transfer_acl_with_notcallable_xfrrej(self):
         # ACL checks only with the default ACL and not callable xfrrej
@@ -500,9 +535,7 @@ class TestXfroutSession(TestXfroutSessionBase):
             self.xfrsess._zone_config[zone_key]['transfer_acl'] = acl
             self.xfrsess._acl = isc.acl.dns.REQUEST_LOADER.load([
                     {"from": "127.0.0.1", "action": "DROP"}])
-        self.assertIsNone(self._zone_name_xfrrej)
         self.check_transfer_acl(acl_setter)
-        self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR)
 
     def test_transfer_zoneacl_nomatch(self):
         # similar to the previous one, but the per zone doesn't match the
@@ -514,9 +547,7 @@ class TestXfroutSession(TestXfroutSessionBase):
                 isc.acl.dns.REQUEST_LOADER.load([
                     {"from": "127.0.0.1", "action": "DROP"}])
             self.xfrsess._acl = acl
-        self.assertIsNone(self._zone_name_xfrrej)
         self.check_transfer_acl(acl_setter)
-        self.assertEqual(self._zone_name_xfrrej, TEST_ZONE_NAME_STR)
 
     def test_get_transfer_acl(self):
         # set the default ACL.  If there's no specific zone ACL, this one
@@ -866,25 +897,11 @@ class TestXfroutSession(TestXfroutSessionBase):
         def myreply(msg, sock):
             self.sock.send(b"success")
 
-        self.assertIsNone(self._zone_name_xfrreqdone)
+        self.assertEqual(self.get_counter('xfrreqdone'), 0)
         self.xfrsess._reply_xfrout_query = myreply
         self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
         self.assertEqual(self.sock.readsent(), b"success")
-        self.assertEqual(self._zone_name_xfrreqdone, TEST_ZONE_NAME_STR)
-
-    def test_dns_xfrout_start_with_nonetype_xfrreqdone(self):
-        def noerror(msg, name, rrclass):
-            return Rcode.NOERROR()
-        self.xfrsess._xfrout_setup = noerror
-
-        def myreply(msg, sock):
-            self.sock.send(b"success")
-
-        self.assertIsNone(self._zone_name_xfrreqdone)
-        self.xfrsess._reply_xfrout_query = myreply
-        self.xfrsess._counter_xfrreqdone = None
-        self.xfrsess.dns_xfrout_start(self.sock, self.mdata)
-        self.assertIsNone(self._zone_name_xfrreqdone)
+        self.assertGreater(self.get_counter('xfrreqdone'), 0)
 
     def test_dns_xfrout_start_with_notcallable_xfrreqdone(self):
         def noerror(msg, name, rrclass):
@@ -1154,10 +1171,20 @@ class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
             self.assertTrue(rrsets_equal(expected_rr, actual_rr))
 
     def test_axfr_normal_session(self):
+        self.assertEqual(self.get_counter('axfr_started'), 0)
+        self.assertEqual(self.get_counter('axfr_ended'), 0)
+        self.assertEqual(self.get_counter('ixfr_started'), 0)
+        self.assertEqual(self.get_counter('ixfr_ended'), 0)
         XfroutSession._handle(self.xfrsess)
         response = self.sock.read_msg(Message.PRESERVE_ORDER);
         self.assertEqual(Rcode.NOERROR(), response.get_rcode())
         self.check_axfr_stream(response)
+        self.assertEqual(self.xfrsess._request_type, RRType.AXFR())
+        self.assertNotEqual(self.xfrsess._request_type, RRType.IXFR())
+        self.assertEqual(self.get_counter('axfr_started'), 1)
+        self.assertEqual(self.get_counter('axfr_ended'), 1)
+        self.assertEqual(self.get_counter('ixfr_started'), 0)
+        self.assertEqual(self.get_counter('ixfr_ended'), 0)
 
     def test_ixfr_to_axfr(self):
         self.xfrsess._request_data = \
@@ -1176,6 +1203,10 @@ class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
         # two beginning and trailing SOAs.
         self.xfrsess._request_data = \
             self.create_request_data(ixfr=IXFR_OK_VERSION)
+        self.assertEqual(self.get_counter('axfr_started'), 0)
+        self.assertEqual(self.get_counter('axfr_ended'), 0)
+        self.assertEqual(self.get_counter('ixfr_started'), 0)
+        self.assertEqual(self.get_counter('ixfr_ended'), 0)
         XfroutSession._handle(self.xfrsess)
         response = self.sock.read_msg(Message.PRESERVE_ORDER)
         actual_records = response.get_section(Message.SECTION_ANSWER)
@@ -1191,6 +1222,12 @@ class TestXfroutSessionWithSQLite3(TestXfroutSessionBase):
         self.assertEqual(len(expected_records), len(actual_records))
         for (expected_rr, actual_rr) in zip(expected_records, actual_records):
             self.assertTrue(rrsets_equal(expected_rr, actual_rr))
+        self.assertNotEqual(self.xfrsess._request_type, RRType.AXFR())
+        self.assertEqual(self.xfrsess._request_type, RRType.IXFR())
+        self.assertEqual(self.get_counter('axfr_started'), 0)
+        self.assertEqual(self.get_counter('axfr_ended'), 0)
+        self.assertEqual(self.get_counter('ixfr_started'), 1)
+        self.assertEqual(self.get_counter('ixfr_ended'), 1)
 
     def ixfr_soa_only_common_checks(self, request_serial):
         self.xfrsess._request_data = \
@@ -1578,9 +1615,9 @@ class MyXfroutServer(XfroutServer):
 
 class TestXfroutCounter(unittest.TestCase):
     def setUp(self):
-        statistics_spec = \
-            isc.config.module_spec_from_file(\
-            xfrout.SPECFILE_LOCATION).get_statistics_spec()
+        self._module_spec = isc.config.module_spec_from_file(\
+            xfrout.SPECFILE_LOCATION)
+        statistics_spec = self._module_spec.get_statistics_spec()
         self.xfrout_counter = XfroutCounter(statistics_spec)
         self._counters = isc.config.spec_name_list(\
             isc.config.find_spec_part(\
@@ -1591,22 +1628,23 @@ class TestXfroutCounter(unittest.TestCase):
         self._cycle = 10000 # number of counting per thread
 
     def test_get_default_statistics_data(self):
-        self.assertEqual(self.xfrout_counter._get_default_statistics_data(),
-                         {XfroutCounter.perzone_prefix: {
-                            XfroutCounter.entire_server: \
-                              dict([(cnt, 0) for cnt in self._counters])
-                         }})
-
-    def setup_incrementer(self, incrementer):
+        self.assertTrue(\
+            self._module_spec.validate_statistics(\
+                True,
+                self.xfrout_counter._get_default_statistics_data(),
+                )
+            )
+
+    def setup_incrementer(self, incrementer, *args):
         self._started.wait()
-        for i in range(self._cycle): incrementer(TEST_ZONE_NAME_STR)
+        for i in range(self._cycle): incrementer(*args)
 
-    def start_incrementer(self, incrementer):
+    def start_incrementer(self, incrementer, *args):
         threads = []
         for i in range(self._number):
             threads.append(threading.Thread(\
-                    target=self.setup_incrementer,\
-                        args=(incrementer,)\
+                    target=self.setup_incrementer, \
+                        args=(incrementer,) + args \
                         ))
         for th in threads: th.start()
         self._started.set()
@@ -1618,24 +1656,61 @@ class TestXfroutCounter(unittest.TestCase):
                 '%s/%s/%s' % (XfroutCounter.perzone_prefix,\
                                   zone_name, counter_name))
 
-    def test_incrementers(self):
+    def test_incdecrementers(self):
+        # for per-zone counters
         result = { XfroutCounter.entire_server: {},
                    TEST_ZONE_NAME_STR: {} }
         for counter_name in self._counters:
-                incrementer = getattr(self.xfrout_counter, 'inc_%s' % counter_name)
-                self.start_incrementer(incrementer)
-                self.assertEqual(self.get_count(\
-                            TEST_ZONE_NAME_STR, counter_name), \
-                                     self._number * self._cycle)
-                self.assertEqual(self.get_count(\
-                        XfroutCounter.entire_server, counter_name), \
-                                     self._number * self._cycle)
-                result[XfroutCounter.entire_server][counter_name] = \
-                    result[TEST_ZONE_NAME_STR][counter_name] = \
-                    self._number * self._cycle
+            cntrs_xfrss = \
+                self.xfrout_counter.get_counters_for_xfroutsession()
+            cntrs_notfy = \
+                self.xfrout_counter.get_counters_for_notifyout()
+            cnt_name = 'counter_%s' % counter_name
+            incrementer = None
+            if cnt_name in cntrs_xfrss:
+                incrementer = cntrs_xfrss[cnt_name]
+            else:
+                incrementer = cntrs_notfy[cnt_name]
+            self.start_incrementer(incrementer, TEST_ZONE_NAME_STR)
+            self.assertEqual(self.get_count(\
+                        TEST_ZONE_NAME_STR, counter_name), \
+                                 self._number * self._cycle)
+            self.assertEqual(self.get_count(\
+                    XfroutCounter.entire_server, counter_name), \
+                                 self._number * self._cycle)
+            result[XfroutCounter.entire_server][counter_name] = \
+                result[TEST_ZONE_NAME_STR][counter_name] = \
+                self._number * self._cycle
+        statistics_data = {XfroutCounter.perzone_prefix: result}
+
+        # for {a|i}xfrrunning counters
+        for counter_name in self.xfrout_counter._xfrrunning_names:
+            incrementer = \
+                dict(self.xfrout_counter.get_counters_for_xfroutsession(), \
+                         **self.xfrout_counter.get_counters_for_notifyout())\
+                         ['inc_%s' % counter_name]
+            self.start_incrementer(incrementer)
+            self.assertEqual(
+                self.xfrout_counter.get_statistics()[counter_name],
+                self._number * self._cycle
+                )
+            decrementer = \
+                dict(self.xfrout_counter.get_counters_for_xfroutsession(), \
+                         **self.xfrout_counter.get_counters_for_notifyout())\
+                         ['dec_%s' % counter_name]
+            self.start_incrementer(decrementer)
+            self.assertEqual(
+                self.xfrout_counter.get_statistics()[counter_name],
+                0)
+            statistics_data[counter_name] = 0
         self.assertEqual(
             self.xfrout_counter.get_statistics(),
-            {XfroutCounter.perzone_prefix: result})
+            statistics_data)
+        self.assertTrue(\
+            self._module_spec.validate_statistics(\
+                True, statistics_data
+                )
+            )
 
     def test_add_perzone_counter(self):
         for counter_name in self._counters:
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index d3141ad..835696e 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -1,6 +1,6 @@
 #!@PYTHON@
 
-# Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2010-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
@@ -154,7 +154,7 @@ def get_soa_serial(soa_rdata):
 class XfroutSession():
     def __init__(self, sock_fd, request_data, server, tsig_key_ring, remote,
                  default_acl, zone_config, client_class=DataSourceClient,
-                 counter_xfrrej=None, counter_xfrreqdone=None):
+                 **counters):
         self._sock_fd = sock_fd
         self._request_data = request_data
         self._server = server
@@ -169,10 +169,13 @@ class XfroutSession():
         self.ClientClass = client_class # parameterize this for testing
         self._soa = None # will be set in _xfrout_setup or in tests
         self._jnl_reader = None # will be set to a reader for IXFR
-        # Set counter handlers for counting Xfr requests. An argument
-        # is required for zone name.
-        self._counter_xfrrej = counter_xfrrej
-        self._counter_xfrreqdone = counter_xfrreqdone
+        # Extract counter handler from the `counters` argument and add
+        # it to the class attribute of the name whose prefix is
+        # '_counter_' '_inc_' or '_dec_'
+        for (k, v) in counters.items():
+            if k.find('counter_') == 0 or k.find('inc_') == 0 \
+                    or k.find('dec_') == 0:
+                setattr(self, "_%s" % k, v)
         self._handle()
 
     def create_tsig_ctx(self, tsig_record, tsig_key_ring):
@@ -275,9 +278,8 @@ class XfroutSession():
                          format_zone_str(zone_name, zone_class))
             return None, None
         elif acl_result == REJECT:
-            if self._counter_xfrrej is not None:
-                # count rejected Xfr request by each zone name
-                self._counter_xfrrej(zone_name.to_text())
+            # count rejected Xfr request by each zone name
+            self._counter_xfrrej(zone_name.to_text())
             logger.debug(DBG_XFROUT_TRACE, XFROUT_QUERY_REJECTED,
                          self._request_type, format_addrinfo(self._remote),
                          format_zone_str(zone_name, zone_class))
@@ -527,15 +529,25 @@ class XfroutSession():
             return self._reply_query_with_error_rcode(msg, sock_fd, rcode_)
 
         try:
+            # increment Xfr starts by RRType
+            if self._request_type == RRType.AXFR():
+                self._inc_axfr_running()
+            else:
+                self._inc_ixfr_running()
             logger.info(XFROUT_XFR_TRANSFER_STARTED, self._request_typestr,
                         format_addrinfo(self._remote), zone_str)
             self._reply_xfrout_query(msg, sock_fd)
         except Exception as err:
             logger.error(XFROUT_XFR_TRANSFER_ERROR, self._request_typestr,
                     format_addrinfo(self._remote), zone_str, err)
-        if self._counter_xfrreqdone is not None:
-            # count done Xfr requests by each zone name
-            self._counter_xfrreqdone(zone_name.to_text())
+        finally:
+            # decrement Xfr starts by RRType
+            if self._request_type == RRType.AXFR():
+                self._dec_axfr_running()
+            else:
+                self._dec_ixfr_running()
+        # count done Xfr requests by each zone name
+        self._counter_xfrreqdone(zone_name.to_text())
         logger.info(XFROUT_XFR_TRANSFER_DONE, self._request_typestr,
                     format_addrinfo(self._remote), zone_str)
 
@@ -948,6 +960,8 @@ class XfroutCounter:
         zones/example.com./notifyoutv6
         zones/example.com./xfrrej
         zones/example.com./xfrreqdone
+        ixfr_running
+        axfr_running
     """
     # '_SERVER_' is a special zone name representing an entire
     # count. It doesn't mean a specific zone, but it means an
@@ -959,8 +973,15 @@ class XfroutCounter:
         self._statistics_spec = statistics_spec
         # holding statistics data for Xfrout module
         self._statistics_data = {}
+        self._counters_for_xfroutsession = {}
+        self._counters_for_notifyout = {}
+        self._xfrrunning_names = [
+            n for n in isc.config.spec_name_list\
+                (self._statistics_spec) \
+                if n.find('xfr_running') == 1 ]
         self._lock = threading.RLock()
         self._create_perzone_incrementers()
+        self._create_xfrrunning_incdecrementers()
 
     def get_statistics(self):
         """Calculates an entire server counts, and returns statistics
@@ -971,9 +992,9 @@ class XfroutCounter:
         # If self._statistics_data contains nothing of zone name, it
         # returns an empty dict.
         if len(self._statistics_data) == 0: return {}
+        # for per-zone counter
         zones = {}
-        with self._lock:
-            zones = self._statistics_data[self.perzone_prefix].copy()
+        zones = self._statistics_data[self.perzone_prefix]
         # Start calculation for '_SERVER_' counts
         attrs = self._get_default_statistics_data()[self.perzone_prefix][self.entire_server]
         statistics_data = {self.perzone_prefix: {}}
@@ -991,7 +1012,12 @@ class XfroutCounter:
             if  sum_ > 0:
                 if self.entire_server not in statistics_data[self.perzone_prefix]:
                     statistics_data[self.perzone_prefix][self.entire_server] = {}
-                statistics_data[self.perzone_prefix][self.entire_server].update({attr: sum_})
+                statistics_data[self.perzone_prefix][self.entire_server]\
+                    .update({attr:sum_})
+        # for xfrrunning incrementer/decrementer
+        for name in self._xfrrunning_names:
+            if name in self._statistics_data:
+                statistics_data[name] = self._statistics_data[name]
         return statistics_data
 
     def _get_default_statistics_data(self):
@@ -1021,11 +1047,51 @@ class XfroutCounter:
                 with self._lock:
                     self._add_perzone_counter(zone_name)
                     self._statistics_data[self.perzone_prefix][zone_name][counter_name] += step
-            setattr(self, 'inc_%s' % item, __perzone_incrementer)
-
+            if 'notifyout' in item:
+                self._counters_for_notifyout['counter_%s' % item] \
+                    = __perzone_incrementer
+            else:
+                self._counters_for_xfroutsession['counter_%s' % item] \
+                    = __perzone_incrementer
+
+    def _create_xfrrunning_incdecrementers(self):
+        """Creates increment/decrement method of (a|i)xfr_running
+        based on the spec file. Incrementer can be accessed by name
+        "inc_${item_name}". Decrementer can be accessed by name
+        "dec_${item_name}". Both of them are passed to the
+        XfroutSession as counter handlers."""
+        # can be accessed by the name 'inc_xxx' or 'dec_xxx'
+        for item in self._xfrrunning_names:
+            def __xfrrunning_incrementer(counter_name=item, step=1):
+                """A incrementer for axfr or ixfr running. Locks the thread
+                because it is considered to be invoked by a multi-threading
+                caller."""
+                with self._lock:
+                    self._add_xfrrunning_counter(counter_name)
+                    self._statistics_data[counter_name] += step
+            def __xfrrunning_decrementer(counter_name=item, step=-1):
+                """A decrementer for axfr or ixfr running. Locks the thread
+                because it is considered to be invoked by a multi-threading
+                caller."""
+                with self._lock:
+                    self._statistics_data[counter_name] += step
+            self._counters_for_xfroutsession['inc_%s' % item] \
+                = __xfrrunning_incrementer
+            self._counters_for_xfroutsession['dec_%s' % item] \
+                = __xfrrunning_decrementer
+
+    def get_counters_for_xfroutsession(self):
+        """Returns counters, incrementers, and decrementers to be
+        passed to XfroutSession/UnixSockServer class."""
+        return self._counters_for_xfroutsession
+
+    def get_counters_for_notifyout(self):
+        """Returns counters handlers to be passed to NotifyOut
+        class."""
+        return self._counters_for_notifyout
 
     def _add_perzone_counter(self, zone):
-        """Adds named_set-type counter for each zone name"""
+        """Adds a named_set-type counter for each zone name."""
         try:
             self._statistics_data[self.perzone_prefix][zone]
         except KeyError:
@@ -1041,6 +1107,17 @@ class XfroutCounter:
                                     (self.perzone_prefix, zone, id_),
                                 spec['item_default'])
 
+    def _add_xfrrunning_counter(self, counter_name):
+        """Adds a counter for counting (a|i)xfr_running"""
+        try:
+            self._statistics_data[counter_name]
+        except KeyError:
+            # examines the names of xfer running
+            for n in self._xfrrunning_names:
+                spec = isc.config.find_spec_part(self._statistics_spec, n)
+                isc.cc.data.set(self._statistics_data, n, \
+                                    spec['item_default'])
+
 class XfroutServer:
     def __init__(self):
         self._unix_socket_server = None
@@ -1064,8 +1141,7 @@ class XfroutServer:
             self._shutdown_event,
             self._config_data,
             self._cc,
-            counter_xfrrej=self._counter.inc_xfrrej,
-            counter_xfrreqdone=self._counter.inc_xfrreqdone
+            **self._counter.get_counters_for_xfroutsession()
             )
         listener = threading.Thread(target=self._unix_socket_server.serve_forever)
         listener.start()
@@ -1074,8 +1150,7 @@ class XfroutServer:
         datasrc = self._unix_socket_server.get_db_file()
         self._notifier = notify_out.NotifyOut(
             datasrc,
-            counter_notifyoutv4=self._counter.inc_notifyoutv4,
-            counter_notifyoutv6=self._counter.inc_notifyoutv6
+            **self._counter.get_counters_for_notifyout()
             )
         if 'also_notify' in self._config_data:
             for slave in self._config_data['also_notify']:
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index c59dee8..1d24be2 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -172,6 +172,22 @@
               }
             ]
           }
+        },
+        {
+          "item_name": "ixfr_running",
+          "item_type": "integer",
+          "item_optional": false,
+          "item_default": 0,
+          "item_title": "IXFR running",
+          "item_description": "Number of IXFRs in progress"
+        },
+        {
+          "item_name": "axfr_running",
+          "item_type": "integer",
+          "item_optional": false,
+          "item_default": 0,
+          "item_title": "AXFR running",
+          "item_description": "Number of AXFRs in progress"
         }
       ]
   }
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index ff4a79a..9dc8d99 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -11,3 +11,18 @@ missingInclude
 //
 //    // cppcheck-suppress duplicateExpression
 //    EXPECT_FALSE(small_name < small_name);
+
+// With cppcheck 1.56, there are a number of false positives, which
+// All of these should be checked and hopefully removed after upgrading
+// cppcheck past 1.56
+
+// eraseDereference: This is a known false positive, which has been
+// fixed in the current development version of cppcheck
+eraseDereference
+
+// Unused functions: there suddenly are a lot of unused function errors
+// We could address those by adding for instance early declarations or
+// (unnecessary) header files, but they were all somewhat false positives
+// When we upgrade past 1.56, we should re-check this, and perhaps enable
+// unused-functions again.
+unusedFunction
diff --git a/src/lib/acl/dns.cc b/src/lib/acl/dns.cc
index d16ec65..5466dad 100644
--- a/src/lib/acl/dns.cc
+++ b/src/lib/acl/dns.cc
@@ -12,12 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <boost/shared_ptr.hpp>
-
 #include <exceptions/exceptions.h>
 
 #include <dns/name.h>
@@ -31,6 +25,13 @@
 #include <acl/loader.h>
 #include <acl/logic_check.h>
 
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <memory>
+#include <string>
+#include <vector>
+
 using namespace std;
 using namespace isc::dns;
 using namespace isc::data;
@@ -106,10 +107,12 @@ internal::RequestCheckCreator::create(const string& name,
 
 RequestLoader&
 getRequestLoader() {
-    static RequestLoader* loader(NULL);
-    if (loader == NULL) {
+    // To ensure that the singleton gets destroyed at the end of the
+    // program's lifetime, we put it in a static scoped_ptr.
+    static boost::scoped_ptr<RequestLoader> loader(NULL);
+    if (loader.get() == NULL) {
         // Creator registration may throw, so we first store the new loader
-        // in an auto pointer in order to provide the strong exception
+        // in a second auto pointer in order to provide the strong exception
         // guarantee.
         auto_ptr<RequestLoader> loader_ptr =
             auto_ptr<RequestLoader>(new RequestLoader(REJECT));
@@ -129,7 +132,7 @@ getRequestLoader() {
                 new LogicCreator<AllOfSpec, RequestContext>("ALL")));
 
         // From this point there shouldn't be any exception thrown
-        loader = loader_ptr.release();
+        loader.reset(loader_ptr.release());
     }
 
     return (*loader);
diff --git a/src/lib/acl/dns.h b/src/lib/acl/dns.h
index d08fcf3..5ea0b13 100644
--- a/src/lib/acl/dns.h
+++ b/src/lib/acl/dns.h
@@ -136,10 +136,6 @@ public:
     virtual boost::shared_ptr<RequestCheck>
     create(const std::string& name, isc::data::ConstElementPtr definition,
            const acl::Loader<RequestContext>& loader);
-
-    /// Until we are sure how the various rules work for this case, we won't
-    /// allow unexpected special interpretation for list definitions.
-    virtual bool allowListAbbreviation() const { return (false); }
 };
 } // end of namespace "internal"
 
diff --git a/src/lib/acl/tests/dns_test.cc b/src/lib/acl/tests/dns_test.cc
index b3ddbf4..8d6ee70 100644
--- a/src/lib/acl/tests/dns_test.cc
+++ b/src/lib/acl/tests/dns_test.cc
@@ -62,6 +62,18 @@ TEST(DNSACL, getRequestLoader) {
                                               "  \"from\": \"192.0.2.1\"}]")));
 }
 
+// Check we can abbreviate the IP address lists and TSIG keys
+TEST(DNSACL, abbreviated) {
+    dns::RequestLoader* l(&getRequestLoader());
+
+    EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\","
+                                              "  \"from\": [\"127.0.0.1\","
+                                              "             \"::1\"]}]")));
+    EXPECT_NO_THROW(l->load(Element::fromJSON("[{\"action\": \"DROP\","
+                                              "  \"key\": [\"key.example.\","
+                                              "            \"other.\"]}]")));
+}
+
 class RequestCheckCreatorTest : public ::testing::Test {
 protected:
     dns::internal::RequestCheckCreator creator_;
@@ -78,7 +90,7 @@ TEST_F(RequestCheckCreatorTest, names) {
 }
 
 TEST_F(RequestCheckCreatorTest, allowListAbbreviation) {
-    EXPECT_FALSE(creator_.allowListAbbreviation());
+    EXPECT_TRUE(creator_.allowListAbbreviation());
 }
 
 // The following two tests check the creator for the form of
diff --git a/src/lib/asiolink/io_address.h b/src/lib/asiolink/io_address.h
index c40e5b9..a3bb61a 100644
--- a/src/lib/asiolink/io_address.h
+++ b/src/lib/asiolink/io_address.h
@@ -131,7 +131,7 @@ public:
         return equals(other);
     }
 
-    // \brief Compare addresses for inequality
+    /// \brief Compare addresses for inequality
     ///
     /// \param other Address to compare against.
     ///
@@ -140,7 +140,58 @@ public:
         return (!equals(other));
     }
 
-    // \brief Compare addresses for inequality
+    /// \brief Checks if one address is smaller than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if this address is smaller than the other address.
+    ///
+    /// It is useful for comparing which address is bigger.
+    /// Operations within one protocol family are obvious.
+    /// Comparisons between v4 and v6 will allways return v4
+    /// being smaller. This follows boost::asio::ip implementation
+    bool lessThan(const IOAddress& other) const {
+        if (this->getFamily() == other.getFamily()) {
+            if (this->getFamily() == AF_INET6) {
+                return (this->asio_address_.to_v6() < other.asio_address_.to_v6());
+            } else {
+                return (this->asio_address_.to_v4() < other.asio_address_.to_v4());
+            }
+        }
+        return (this->getFamily() < other.getFamily());
+    }
+
+    /// \brief Checks if one address is smaller or equal than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if this address is smaller than the other address.
+    bool smallerEqual(const IOAddress& other) const {
+        if (equals(other)) {
+            return (true);
+        }
+        return (lessThan(other));
+    }
+
+    /// \brief Checks if one address is smaller than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// See \ref smaller_than method for details.
+    bool operator<(const IOAddress& other) const {
+        return (lessThan(other));
+    }
+
+    /// \brief Checks if one address is smaller or equal than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// See \ref smaller_equal method for details.
+    bool operator<=(const IOAddress& other) const {
+        return (smallerEqual(other));
+    }
+
+    /// \brief Compare addresses for inequality
     ///
     /// \param other Address to compare against.
     ///
diff --git a/src/lib/asiolink/tests/io_address_unittest.cc b/src/lib/asiolink/tests/io_address_unittest.cc
index 4322283..a1a9076 100644
--- a/src/lib/asiolink/tests/io_address_unittest.cc
+++ b/src/lib/asiolink/tests/io_address_unittest.cc
@@ -99,3 +99,35 @@ TEST(IOAddressTest, uint32) {
 
     EXPECT_EQ(addr3.toText(), "192.0.2.5");
 }
+
+TEST(IOAddressTest, lessThanEqual) {
+    IOAddress addr1("192.0.2.5");
+    IOAddress addr2("192.0.2.6");
+    IOAddress addr3("0.0.0.0");
+
+    IOAddress addr4("::");
+    IOAddress addr5("2001:db8::1");
+    IOAddress addr6("2001:db8::1:0");
+    IOAddress addr7("2001:db8::1:0"); // the same as 6
+
+    // v4 comparisons
+    EXPECT_TRUE(addr1 < addr2);
+    EXPECT_FALSE(addr2 < addr1);
+    EXPECT_FALSE(addr2 <= addr1);
+    EXPECT_TRUE(addr3 < addr1);
+    EXPECT_TRUE(addr3 < addr2);
+    EXPECT_TRUE(addr3 <= addr2);
+
+    // v6 comparisons
+    EXPECT_TRUE(addr4 < addr5);
+    EXPECT_TRUE(addr5 < addr6);
+    EXPECT_FALSE(addr6 < addr5);
+    EXPECT_FALSE(addr6 <= addr5);
+
+    // v4 to v6 - v4 should always be smaller
+    EXPECT_TRUE(addr1 < addr4);
+    EXPECT_TRUE(addr3 < addr4);
+    EXPECT_TRUE(addr2 < addr5);
+
+    EXPECT_TRUE(addr6 <= addr7);
+}
diff --git a/src/lib/cache/tests/rrset_entry_unittest.cc b/src/lib/cache/tests/rrset_entry_unittest.cc
index c7c3c6e..a6ac27e 100644
--- a/src/lib/cache/tests/rrset_entry_unittest.cc
+++ b/src/lib/cache/tests/rrset_entry_unittest.cc
@@ -50,9 +50,6 @@ class DerivedRRsetEntry: public RRsetEntry {
 public:
     DerivedRRsetEntry(const isc::dns::RRset& rrset, const RRsetTrustLevel& level) : RRsetEntry(rrset, level) {};
 
-    void updateTTLForTest() {
-
-    }
 };
 
 #define TEST_TTL 100
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 6ec243a..47b1eb1 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -413,7 +413,7 @@ from_stringstream_number(std::istream &in, int &pos) {
             isc_throw(JSONError, std::string("Number overflow: ") + number);
         }
     }
-    
+
     if (is_double) {
         return (Element::create(d));
     } else {
@@ -501,7 +501,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
 
             ConstElementPtr value = Element::fromJSON(in, file, line, pos);
             map->set(key, value);
-            
+
             skip_to(in, file, line, pos, ",}", WHITESPACE);
             c = in.get();
             pos++;
@@ -942,7 +942,7 @@ removeIdentical(ConstElementPtr a, ConstElementPtr b) {
     if (!b) {
         return (result);
     }
-    
+
     if (a->getType() != Element::map || b->getType() != Element::map) {
         isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
     }
@@ -965,7 +965,7 @@ merge(ElementPtr element, ConstElementPtr other) {
         other->getType() != Element::map) {
         isc_throw(TypeError, "merge arguments not MapElements");
     }
-    
+
     const std::map<std::string, ConstElementPtr>& m = other->mapValue();
     for (std::map<std::string, ConstElementPtr>::const_iterator it = m.begin();
          it != m.end() ; ++it) {
diff --git a/src/lib/config/tests/testdata/Makefile.am b/src/lib/config/tests/testdata/Makefile.am
index fce74d0..9b383d6 100644
--- a/src/lib/config/tests/testdata/Makefile.am
+++ b/src/lib/config/tests/testdata/Makefile.am
@@ -70,3 +70,4 @@ EXTRA_DIST += spec38.spec
 EXTRA_DIST += spec39.spec
 EXTRA_DIST += spec40.spec
 EXTRA_DIST += spec41.spec
+EXTRA_DIST += spec42.spec
diff --git a/src/lib/config/tests/testdata/spec32.spec b/src/lib/config/tests/testdata/spec32.spec
index 854d967..2baf1c1 100644
--- a/src/lib/config/tests/testdata/spec32.spec
+++ b/src/lib/config/tests/testdata/spec32.spec
@@ -49,6 +49,22 @@
             "item_optional": true
           }
         }
+      },
+      { "item_name": "named_set_item4",
+        "item_type": "named_set",
+        "item_optional": true,
+        "item_default": {},
+        "named_set_item_spec": {
+          "item_name": "named_set_element",
+          "item_type": "named_set",
+          "item_optional": false,
+          "item_default": { "a": 1, "b": 2 },
+          "named_set_item_spec":
+          { "item_name": "named_set_element",
+            "item_type": "integer",
+            "item_optional": true
+          }
+        }
       }
     ]
   }
diff --git a/src/lib/config/tests/testdata/spec42.spec b/src/lib/config/tests/testdata/spec42.spec
new file mode 100644
index 0000000..d822465
--- /dev/null
+++ b/src/lib/config/tests/testdata/spec42.spec
@@ -0,0 +1,17 @@
+{
+  "module_spec": {
+    "module_name": "Spec42",
+    "config_data": [
+      { "item_name": "list_item",
+        "item_type": "list",
+        "item_optional": true,
+        "list_item_spec": {
+          "item_name": "list_element",
+          "item_type": "string",
+          "item_optional": false,
+          "item_default": ""
+        }
+      }
+    ]
+  }
+}
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index bd96838..eccc147 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . memory tests
+SUBDIRS = memory . tests
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -12,7 +12,9 @@ pkglibdir = $(libexecdir)/@PACKAGE@/backends
 datasrc_config.h: datasrc_config.h.pre
 	$(SED) -e "s|@@PKGLIBDIR@@|$(pkglibdir)|" datasrc_config.h.pre >$@
 
-static.zone: static.zone.pre
+# The top config.h defines "PACKAGE_STRING".  When it's changed we neeed to
+# regenerate this zone file.
+static.zone: static.zone.pre $(top_builddir)/config.h $(top_srcdir)/AUTHORS
 	$(SED) -e "s|@@VERSION_STRING@@|$(PACKAGE_STRING)|" $(srcdir)/static.zone.pre >$@
 	$(SED) -e 's/\(.*\)/AUTHORS.BIND.	0	CH	TXT	"\1"/' $(top_srcdir)/AUTHORS >>$@
 
@@ -21,11 +23,7 @@ CLEANFILES += datasrc_config.h
 CLEANFILES += static.zone
 
 lib_LTLIBRARIES = libb10-datasrc.la
-libb10_datasrc_la_SOURCES = data_source.h data_source.cc
-libb10_datasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
-libb10_datasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
-libb10_datasrc_la_SOURCES += query.h query.cc
-libb10_datasrc_la_SOURCES += cache.h cache.cc
+libb10_datasrc_la_SOURCES = data_source.h
 libb10_datasrc_la_SOURCES += rbnode_rrset.h
 libb10_datasrc_la_SOURCES += rbtree.h
 libb10_datasrc_la_SOURCES += zonetable.h zonetable.cc
@@ -64,6 +62,7 @@ libb10_datasrc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
 libb10_datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+libb10_datasrc_la_LIBADD += $(builddir)/memory/libdatasrc_memory.la
 libb10_datasrc_la_LIBADD += $(SQLITE_LIBS)
 
 BUILT_SOURCES = datasrc_config.h datasrc_messages.h datasrc_messages.cc
diff --git a/src/lib/datasrc/cache.cc b/src/lib/datasrc/cache.cc
deleted file mode 100644
index d88e649..0000000
--- a/src/lib/datasrc/cache.cc
+++ /dev/null
@@ -1,396 +0,0 @@
-// Copyright (C) 2010  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 <stdint.h>
-
-#include <map>
-
-#include <dns/question.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-
-#include <list>
-
-#include <datasrc/cache.h>
-#include <datasrc/logger.h>
-
-using namespace std;
-using namespace isc::dns;
-
-namespace isc {
-namespace datasrc {
-
-/// \brief A \c CacheEntry object contains the data stored with
-/// each \c CacheNode: a pointer to the cached RRset (empty in
-/// the case of a negative cache entry), and a copy of the
-/// query-response flags that were returned when the RRset
-/// was originally looked up in the low-level data source.
-class CacheEntry {
-private:
-    /// The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    CacheEntry(const CacheEntry& source);
-    CacheEntry& operator=(const CacheEntry& source);
-
-public:
-    CacheEntry(RRsetPtr r, uint32_t f) : rrset(r), flags(f) {};
-
-    RRsetPtr rrset;
-    uint32_t flags;
-};
-
-typedef boost::shared_ptr<CacheEntry> CacheEntryPtr;
-
-/// \brief A \c CacheNode is a node in the \c HotCache LRU queue.  It
-/// contains a pointer to a \c CacheEntry, a reference to the \c Question
-/// that we are answering, a lifespan during which this entry remains
-/// valid, and pointers to the next and previous entries in the list.
-class CacheNode {
-private:
-    /// \name Constructors and Assignment Operator
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-    CacheNode(const CacheNode& source);
-    CacheNode& operator=(const CacheNode& source);
-
-public:
-    /// \brief Constructor for positive cache entry.
-    ///
-    /// \param rrset The \c RRset to cache.
-    /// \param flags The query response flags returned from the low-level
-    /// data source when this \c RRset was looked up.
-    /// \param lifespan How long the cache node is to be considered valid.
-    CacheNode(const RRsetPtr rrset, uint32_t flags, time_t lifespan);
-
-    /// \brief Constructor for negative cache entry.
-    ///
-    /// \param name Query name
-    /// \param rrclass Query class
-    /// \param rrtype Query type
-    /// \param flags Query response flags returned from the low-level
-    /// data source, indicating why this lookup failed (name not found,
-    /// type not found, etc).
-    /// \param lifespan How long the cache node is to be considered valid.
-    CacheNode(const Name& name,
-              const RRClass& rrclass,
-              const RRType& rrtype,
-              uint32_t flags,
-              time_t lifespan);
-    //@}
-
-    /// \name Getter and Setter Methods
-    //@{
-    /// \brief Returns a pointer to the cached RRset (or an empty
-    /// RRsetPtr for negative cache entries).
-
-    /// \return \c RRsetPtr
-    RRsetPtr getRRset() const { return (entry->rrset); }
-
-    /// \brief Returns name associated with cached node
-    ///
-    /// This is the name associated with the RRset if it is a positive
-    /// entry, and the associated question name if the RRSet is NULL
-    /// and this is a negative entry (together with an indication that
-    /// this is a negative entry).
-    string getNodeName() const {
-        if (getRRset()) {
-            return (getRRset()->getName().toText());
-        }
-        return (std::string("negative entry for ") + question.toText());
-    }
-
-    /// \brief Returns the query response flags associated with the data.
-    ///
-    /// \return \c uint32_t
-    uint32_t getFlags() const { return (entry->flags); }
-
-    /// \brief Is this record still valid?
-    ///
-    /// \return True if the expiration time has not yet passed,
-    /// or false if it has.
-    bool isValid() const;
-    //@}
-
-    // An iterator referencing this entry in the LRU list. This
-    // permits unit-time removal using list::erase().
-    list<CacheNodePtr>::iterator lru_entry_;
-
-    /// The \c Question (name/rrclass/rrtype) answered by this cache node
-    const isc::dns::Question question;
-
-private:
-    // The cached RRset data
-    CacheEntryPtr entry;
-
-    // When this record should be discarded
-    time_t expiry;
-};
-
-// CacheNode constructor for a positive cache entry
-CacheNode::CacheNode(const RRsetPtr rrset, const uint32_t flags,
-                     const time_t lifespan) :
-    question(Question(rrset->getName(), rrset->getClass(), rrset->getType()))
-{
-    const time_t now = time(NULL);
-    expiry = now + lifespan;
-
-    entry = CacheEntryPtr(new CacheEntry(rrset, flags));
-}
-
-// CacheNode constructor for a negative cache entry
-CacheNode::CacheNode(const Name& name,
-                     const RRClass& rrclass,
-                     const RRType& rrtype,
-                     const uint32_t flags,
-                     const time_t lifespan) :
-    question(Question(name, rrclass, rrtype))
-{
-    const time_t now = time(NULL);
-    expiry = now + lifespan;
-
-    entry = CacheEntryPtr(new CacheEntry(RRsetPtr(), flags));
-}
-// Returns true if the node has not yet expired.
-bool
-CacheNode::isValid() const {
-    const time_t now = time(NULL);
-    return (now < expiry);
-}
-
-/// This class abstracts the implementation details for \c HotCache.
-///
-/// Each node inserted into the cache is placed at the head of a
-/// doubly-linked list.  Whenever that node is retrieved from the cache,
-/// it is again moved to the head of the list.  When the configured
-/// number of slots in the cache has been exceeded, the least recently
-/// used nodes will be removed from the tail of the list.
-///
-/// A pointer to each cache node is also placed in a \c std::map object,
-/// keyed by \c isc::dns::Question.  This allows retrieval of data in
-/// (usually) logarithmic time.  (Possible TODO item: replace this with a
-/// hash table instead.)
-class HotCacheImpl {
-public:
-    HotCacheImpl(int slots, bool enabled);
-
-    // The LRU list
-    list<CacheNodePtr> lru_;
-
-    // Flag to indicate whether the cache is enabled
-    bool enabled_;
-
-    // The number of available slots in the LRU list.  (If zero,
-    // then the list is unbounded; otherwise, items are removed
-    // from the tail of the list whenever it grows past slots_.)
-    int slots_;
-
-    // The number of items currently in the list.
-    int count_;
-
-    // Map from query tuple to cache node pointer, allowing fast retrieval
-    // of data without a linear search of the LRU list
-    std::map<Question, CacheNodePtr> map_;
-
-    // Move a node to the front of the LRU list.
-    void promote(CacheNodePtr node);
-
-    // Remove a node from the cache.
-    void remove(ConstCacheNodePtr node);
-
-    // Insert a node into the cache (called by both cache() and ncache())
-    void insert(CacheNodePtr node);
-};
-
-// HotCacheImpl constructor
-HotCacheImpl::HotCacheImpl(int slots, bool enabled) :
-    enabled_(enabled), slots_(slots), count_(0)
-{
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_CREATE);
-}
-
-// Insert a cache node into the cache
-inline void
-HotCacheImpl::insert(const CacheNodePtr node) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_INSERT).
-        arg(node->getNodeName());
-    std::map<Question, CacheNodePtr>::const_iterator iter;
-    iter = map_.find(node->question);
-    if (iter != map_.end()) {
-        CacheNodePtr old = iter->second;
-        if (old && old->isValid()) {
-            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_OLD_FOUND)
-                      .arg(node->getNodeName());
-            remove(old);
-        }
-    }
-
-    lru_.push_front(node);
-    node->lru_entry_ = lru_.begin();
-
-    map_[node->question] = node;
-    ++count_;
-
-    if (slots_ != 0 && count_ > slots_) {
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FULL);
-        remove(lru_.back());
-    }
-}
-
-// Promote a node to the head of the LRU list
-void
-HotCacheImpl::promote(CacheNodePtr node) {
-    if (!node) {
-        return;
-    }
-    if (node->lru_entry_ == lru_.begin()) {
-        return;
-    }
-    lru_.splice(lru_.begin(), lru_, node->lru_entry_); // move node to front
-    node->lru_entry_ = lru_.begin();
-}
-
-// Remove a node from the LRU list and the map
-void
-HotCacheImpl::remove(ConstCacheNodePtr node) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_REMOVE).
-        arg(node->getNodeName());
-    lru_.erase(node->lru_entry_);
-    map_.erase(node->question);
-    --count_;
-}
-
-// HotCache constructor
-HotCache::HotCache(const int slots) {
-    impl_ = new HotCacheImpl(slots, true);
-}
-
-// HotCache destructor
-HotCache::~HotCache() {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_CACHE_DESTROY);
-    delete impl_;
-}
-
-// Add a positive entry to the cache
-void
-HotCache::addPositive(RRsetPtr rrset, const uint32_t flags,
-                      const time_t lifespan)
-{
-    if (!impl_->enabled_) {
-        return;
-    }
-
-    impl_->insert(CacheNodePtr(new CacheNode(rrset, flags, lifespan)));
-}
-
-// Add a negative entry to the cache
-void
-HotCache::addNegative(const Name& name, const RRClass &rrclass,
-                      const RRType& rrtype, const uint32_t flags,
-                      const time_t lifespan)
-{
-    if (!impl_->enabled_) {
-        return;
-    }
-
-    if (rrtype == RRType::ANY() || rrclass == RRClass::ANY()) {
-        return;
-    }
-
-    impl_->insert(CacheNodePtr(new CacheNode(name, rrclass, rrtype,
-                                             flags, lifespan)));
-}
-
-// Try to retrieve an entry from the cache, returning true if
-// it was found and valid.
-bool
-HotCache::retrieve(const Name& n, const RRClass& c, const RRType& t,
-                   RRsetPtr& rrset, uint32_t& flags)
-{
-    if (!impl_->enabled_) {
-        return (false);
-    }
-
-    std::map<Question, CacheNodePtr>::const_iterator iter;
-    iter = impl_->map_.find(Question(n, c, t));
-    if (iter == impl_->map_.end()) {
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_NOT_FOUND).arg(n);
-        return (false);
-    }
-
-    CacheNodePtr node = iter->second;
-
-    if (node->isValid()) {
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_FOUND).arg(n);
-        impl_->promote(node);
-        rrset = node->getRRset();
-        flags = node->getFlags();
-        return (true);
-    }
-
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_CACHE_EXPIRED).arg(n);
-    impl_->remove(node);
-    return (false);
-}
-
-// Set the number of slots in the cache.
-void
-HotCache::setSlots(const int slots) {
-    impl_->slots_ = slots;
-
-    if (!impl_->enabled_) {
-        return;
-    }
-
-    logger.info(DATASRC_CACHE_SLOTS).arg(slots).arg(max(0, impl_->count_ -
-                                                        slots));
-
-    while (impl_->slots_ != 0 && impl_->count_ > impl_->slots_) {
-        impl_->remove(impl_->lru_.back());
-    }
-}
-
-// Return the number of slots in the cache
-int
-HotCache::getSlots() const {
-    return (impl_->slots_);
-}
-
-/// Enable or disable the cache
-void
-HotCache::setEnabled(const bool e) {
-    impl_->enabled_ = e;
-    if (e) {
-        logger.info(DATASRC_CACHE_ENABLE);
-    } else {
-        logger.info(DATASRC_CACHE_DISABLE);
-    }
-}
-
-/// Indicate whether the cache is enabled
-bool
-HotCache::getEnabled() const {
-    return (impl_->enabled_);
-}
-
-// Return the number of entries in the cache
-int
-HotCache::getCount() const {
-    return (impl_->count_);
-}
-
-}
-}
diff --git a/src/lib/datasrc/cache.h b/src/lib/datasrc/cache.h
deleted file mode 100644
index 054912c..0000000
--- a/src/lib/datasrc/cache.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (C) 2010  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 __CACHE_H
-#define __CACHE_H
-
-#include <time.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <dns/rrset.h>
-
-namespace isc {
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-}
-
-namespace datasrc {
-
-class CacheNode;
-typedef boost::shared_ptr<CacheNode> CacheNodePtr;
-typedef boost::shared_ptr<const CacheNode> ConstCacheNodePtr;
-
-class HotCacheImpl;
-
-/// \brief A \c HotCache is a hot-spot cache for one or more data sources.
-///
-/// A \c HotCache must be instantiated prior to creating a \c Query.
-/// The same instance should be passed to the constructor for all queries,
-/// so that all of them will be using the same cache.
-///
-/// The cache may contain positive or negative entries, indicating
-/// whether the data does or does not exist in the underlying data
-/// source.  Entries have a fixed and limited lifespan (currently
-/// set to 30 seconds, see LIFESPAN_ below).  If a cache entry is
-/// found which has exceeded its lifespan, it will not be returned
-/// to the caller--exactly as if it had not been found.
-/// 
-/// The current 30 second cache entry lifespan is experimental.  A longer
-/// lifespan would improve performance somewhat; however, longer-lived
-/// cache entries are more likely to be incorrect in the event that
-/// the underlying data source had been updated.  Depending on the
-/// frequency of queries and the frequency of updates, longer or
-/// shorter lifespans may be desirable -- it's even possible
-/// we may want the lifespan to be set differently depending on
-/// the zone or the data source (i.e., with an infinite lifespan
-/// for cached data from a static data source).  Additional benchmarking
-/// and analysis will be needed for this.
-/// 
-/// The cache may be configured with a number of available slots for
-/// for entries.  When set to a nonzero value, no more than that number
-/// of entries can exist in the cache.  If more entries are inserted,
-/// old entries will be dropped in "least recently used" order.  If
-/// set to zero, the cache size is unlimited.  The current default is
-/// based on one thousand queries per second, times the number of seconds
-/// in the cache lifespan: 30,000 slots.
-///
-/// Notes to developers: The current implementation of HotCache uses
-/// a std::map (keyed by isc::dns::Question) to locate nodes, so access
-/// will generally be in O(log n) time.  (XXX: This might be faster if a
-/// hash table were used instead.)
-///
-/// A linked list is also maintained to keep track of recent accesses
-/// to cache entries; each time an entry is accessed, it is moved to the
-/// head of the list; when entries need to be removed, they are taken
-/// from the tail of the list.  This operation is not locked.  BIND 10
-/// does not currently use threads, but if it ever does (or if libdatasrc
-/// is ever used by a threaded application), this will need to be
-/// revisited.
-class HotCache {
-private:
-    /// \name Static definitions
-    //@{
-    /// \brief Default validity period for cache entries
-    static const int LIFESPAN_ = 30;
-
-    /// \brief Default number of slots in cache
-    static const int SLOTS_ = 1000 * LIFESPAN_;
-    //@}
-
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-    HotCache(const HotCache& source);
-    HotCache& operator=(const HotCache& source);
-
-public:
-    /// \brief Constructor for HotCache
-    ///
-    /// \param slots The number of slots available in the cache.
-    HotCache(const int slots = SLOTS_);
-
-    /// \brief Destructor for HotCache
-    ~HotCache();
-    //@}
-
-    /// \name Cache Manipulation Methods
-    //@{
-    /// \brief Enter a positive cache entry.
-    ///
-    /// If an entry already exists in the cache which matches the
-    /// name/class/type of the RRset being cached, then the old entry
-    /// is removed before the the new one is inserted.  (XXX: This is
-    /// currently slightly inefficient; it would be quicker to keep the
-    /// existing node and simply update the rrset, flags, and lifespan.)
-    ///
-    /// \param rrset The \c RRset to cache.
-    /// \param flags The query response flags returned from the low-level
-    /// data source when this \c RRset was looked up.
-    /// \param lifespan How long the cache node is to be considered valid;
-    /// defaulting to 30 seconds.
-    void addPositive(isc::dns::RRsetPtr rrset,
-                     uint32_t flags,
-                     time_t lifespan = LIFESPAN_);
-
-    /// \brief Enter a negative cache entry.
-    ///
-    /// In the case of a negative cache entry there is no \c RRset to
-    /// cache, so instead a null \c RRsetPtr will be stored.  Since the
-    /// name, class, and type cannot be retrieved from an \c RRset, they
-    /// must be specified in the parameters.
-    ///
-    /// If an entry already exists in the cache which matches the
-    /// specified name/class/type, then the old entry is removed
-    /// before the the new one is inserted.  (XXX: As noted in the comments
-    /// for addPositive(), this is currently slightly inefficient.)
-    /// 
-    /// \param name Query name
-    /// \param rrclass Query class
-    /// \param rrtype Query type
-    /// \param flags Query response flags returned from the low-level
-    /// data source, indicating why this lookup failed (name not found,
-    /// type not found, etc).
-    /// \param lifespan How long the cache node is to be considered valid;
-    /// defaulting to 30 seconds.
-    ///
-    /// Note: 'rrclass' and 'rrtype' must refer to a specific class and
-    /// type; it is not meaningful to cache type or class ANY.  Currently,
-    /// this condition is silently ignored.
-    void addNegative(const isc::dns::Name& name,
-                     const isc::dns::RRClass& rrclass,
-                     const isc::dns::RRType& rrtype,
-                     uint32_t flags,
-                     time_t lifespan = LIFESPAN_);
-
-    /// \brief Retrieve (and promote) a record from the cache
-    ///
-    /// Retrieves a record from the cache matching the given 
-    /// query-tuple.  Returns true if one is found.  If it is a
-    /// positive cache entry, then 'rrset' is set to the cached
-    /// RRset.  For both positive and negative cache entries, 'flags'
-    /// is set to the query response flags.  The cache entry is 
-    /// then promoted to the head of the LRU queue.  (NOTE: Because
-    /// of this, "retrieve" cannot be implemented as a const method.)
-    ///
-    /// \param qname The query name
-    /// \param qclass The query class
-    /// \param qtype The query type
-    /// \param rrset Returns the RRset found, if any, to the caller
-    /// \param flags Returns the flags, if any, to the caller
-    ///
-    /// \return \c bool, true if data was found in the cache, false if not.
-    bool retrieve(const isc::dns::Name& qname,
-                  const isc::dns::RRClass& qclass,
-                  const isc::dns::RRType& qtype,
-                  isc::dns::RRsetPtr& rrset,
-                  uint32_t& flags);
-    //@}
-
-    /// \name Getter and Setter Methods
-    //@{
-    /// \brief Sets the number of slots in the cache.
-    ///
-    /// If slots is set to zero, the cache can grow without any imposed
-    /// limit.  If slots is to set a lower number than the cache currently
-    /// contains, then the least recently used records will be purged from
-    /// the cache until the total number of items in the cache equals slots.
-    void setSlots(int slots);
-
-    /// \brief Returns the number of slots in the cache.
-    int getSlots() const;
-
-    /// \brief Enable or disable the cache
-    void setEnabled(bool e);
-
-    /// \brief Indicate whether the cache is enabled
-    bool getEnabled() const;
-
-    /// \brief Returns the number of nodes currently stored in the cache.
-    ///
-    /// Note that this doesn't indicate how many nodes are still valid;
-    /// some may have expired.
-    int getCount() const;
-    //@}
-
-private:
-    /// \brief Hidden implementation details
-    HotCacheImpl* impl_;
-};
-
-}
-}
-
-#endif
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/datasrc/client_list.cc b/src/lib/datasrc/client_list.cc
index 3c0ea19..865a1ce 100644
--- a/src/lib/datasrc/client_list.cc
+++ b/src/lib/datasrc/client_list.cc
@@ -12,10 +12,12 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <util/memory_segment_local.h>
+
 #include "client_list.h"
 #include "client.h"
 #include "factory.h"
-#include "memory_datasrc.h"
+#include "memory/memory_client.h"
 #include "logger.h"
 #include <dns/masterload.h>
 
@@ -25,32 +27,58 @@
 using namespace isc::data;
 using namespace isc::dns;
 using namespace std;
+using isc::util::MemorySegment;
 using boost::lexical_cast;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
+using isc::datasrc::memory::InMemoryClient;
 
 namespace isc {
 namespace datasrc {
 
 ConfigurableClientList::DataSourceInfo::DataSourceInfo(
     DataSourceClient* data_src_client,
-    const DataSourceClientContainerPtr& container, bool has_cache) :
+    const DataSourceClientContainerPtr& container, bool has_cache,
+    const RRClass& rrclass, MemorySegment& mem_sgmt) :
     data_src_client_(data_src_client),
     container_(container)
 {
     if (has_cache) {
-        cache_.reset(new InMemoryClient);
+        cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
     }
 }
 
-ConfigurableClientList::DataSourceInfo::DataSourceInfo(bool has_cache) :
+ConfigurableClientList::DataSourceInfo::DataSourceInfo(
+    const RRClass& rrclass, MemorySegment& mem_sgmt, bool has_cache) :
     data_src_client_(NULL)
 {
     if (has_cache) {
-        cache_.reset(new InMemoryClient);
+        cache_.reset(new InMemoryClient(mem_sgmt, rrclass));
     }
 }
 
+const DataSourceClient*
+ConfigurableClientList::DataSourceInfo::getCacheClient() const {
+    return (cache_.get());
+}
+
+ConfigurableClientList::ConfigurableClientList(const RRClass& rrclass) :
+    rrclass_(rrclass),
+    mem_sgmt_(new util::MemorySegmentLocal),
+    configuration_(new isc::data::ListElement),
+    allow_cache_(false)
+{}
+
+ConfigurableClientList::~ConfigurableClientList() {
+    // Explicitly clear the contained data source clients, and check memory
+    // leak.  assert() (with abort on failure) may be too harsh, but
+    // it's probably better to find more leaks initially.  Once it's stabilized
+    // we should probably revisit it.
+
+    data_sources_.clear();
+    assert(mem_sgmt_->allMemoryDeallocated());
+}
+
 void
 ConfigurableClientList::configure(const ConstElementPtr& config,
                                   bool allow_cache)
@@ -98,14 +126,16 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                     isc_throw(ConfigurationError, "The cache must be enabled "
                               "for the MasterFiles type");
                 }
-                new_data_sources.push_back(DataSourceInfo(true));
+                new_data_sources.push_back(DataSourceInfo(rrclass_, *mem_sgmt_,
+                                                          true));
             } else {
                 // Ask the factory to create the data source for us
                 const DataSourcePair ds(this->getDataSourceClient(type,
                                                                   paramConf));
                 // And put it into the vector
                 new_data_sources.push_back(DataSourceInfo(ds.first, ds.second,
-                                                          want_cache));
+                                                          want_cache, rrclass_,
+                                                          *mem_sgmt_));
             }
 
             if (want_cache) {
@@ -141,13 +171,10 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                 for (vector<string>::const_iterator it(zones_origins.begin());
                      it != zones_origins.end(); ++it) {
                     const Name origin(*it);
-                    shared_ptr<InMemoryZoneFinder>
-                        finder(new
-                            InMemoryZoneFinder(rrclass_, origin));
                     if (type == "MasterFiles") {
                         try {
-                            finder->load(paramConf->get(*it)->stringValue());
-                            cache->addZone(finder);
+                            cache->load(origin,
+                                        paramConf->get(*it)->stringValue());
                         } catch (const isc::dns::MasterLoadError& mle) {
                             LOG_ERROR(logger, DATASRC_MASTERLOAD_ERROR)
                                 .arg(mle.what());
@@ -165,8 +192,7 @@ ConfigurableClientList::configure(const ConstElementPtr& config,
                             isc_throw(isc::Unexpected, "Got NULL iterator "
                                       "for zone " << origin);
                         }
-                        finder->load(*iterator);
-                        cache->addZone(finder);
+                        cache->load(origin, *iterator);
                     }
                 }
             }
@@ -324,14 +350,11 @@ ConfigurableClientList::reload(const Name& name) {
     }
     // Try to convert the finder to in-memory one. If it is the cache,
     // it should work.
-    shared_ptr<InMemoryZoneFinder>
-        finder(dynamic_pointer_cast<InMemoryZoneFinder>(result.finder));
-    const DataSourceInfo* info(result.info);
     // It is of a different type or there's no cache.
-    if (!info->cache_ || !finder) {
+    if (!result.info->cache_) {
         return (ZONE_NOT_CACHED);
     }
-    DataSourceClient* client(info->data_src_client_);
+    DataSourceClient* client(result.info->data_src_client_);
     if (client) {
         // Now do the final reload. If it does not exist in client,
         // DataSourceError is thrown, which is exactly the result what we
@@ -340,15 +363,15 @@ ConfigurableClientList::reload(const Name& name) {
         if (!iterator) {
             isc_throw(isc::Unexpected, "Null iterator from " << name);
         }
-        finder->load(*iterator);
+        result.info->cache_->load(name, *iterator);
     } else {
         // The MasterFiles special case
-        const string filename(finder->getFileName());
+        const string filename(result.info->cache_->getFileName(name));
         if (filename.empty()) {
             isc_throw(isc::Unexpected, "Confused about missing both filename "
                       "and data source");
         }
-        finder->load(filename);
+        result.info->cache_->load(name, filename);
     }
     return (ZONE_RELOADED);
 }
diff --git a/src/lib/datasrc/client_list.h b/src/lib/datasrc/client_list.h
index 0dd522f..61544ef 100644
--- a/src/lib/datasrc/client_list.h
+++ b/src/lib/datasrc/client_list.h
@@ -15,6 +15,8 @@
 #ifndef DATASRC_CONTAINER_H
 #define DATASRC_CONTAINER_H
 
+#include <util/memory_segment.h>
+
 #include <dns/name.h>
 #include <dns/rrclass.h>
 #include <cc/data.h>
@@ -22,6 +24,7 @@
 
 #include <vector>
 #include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
 #include <boost/noncopyable.hpp>
 
 namespace isc {
@@ -34,7 +37,13 @@ typedef boost::shared_ptr<DataSourceClient> DataSourceClientPtr;
 class DataSourceClientContainer;
 typedef boost::shared_ptr<DataSourceClientContainer>
     DataSourceClientContainerPtr;
+
+// XXX: it's better to even hide the existence of the "memory" namespace.
+// We should probably consider pimpl for details of ConfigurableClientList
+// and hide real definitions except for itself and tests.
+namespace memory {
 class InMemoryClient;
+}
 
 /// \brief The list of data source clients.
 ///
@@ -209,11 +218,11 @@ public:
     /// \brief Constructor
     ///
     /// \param rrclass For which class the list should work.
-    ConfigurableClientList(const isc::dns::RRClass &rrclass) :
-        rrclass_(rrclass),
-        configuration_(new isc::data::ListElement),
-        allow_cache_(false)
-    {}
+    ConfigurableClientList(const isc::dns::RRClass& rrclass);
+
+    /// \brief Destructor
+    virtual ~ConfigurableClientList();
+
     /// \brief Exception thrown when there's an error in configuration.
     class ConfigurationError : public Exception {
     public:
@@ -290,24 +299,27 @@ public:
     /// \todo The content yet to be defined.
     struct DataSourceInfo {
         // Plays a role of default constructor too (for vector)
-        DataSourceInfo(bool has_cache = false);
+        DataSourceInfo(const dns::RRClass& rrclass,
+                       util::MemorySegment& mem_sgmt,
+                       bool has_cache = false);
         DataSourceInfo(DataSourceClient* data_src_client,
                        const DataSourceClientContainerPtr& container,
-                       bool has_cache);
+                       bool has_cache, const dns::RRClass& rrclass,
+                       util::MemorySegment& mem_sgmt);
         DataSourceClient* data_src_client_;
         DataSourceClientContainerPtr container_;
-        boost::shared_ptr<InMemoryClient> cache_;
+
+        // Accessor to cache_ in the form of DataSourceClient, hiding
+        // the existence of InMemoryClient as much as possible.  We should
+        // really consider cleaner abstraction, but for now it works.
+        // This is also only intended to be used in auth unit tests right now.
+        // No other applications or tests may use it.
+        const DataSourceClient* getCacheClient() const;
+        boost::shared_ptr<memory::InMemoryClient> cache_;
     };
 
     /// \brief The collection of data sources.
     typedef std::vector<DataSourceInfo> DataSources;
-protected:
-    /// \brief The data sources held here.
-    ///
-    /// All our data sources are stored here. It is protected to let the
-    /// tests in. You should consider it private if you ever want to
-    /// derive this class (which is not really recommended anyway).
-    DataSources data_sources_;
 
     /// \brief Convenience type alias.
     ///
@@ -357,10 +369,26 @@ private:
     void findInternal(MutableResult& result, const dns::Name& name,
                       bool want_exact_match, bool want_finder) const;
     const isc::dns::RRClass rrclass_;
+
+    /// \brief Memory segment for in-memory cache.
+    ///
+    /// Note that this must be placed before data_sources_ so it won't be
+    /// destroyed before the built objects in the destructor.
+    boost::scoped_ptr<util::MemorySegment> mem_sgmt_;
+
     /// \brief Currently active configuration.
     isc::data::ConstElementPtr configuration_;
+
     /// \brief The last set value of allow_cache.
     bool allow_cache_;
+
+protected:
+    /// \brief The data sources held here.
+    ///
+    /// All our data sources are stored here. It is protected to let the
+    /// tests in. You should consider it private if you ever want to
+    /// derive this class (which is not really recommended anyway).
+    DataSources data_sources_;
 };
 
 } // namespace datasrc
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
deleted file mode 100644
index 0e5b3a3..0000000
--- a/src/lib/datasrc/data_source.cc
+++ /dev/null
@@ -1,1434 +0,0 @@
-// Copyright (C) 2010  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 <config.h>
-
-#include <cassert>
-#include <iomanip>
-#include <iostream>
-#include <vector>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/foreach.hpp>
-
-#include <datasrc/cache.h>
-#include <datasrc/data_source.h>
-#include <datasrc/query.h>
-#include <datasrc/logger.h>
-
-#include <util/encode/base32hex.h>
-#include <util/hash/sha1.h>
-#include <util/buffer.h>
-
-#include <dns/message.h>
-#include <dns/name.h>
-#include <dns/rcode.h>
-#include <dns/rdataclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-
-#include <cc/data.h>
-
-#define RETERR(x) do { \
-                      DataSrc::Result r = (x); \
-                      if (r != DataSrc::SUCCESS) \
-                          return (r); \
-                      } while (0)
-
-using namespace std;
-using namespace isc::util;
-using namespace isc::util::encode;
-using namespace isc::util::hash;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace {
-
-struct MatchRRsetForType {
-    MatchRRsetForType(const RRType rrtype) : rrtype_(rrtype) {}
-    bool operator()(RRsetPtr rrset) {
-        return (rrset->getType() == rrtype_);
-    }
-    const RRType rrtype_;
-};
-
-// This is a helper to retrieve a specified RR type of RRset from RRsetList.
-// In our case the data source search logic should ensure that the class is
-// valid.  We use this find logic of our own so that we can support both
-// specific RR class queries (normal case) and class ANY queries.
-RRsetPtr
-findRRsetFromList(RRsetList& list, const RRType rrtype) {
-    RRsetList::iterator it(find_if(list.begin(), list.end(),
-                                   MatchRRsetForType(rrtype)));
-    return (it != list.end() ? *it : RRsetPtr());
-}
-}
-
-namespace isc {
-namespace datasrc {
-
-typedef boost::shared_ptr<const Nsec3Param> ConstNsec3ParamPtr;
-
-class ZoneInfo {
-public:
-    ZoneInfo(DataSrc* ts,
-             const isc::dns::Name& n,
-             const isc::dns::RRClass& c,
-             const isc::dns::RRType& t = isc::dns::RRType::ANY()) :
-        top_source_(ts),
-        dsm_(((t == RRType::DS() && n.getLabelCount() != 1)
-              ? n.split(1, n.getLabelCount() - 1) : n),
-             c)
-    {}
-
-    const Name* getEnclosingZone() {
-        if (dsm_.getEnclosingZone() == NULL) {
-            top_source_->findClosestEnclosure(dsm_);
-        }
-        return (dsm_.getEnclosingZone());
-    }
-
-    const DataSrc* getDataSource() {
-        if (dsm_.getDataSource() == NULL) {
-            top_source_->findClosestEnclosure(dsm_);
-        }
-        return (dsm_.getDataSource());
-    }
-
-private:
-    const DataSrc* top_source_;
-    DataSrcMatch dsm_;
-};
-
-// Add a task to the query task queue to look up additional data
-// (i.e., address records for the names included in NS or MX records)
-void
-getAdditional(Query& q, ConstRRsetPtr rrset) {
-    if (!q.wantAdditional()) {
-        return;
-    }
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    for (; !it->isLast(); it->next()) {
-        const Rdata& rd(it->getCurrent());
-        if (rrset->getType() == RRType::NS()) {
-            const generic::NS& ns = dynamic_cast<const generic::NS&>(rd);
-            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_NS_ADDITIONAL).
-                arg(ns.getNSName()).arg(rrset->getName());
-            q.tasks().push(QueryTaskPtr(
-                               new QueryTask(q, ns.getNSName(),
-                                             Message::SECTION_ADDITIONAL,
-                                             QueryTask::GLUE_QUERY,
-                                             QueryTask::GETADDITIONAL)));
-        } else if (rrset->getType() == RRType::MX()) {
-            const generic::MX& mx = dynamic_cast<const generic::MX&>(rd);
-            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_GET_MX_ADDITIONAL).
-                arg(mx.getMXName()).arg(rrset->getName());
-            q.tasks().push(QueryTaskPtr(
-                               new QueryTask(q, mx.getMXName(),
-                                             Message::SECTION_ADDITIONAL,
-                                             QueryTask::NOGLUE_QUERY,
-                                             QueryTask::GETADDITIONAL)));
-        }
-    }
-}
-
-// Synthesize a CNAME answer, for the benefit of clients that don't
-// understand DNAME
-void
-synthesizeCname(QueryTaskPtr task, RRsetPtr rrset, RRsetList& target) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_SYNTH_CNAME).
-        arg(rrset->getName());
-    RdataIteratorPtr it = rrset->getRdataIterator();
-
-    // More than one DNAME RR in the RRset is illegal, so we only have
-    // to process the first one.
-    if (it->isLast()) {
-        logger.error(DATASRC_QUERY_EMPTY_DNAME).arg(rrset->getName());
-        return;
-    }
-
-    const Rdata& rd(it->getCurrent());
-    const generic::DNAME& dname = dynamic_cast<const generic::DNAME&>(rd);
-    const Name& dname_target(dname.getDname());
-
-    RRsetPtr cname(new RRset(task->qname, rrset->getClass(), RRType::CNAME(),
-                             rrset->getTTL()));
-
-    const int qnlen = task->qname.getLabelCount();
-    const int dnlen = rrset->getName().getLabelCount();
-    assert(qnlen > dnlen);
-    const Name& prefix(task->qname.split(0, qnlen - dnlen));
-    cname->addRdata(generic::CNAME(prefix.concatenate(dname_target)));
-
-    target.addRRset(cname);
-}
-
-// Add a task to the query task queue to look up the data pointed
-// to by a CNAME record
-void
-chaseCname(Query& q, QueryTaskPtr task, RRsetPtr rrset) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_FOLLOW_CNAME).
-        arg(rrset->getName());
-    RdataIteratorPtr it = rrset->getRdataIterator();
-
-    // More than one CNAME RR in the RRset is illegal, so we only have
-    // to process the first one.
-    if (it->isLast()) {
-        logger.error(DATASRC_QUERY_EMPTY_CNAME).arg(rrset->getName());
-        return;
-    }
-
-    // Stop chasing CNAMES after 16 lookups, to prevent loops
-    if (q.tooMany()) {
-        logger.error(DATASRC_QUERY_TOO_MANY_CNAMES).arg(rrset->getName());
-        return;
-    }
-
-    q.tasks().push(QueryTaskPtr(
-                       new QueryTask(q, dynamic_cast<const generic::CNAME&>
-                                     (it->getCurrent()).getCname(),
-                                     task->qtype, Message::SECTION_ANSWER,
-                                     QueryTask::FOLLOWCNAME)));
-}
-
-// Check the cache for data which can answer the current query task.
-bool
-checkCache(QueryTask& task, RRsetList& target) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CHECK_CACHE).
-        arg(task.qname).arg(task.qtype);
-    HotCache& cache = task.q.getCache();
-    RRsetList rrsets;
-    RRsetPtr rrset;
-    int count = 0;
-    uint32_t flags = 0, cflags = 0;
-    bool hit = false, found = false;
-
-    switch (task.op) {
-    case QueryTask::SIMPLE_QUERY:       // Find exact RRset
-        // ANY queries must be handled by the low-level data source,
-        // or the results won't be guaranteed to be complete
-        if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
-            LOG_DEBUG(logger, DBG_TRACE_DATA,
-                      DATASRC_QUERY_NO_CACHE_ANY_SIMPLE).arg(task.qname).
-                arg(task.qtype).arg(task.qclass);
-            break;
-        }
-
-        hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
-        if (hit) {
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                target.append(rrsets);
-            }
-
-            // Reset the referral flag and treat CNAME as "not found".
-            // This emulates the behavior of the sqlite3 data source.
-            // XXX: this is not ideal in that the responsibility for handling
-            // operation specific cases is spread over various classes at
-            // different abstraction levels.  For longer terms we should
-            // revisit the whole datasource/query design, and clarify this
-            // point better.
-            flags &= ~DataSrc::REFERRAL;
-            if ((flags & DataSrc::CNAME_FOUND) != 0) {
-                flags &= ~DataSrc::CNAME_FOUND;
-                flags |= DataSrc::TYPE_NOT_FOUND;
-            }
-            task.flags = flags;
-            return (true);
-        }
-        break;
-
-    case QueryTask::AUTH_QUERY:         // Find exact RRset or CNAME
-        if (task.qtype == RRType::ANY() || task.qclass == RRClass::ANY()) {
-            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_NO_CACHE_ANY_AUTH).
-                arg(task.qname).arg(task.qtype).arg(task.qclass);
-            break;
-        }
-
-        hit = cache.retrieve(task.qname, task.qclass, task.qtype, rrset, flags);
-        if (!hit || !rrset || (flags & DataSrc::CNAME_FOUND) != 0) {
-            hit = cache.retrieve(task.qname, task.qclass, RRType::CNAME(),
-                                 rrset, flags);
-            if (!rrset) {
-                // If we don't have a positive cache, forget it; otherwise the
-                // intermediate result may confuse the subsequent processing.
-                hit = false;
-            }
-        }
-
-        if (hit) {
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                target.append(rrsets);
-            }
-            task.flags = flags;
-            return (true);
-        }
-        break;
-
-    case QueryTask::GLUE_QUERY:         // Find addresses
-    case QueryTask::NOGLUE_QUERY:
-        // (XXX: need to figure out how to deal with noglue case)
-        flags = 0;
-
-        hit = cache.retrieve(task.qname, task.qclass, RRType::A(),
-                             rrset, cflags);
-        if (hit) {
-            flags |= cflags;
-            ++count;
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                found = true;
-            }
-        }
-
-        hit = cache.retrieve(task.qname, task.qclass, RRType::AAAA(),
-                             rrset, flags);
-        if (hit) {
-            flags |= cflags;
-            ++count;
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                found = true;
-            }
-        }
-
-        if (count == 2) {
-            if (found) {
-                flags &= ~DataSrc::TYPE_NOT_FOUND;
-                target.append(rrsets);
-            }
-            task.flags = flags;
-            return (true);
-        } 
-        break;
-
-
-    case QueryTask::REF_QUERY:          // Find NS, DS and/or DNAME
-        flags = count = 0;
-
-        hit = cache.retrieve(task.qname, task.qclass, RRType::NS(),
-                             rrset, cflags);
-        if (hit) {
-            flags |= cflags;
-            ++count;
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                found = true;
-            }
-        }
-
-        hit = cache.retrieve(task.qname, task.qclass, RRType::DS(),
-                             rrset, flags);
-        if (hit) {
-            flags |= cflags;
-            ++count;
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                found = true;
-            }
-        }
-
-        hit = cache.retrieve(task.qname, task.qclass, RRType::DNAME(),
-                             rrset, flags);
-        if (hit) {
-            flags |= cflags;
-            ++count;
-            if (rrset) {
-                rrsets.addRRset(rrset);
-                found = true;
-            }
-        }
-
-        if (count == 3) {
-            if (found) {
-                flags &= ~DataSrc::TYPE_NOT_FOUND;
-                flags &= DataSrc::REFERRAL;
-                target.append(rrsets);
-            }
-            task.flags = flags;
-            return (true);
-        } 
-        break;
-    }
-
-    return (false);
-}
-
-// Carry out the query specified in a QueryTask object
-DataSrc::Result
-doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
-    HotCache& cache = task.q.getCache();
-    RRsetPtr rrset;
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_DO_QUERY).arg(task.qname).
-        arg(task.qtype);
-
-    // First off, make sure at least we have a matching zone in some data
-    // source.  We must do this before checking the cache, because it can
-    // happen that the matching zone has been removed after an RRset of that
-    // zone is cached.  Such inconsistency will cause various problems,
-    // including a crash.
-    const DataSrc* ds = zoneinfo.getDataSource();
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    if (ds == NULL) {
-        task.flags |= DataSrc::NO_SUCH_ZONE;
-        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_NO_ZONE).
-            arg(task.qname).arg(task.qclass);
-        return (DataSrc::SUCCESS);
-    }
-
-    // Then check the cache for matching data
-    if (checkCache(task, target)) {
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_CACHED).
-            arg(task.qname).arg(task.qtype);
-        return (DataSrc::SUCCESS);
-    }
-
-    // Requested data weren't in the cache (or were, but had expired),
-    // so now we proceed with the low-level data source lookup, and cache
-    // whatever we find.
-
-    DataSrc::Result result;
-    switch (task.op) {
-    case QueryTask::SIMPLE_QUERY:
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_SIMPLE).
-            arg(task.qname).arg(task.qtype);
-        result = ds->findExactRRset(task.qname, task.qclass, task.qtype,
-                                    target, task.flags, zonename);
-
-        if (result != DataSrc::SUCCESS) {
-            logger.error(DATASRC_QUERY_SIMPLE_FAIL).arg(result);
-            return (result);
-        }
-
-        if (task.qclass == RRClass::ANY()) {
-            // XXX: Currently, RRsetList::findRRset() doesn't handle
-            // ANY queries, and without that we can't cache the results,
-            // so we just return in that case.
-            return (result);
-        }
-
-        if (task.flags == 0) {
-            rrset = target.findRRset(task.qtype, task.qclass);
-            assert(rrset);
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
-        }
-
-        return (result);
-
-    case QueryTask::AUTH_QUERY:
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_AUTH).
-            arg(task.qname).arg(task.qtype);
-        result = ds->findRRset(task.qname, task.qclass, task.qtype,
-                               target, task.flags, zonename);
-
-        if (result != DataSrc::SUCCESS) {
-            logger.error(DATASRC_QUERY_AUTH_FAIL).arg(result);
-            return (result);
-        }
-
-        if (task.qclass == RRClass::ANY()) {
-            return (result);
-        }
-
-        if (task.qtype == RRType::ANY()) {
-            BOOST_FOREACH(RRsetPtr rr, target) {
-                cache.addPositive(rr, task.flags);
-            }
-        } else if ((task.flags & DataSrc::CNAME_FOUND) != 0) {
-            cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
-            rrset = target.findRRset(RRType::CNAME(), task.qclass);
-            assert(rrset);
-            cache.addPositive(rrset, task.flags);
-        } else if ((task.flags & DataSrc::DATA_NOT_FOUND) == 0) {
-            if (task.qtype != RRType::CNAME()) {
-                cache.addNegative(task.qname, task.qclass, RRType::CNAME(),
-                                  task.flags);
-            }
-            rrset = target.findRRset(task.qtype, task.qclass);
-            assert(rrset);
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, task.qtype, task.flags);
-        }
-
-        return (result);
-
-    case QueryTask::GLUE_QUERY:
-    case QueryTask::NOGLUE_QUERY:
-        LOG_DEBUG(logger, DBG_TRACE_DATA, task.op == QueryTask::GLUE_QUERY ?
-                     DATASRC_QUERY_IS_GLUE : DATASRC_QUERY_IS_NOGLUE).
-            arg(task.qname).arg(task.qtype);
-        result = ds->findAddrs(task.qname, task.qclass, target,
-                               task.flags, zonename);
-
-        if (result != DataSrc::SUCCESS) {
-            logger.error(task.op == QueryTask::GLUE_QUERY ?
-                         DATASRC_QUERY_GLUE_FAIL : DATASRC_QUERY_NOGLUE_FAIL).
-                arg(result);
-            return (result);
-        }
-
-        if (task.qclass == RRClass::ANY()) {
-            return (result);
-        }
-
-        rrset = target.findRRset(RRType::A(), task.qclass);
-        if (rrset) {
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, RRType::A(), task.flags);
-        }
-
-        rrset = target.findRRset(RRType::AAAA(), task.qclass);
-        if (rrset) {
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, RRType::AAAA(),
-                              task.flags);
-        }
-
-        return (result);
-
-    case QueryTask::REF_QUERY:
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_IS_REF).
-            arg(task.qname).arg(task.qtype);
-        result = ds->findReferral(task.qname, task.qclass, target,
-                                 task.flags, zonename);
-
-        if (result != DataSrc::SUCCESS) {
-            logger.error(DATASRC_QUERY_REF_FAIL).arg(result);
-            return (result);
-        }
-
-        if (task.qclass == RRClass::ANY()) {
-            return (result);
-        }
-
-        rrset = target.findRRset(RRType::NS(), task.qclass);
-        if (rrset) {
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, RRType::NS(),
-                              task.flags);
-        }
-        rrset = target.findRRset(RRType::DS(), task.qclass);
-        if (rrset) {
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, RRType::DS(),
-                              task.flags);
-        }
-        rrset = target.findRRset(RRType::DNAME(), task.qclass);
-        if (rrset) {
-            cache.addPositive(rrset, task.flags);
-        } else {
-            cache.addNegative(task.qname, task.qclass, RRType::DNAME(),
-                              task.flags);
-        }
-
-        return (result);
-    }
-
-    // Not reached
-    logger.error(DATASRC_QUERY_INVALID_OP);
-    return (DataSrc::ERROR);
-}
-
-
-// Add an RRset (and its associated RRSIG) to a message section,
-// checking first to ensure that there isn't already an RRset with
-// the same name and type.
-inline void
-addToMessage(Query& q, const Message::Section sect, RRsetPtr rrset,
-             bool no_dnssec = false)
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_RRSET).
-        arg(rrset->getName()).arg(rrset->getType());
-    Message& m = q.message();
-    if (no_dnssec) {
-        if (rrset->getType() == RRType::RRSIG() ||
-            !m.hasRRset(sect, rrset->getName(), rrset->getClass(),
-                        rrset->getType())) {
-            m.addRRset(sect, rrset);
-        }
-    } else {
-        if (!m.hasRRset(sect, rrset->getName(), rrset->getClass(),
-                        rrset->getType())) {
-            m.addRRset(sect, rrset);
-        }
-    }
-}
-
-// Copy referral information into the authority section of a message
-inline void
-copyAuth(Query& q, RRsetList& auth) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_COPY_AUTH);
-    BOOST_FOREACH(RRsetPtr rrset, auth) {
-        if (rrset->getType() == RRType::DNAME()) {
-            continue;
-        }
-        if (rrset->getType() == RRType::DS() && !q.wantDnssec()) {
-            continue;
-        }
-        addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-        getAdditional(q, rrset);
-    }
-}
-
-// Query for referrals (i.e., NS/DS or DNAME) at a given name
-inline bool
-refQuery(const Query& q, const Name& name, ZoneInfo& zoneinfo,
-         RRsetList& target)
-{
-    QueryTask newtask(q, name, QueryTask::REF_QUERY);
-
-    if (doQueryTask(newtask, zoneinfo, target) != DataSrc::SUCCESS) {
-        // Lookup failed
-        return (false);
-    }
-
-    // Referral bit is expected, so clear it when checking flags
-    if ((newtask.flags & ~DataSrc::REFERRAL) != 0) {
-        return (false);
-    }
-
-    return (true);
-}
-
-// Match downward, from the zone apex to the query name, looking for
-// referrals.  Note that we exclude the apex name and query name themselves;
-// they'll be handled in a normal lookup in the zone.
-inline bool
-hasDelegation(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_DELEGATION).
-        arg(task->qname);
-
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    if (zonename == NULL) {
-        if (task->state == QueryTask::GETANSWER) {
-            q.message().setRcode(Rcode::REFUSED());
-        }
-        return (false);
-    }
-
-    const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
-    if (diff > 1) {
-        bool found = false;
-        RRsetList ref;
-        for (int i = diff - 1; i > 0; --i) {
-            const Name sub(task->qname.split(i));
-            if (refQuery(q, sub, zoneinfo, ref)) {
-                found = true;
-                break;
-            }
-        }
-
-        // Found a referral while getting additional data
-        // for something other than NS; we skip it.
-        if (found && task->op == QueryTask::NOGLUE_QUERY) {
-            return (true);
-        }
-
-        // Found a referral while getting answer data;
-        // send a delegation.
-        if (found) {
-            RRsetPtr r = findRRsetFromList(ref, RRType::DNAME());
-            if (r != NULL) {
-                RRsetList syn;
-                addToMessage(q, Message::SECTION_ANSWER, r);
-                q.message().setHeaderFlag(Message::HEADERFLAG_AA);
-                synthesizeCname(task, r, syn);
-                if (syn.size() == 1) {
-                    RRsetPtr cname_rrset = findRRsetFromList(syn,
-                                                             RRType::CNAME());
-                    addToMessage(q, Message::SECTION_ANSWER, cname_rrset);
-                    chaseCname(q, task, cname_rrset);
-                    return (true);
-                }
-            }
-
-            copyAuth(q, ref);
-            return (true);
-        }
-    }
-
-    // We appear to have authoritative data; set the header
-    // flag.  (We may clear it later if we find a referral
-    // at the actual qname node.)
-    if (task->op == QueryTask::AUTH_QUERY &&
-        task->state == QueryTask::GETANSWER) {
-        q.message().setHeaderFlag(Message::HEADERFLAG_AA);
-    }
-
-    return (false);
-}
-
-inline DataSrc::Result
-addSOA(Query& q, ZoneInfo& zoneinfo) {
-    RRsetList soa;
-
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_SOA).arg(*zonename);
-    QueryTask newtask(q, *zonename, RRType::SOA(), QueryTask::SIMPLE_QUERY);
-    RETERR(doQueryTask(newtask, zoneinfo, soa));
-    if (newtask.flags != 0) {
-        return (DataSrc::ERROR);
-    }
-
-    addToMessage(q, Message::SECTION_AUTHORITY,
-                 findRRsetFromList(soa, RRType::SOA()));
-    return (DataSrc::SUCCESS);
-}
-
-inline DataSrc::Result
-addNSEC(Query& q, const Name& name, ZoneInfo& zoneinfo) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC).arg(name);
-    RRsetList nsec;
-
-    QueryTask newtask(q, name, RRType::NSEC(), QueryTask::SIMPLE_QUERY); 
-    RETERR(doQueryTask(newtask, zoneinfo, nsec));
-    if (newtask.flags == 0) {
-        addToMessage(q, Message::SECTION_AUTHORITY,
-                     findRRsetFromList(nsec, RRType::NSEC()));
-    }
-
-    return (DataSrc::SUCCESS);
-}
-
-inline DataSrc::Result
-getNsec3(Query& q, ZoneInfo& zoneinfo, string& hash, RRsetPtr& target) {
-    const DataSrc* ds = zoneinfo.getDataSource();
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_ADD_NSEC3).arg(*zonename);
-
-    if (ds == NULL) {
-        q.message().setRcode(Rcode::SERVFAIL());
-        logger.error(DATASRC_QUERY_NO_DS_NSEC3).arg(*zonename);
-        return (DataSrc::ERROR);
-    }
-
-    RRsetList rl;
-    RETERR(ds->findCoveringNSEC3(*zonename, hash, rl));
-    target = rl.findRRset(RRType::NSEC3(), q.qclass());
-
-    return (DataSrc::SUCCESS);
-}
-
-ConstNsec3ParamPtr
-getNsec3Param(Query& q, ZoneInfo& zoneinfo) {
-    DataSrc::Result result;
-    RRsetList nsec3param;
-
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    QueryTask newtask(q, *zonename, RRType::NSEC3PARAM(),
-                      QueryTask::SIMPLE_QUERY); 
-    result = doQueryTask(newtask, zoneinfo, nsec3param);
-    newtask.flags &= ~DataSrc::REFERRAL;
-    if (result != DataSrc::SUCCESS || newtask.flags != 0) {
-        return (ConstNsec3ParamPtr());
-    }
-
-    RRsetPtr rrset = nsec3param.findRRset(RRType::NSEC3PARAM(), q.qclass());
-    if (!rrset) {
-        return (ConstNsec3ParamPtr());
-    }
-
-    // XXX: currently only one NSEC3 chain per zone is supported;
-    // we will need to revisit this.
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    if (it->isLast()) {
-        return (ConstNsec3ParamPtr());
-    }
-
-    const generic::NSEC3PARAM& np =
-            dynamic_cast<const generic::NSEC3PARAM&>(it->getCurrent());
-    return (ConstNsec3ParamPtr(new Nsec3Param(np.getHashalg(), np.getFlags(),
-                                              np.getIterations(),
-                                              np.getSalt())));
-}
-
-inline DataSrc::Result
-proveNX(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, const bool wildcard) {
-    Message& m = q.message();
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    ConstNsec3ParamPtr nsec3 = getNsec3Param(q, zoneinfo);
-
-    if (nsec3 != NULL) {
-        // Attach the NSEC3 record covering the QNAME
-        RRsetPtr rrset;
-        string hash1(nsec3->getHash(task->qname));
-        RETERR(getNsec3(q, zoneinfo, hash1, rrset));
-        addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-
-        // If this is an NXRRSET or NOERROR/NODATA, we're done
-        if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0) {
-            return (DataSrc::SUCCESS);
-        }
-
-        // Find the closest provable enclosing name for QNAME
-        Name enclosure(*zonename);
-        const int diff = task->qname.getLabelCount() -
-            enclosure.getLabelCount();
-        string hash2;
-        for (int i = 1; i <= diff; ++i) {
-            enclosure = task->qname.split(i);
-            string nodehash(nsec3->getHash(enclosure));
-            if (nodehash == hash1) {
-                break;
-            }
-            hash2 = nodehash;
-            RRsetList rl;
-
-            // hash2 will be overwritten with the actual hash found;
-            // we don't want to use one until we find an exact match
-            RETERR(getNsec3(q, zoneinfo, hash2, rrset));
-            if (hash2 == nodehash) {
-                addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-                break;
-            }
-        }
-
-        // If we are processing a wildcard answer, we're done.
-        if (wildcard) {
-            return (DataSrc::SUCCESS);
-        }
-
-        // Otherwise, there is no wildcard record, so we must add a
-        // covering NSEC3 to prove that it doesn't exist.
-        string hash3(nsec3->getHash(Name("*").concatenate(enclosure)));
-        RETERR(getNsec3(q, zoneinfo, hash3, rrset));
-        if (hash3 != hash1 && hash3 != hash2) {
-            addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-        }
-    } else {
-        Name nsecname(task->qname);
-        if ((task->flags & DataSrc::NAME_NOT_FOUND) != 0 || wildcard) {
-            const DataSrc* ds = zoneinfo.getDataSource();
-            if (ds == NULL) {
-                m.setRcode(Rcode::SERVFAIL());
-                logger.error(DATASRC_QUERY_NO_DS_NSEC).arg(*zonename);
-                return (DataSrc::ERROR);
-            }
-            ds->findPreviousName(task->qname, nsecname, zonename);
-        }
-
-        RETERR(addNSEC(q, nsecname, zoneinfo));
-        if ((task->flags & DataSrc::TYPE_NOT_FOUND) != 0 ||
-            nsecname == *zonename)
-        {
-            return (DataSrc::SUCCESS);
-        }
-
-        // If we are processing a wildcard answer, we're done.
-        if (wildcard) {
-            return (DataSrc::SUCCESS);
-        }
-
-        // Otherwise, there is no wildcard record, so we must add an
-        // NSEC for the zone to prove the wildcard doesn't exist.
-        RETERR(addNSEC(q, *zonename, zoneinfo));
-    }
-
-    return (DataSrc::SUCCESS);
-}
-
-// Attempt a wildcard lookup
-inline DataSrc::Result
-tryWildcard(Query& q, QueryTaskPtr task, ZoneInfo& zoneinfo, bool& found) {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_QUERY_WILDCARD).arg(task->qname);
-    Message& m = q.message();
-    DataSrc::Result result;
-    found = false;
-
-    if ((task->flags & DataSrc::NAME_NOT_FOUND) == 0 || 
-        (task->state != QueryTask::GETANSWER &&
-         task->state != QueryTask::FOLLOWCNAME)) {
-        return (DataSrc::SUCCESS);
-    }
-
-    const Name* const zonename = zoneinfo.getEnclosingZone();
-    const int diff = task->qname.getLabelCount() - zonename->getLabelCount();
-    if (diff < 1) {
-        return (DataSrc::SUCCESS);
-    }
-
-    RRsetList wild;
-    const Name star("*");
-    bool cname = false;
-
-    for (int i = 1; i <= diff; ++i) {
-        const Name& wname(star.concatenate(task->qname.split(i)));
-        QueryTask newtask(q, wname, task->qtype, Message::SECTION_ANSWER,
-                          QueryTask::AUTH_QUERY); 
-        result = doQueryTask(newtask, zoneinfo, wild);
-        if (result == DataSrc::SUCCESS) {
-            if (newtask.flags == 0) {
-                task->flags &= ~DataSrc::NAME_NOT_FOUND;
-                task->flags &= ~DataSrc::TYPE_NOT_FOUND;
-                found = true;
-                break;
-            } else if ((newtask.flags & DataSrc::CNAME_FOUND) != 0) {
-                task->flags &= ~DataSrc::NAME_NOT_FOUND;
-                task->flags &= ~DataSrc::TYPE_NOT_FOUND;
-                task->flags |= DataSrc::CNAME_FOUND;
-                found = true;
-                cname = true;
-                break;
-            } else if ((newtask.flags & DataSrc::TYPE_NOT_FOUND) != 0) {
-                task->flags &= ~DataSrc::NAME_NOT_FOUND;
-                task->flags |= DataSrc::TYPE_NOT_FOUND;
-                break;
-            }
-        }
-    }
-
-    // A wildcard was found.
-    if (found) {
-        // Prove the nonexistence of the name we were looking for
-        if (q.wantDnssec()) {
-            result = proveNX(q, task, zoneinfo, true);
-            if (result != DataSrc::SUCCESS) {
-                m.setRcode(Rcode::SERVFAIL());
-                logger.error(DATASRC_QUERY_WILDCARD_PROVE_NX_FAIL).
-                    arg(task->qname).arg(result);
-                return (DataSrc::ERROR);
-            }
-        }
-
-        // Add the data to the answer section (but with the name changed to
-        // match the qname), and then continue as if this were a normal
-        // answer: if a CNAME, chase the target, otherwise add authority.
-        if (cname) {
-            RRsetPtr rrset = findRRsetFromList(wild, RRType::CNAME());
-            if (rrset != NULL) {
-                rrset->setName(task->qname);
-                addToMessage(q, Message::SECTION_ANSWER, rrset);
-                chaseCname(q, task, rrset);
-            }
-        } else {
-            BOOST_FOREACH (RRsetPtr rrset, wild) {
-                rrset->setName(task->qname);
-                addToMessage(q, Message::SECTION_ANSWER, rrset);
-            }
-
-            RRsetList auth;
-            if (!refQuery(q, *zonename, zoneinfo, auth)) {
-                logger.error(DATASRC_QUERY_WILDCARD_REFERRAL).arg(task->qname).
-                    arg(result);
-                return (DataSrc::ERROR);
-            }
-
-            copyAuth(q, auth);
-        }
-    }
-
-    return (DataSrc::SUCCESS);
-}
-
-//
-// doQuery: Processes a query.
-// 
-void
-DataSrc::doQuery(Query& q) {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_PROCESS).arg(q.qname()).
-        arg(q.qtype()).arg(q.qclass());
-    Message& m = q.message();
-    vector<RRsetPtr> additional;
-
-    // Record the fact that the query is being processed by the
-    // current data source.
-    q.setDatasrc(this);
-
-    // Process the query task queue.  (The queue is initialized
-    // and the first task placed on it by the Query constructor.)
-    m.setHeaderFlag(Message::HEADERFLAG_AA, false);
-    while (!q.tasks().empty()) {
-        QueryTaskPtr task = q.tasks().front();
-        q.tasks().pop();
-
-        // Can't query directly for RRSIG.
-        if (task->qtype == RRType::RRSIG()) {
-            m.setRcode(Rcode::REFUSED());
-            logger.warn(DATASRC_QUERY_RRSIG).arg(task->qname);
-            return;
-        }
-
-        // These task types should never be on the task queue.
-        if (task->op == QueryTask::SIMPLE_QUERY ||
-            task->op == QueryTask::REF_QUERY) {
-            m.setRcode(Rcode::SERVFAIL());
-            logger.error(DATASRC_QUERY_MISPLACED_TASK);
-            return;
-        }
-
-        ZoneInfo zoneinfo(this, task->qname, task->qclass, task->qtype);
-        RRsetList data;
-        Result result = SUCCESS;
-
-        // For these query task types, if there is more than
-        // one level between the zone name and qname, we need to
-        // check the intermediate nodes for referrals.
-        if ((task->op == QueryTask::AUTH_QUERY ||
-             task->op == QueryTask::NOGLUE_QUERY) &&
-            hasDelegation(q, task, zoneinfo)) {
-            continue;
-        }
-
-        result = doQueryTask(*task, zoneinfo, data);
-        if (result != SUCCESS) {
-            m.setRcode(Rcode::SERVFAIL());
-            logger.error(DATASRC_QUERY_TASK_FAIL).arg(result);
-            return;
-        }
-
-        // No such zone.  If we're chasing cnames or adding additional
-        // data, that's okay, but if doing an original query, return
-        // REFUSED.
-        if (task->flags == NO_SUCH_ZONE) {
-            if (task->state == QueryTask::GETANSWER) {
-                m.setRcode(Rcode::REFUSED());
-                // No need to log it here, it was already logged in doQueryTask
-                return;
-            }
-            continue;
-        }
-
-        // Query found a referral; let's find out if that was expected--
-        // i.e., if an NS was at the zone apex, or if we were querying
-        // specifically for, and found, a DS, NSEC, or DNAME record.
-        const Name* const zonename = zoneinfo.getEnclosingZone();
-        if ((task->flags & REFERRAL) != 0 &&
-            (zonename->getLabelCount() == task->qname.getLabelCount() ||
-             ((task->qtype == RRType::NSEC() ||
-               task->qtype == RRType::DS() ||
-               task->qtype == RRType::DNAME()) &&
-              findRRsetFromList(data, task->qtype)))) {
-            task->flags &= ~REFERRAL;
-        }
-
-        if (result == SUCCESS && task->flags == 0) {
-            bool have_ns = false, need_auth = false;
-            switch (task->state) {
-            case QueryTask::GETANSWER:
-            case QueryTask::FOLLOWCNAME:
-                BOOST_FOREACH(RRsetPtr rrset, data) {
-                    addToMessage(q, task->section, rrset);
-                    if (q.tasks().empty()) {
-                        need_auth = true;
-                    }
-                    getAdditional(q, rrset);
-                    if (rrset->getType() == RRType::NS()) {
-                        have_ns = true;
-                    }
-                }
-                q.setStatus(Query::ANSWERED);
-                if (need_auth && !have_ns) {
-                    // Data found, no additional processing needed.
-                    // Add the NS records for the enclosing zone to
-                    // the authority section.
-                    RRsetList auth;
-                    if (!refQuery(q, Name(*zonename), zoneinfo, auth) ||
-                        !findRRsetFromList(auth, RRType::NS())) {
-                        logger.error(DATASRC_QUERY_MISSING_NS).arg(*zonename);
-                        isc_throw(DataSourceError,
-                                  "NS RR not found in " << *zonename << "/" <<
-                                  q.qclass());
-                    }
-
-                    copyAuth(q, auth);
-                }
-                continue;
-
-            case QueryTask::GETADDITIONAL:
-                // Got additional data.  Do not add it to the message
-                // yet; instead store it and copy it in at the end
-                // (this allow RRSIGs to be omitted if necessary).
-                BOOST_FOREACH(RRsetPtr rrset, data) {
-                    if (q.status() == Query::ANSWERED &&
-                        rrset->getName() == q.qname() &&
-                        rrset->getType() == q.qtype()) {
-                        continue;
-                    }
-                    additional.push_back(rrset);
-                }
-                continue;
-
-            default:
-                logger.error(DATASRC_UNEXPECTED_QUERY_STATE);
-                isc_throw (Unexpected, "unexpected query state");
-            }
-        } else if (result == ERROR || result == NOT_IMPLEMENTED) {
-            m.setRcode(Rcode::SERVFAIL());
-            logger.error(DATASRC_QUERY_FAIL);
-            return;
-        } else if ((task->flags & CNAME_FOUND) != 0) {
-            // The qname node contains a CNAME.  Add a new task to the
-            // queue to look up its target.
-            RRsetPtr rrset = findRRsetFromList(data, RRType::CNAME());
-            if (rrset != NULL) {
-                addToMessage(q, task->section, rrset);
-                chaseCname(q, task, rrset);
-            }
-            continue;
-        } else if ((task->flags & REFERRAL) != 0) {
-            // The qname node contains an out-of-zone referral.
-            if (task->state == QueryTask::GETANSWER) {
-                RRsetList auth;
-                m.setHeaderFlag(Message::HEADERFLAG_AA, false);
-                if (!refQuery(q, task->qname, zoneinfo, auth)) {
-                    m.setRcode(Rcode::SERVFAIL());
-                    logger.error(DATASRC_QUERY_BAD_REFERRAL).arg(task->qname);
-                    return;
-                }
-                BOOST_FOREACH (RRsetPtr rrset, auth) {
-                    if (rrset->getType() == RRType::NS()) {
-                        addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-                    } else if (rrset->getType() == task->qtype) {
-                        addToMessage(q, Message::SECTION_ANSWER, rrset);
-                    } else if (rrset->getType() == RRType::DS() &&
-                               q.wantDnssec()) {
-                        addToMessage(q, Message::SECTION_AUTHORITY, rrset);
-                    }
-                    getAdditional(q, rrset);
-                }
-            } 
-            continue;
-        } else if ((task->flags & (NAME_NOT_FOUND|TYPE_NOT_FOUND)) != 0) {
-            // No data found at this qname/qtype.
-
-            // If we were looking for additional data, we should simply
-            // ignore this result.
-            if (task->state == QueryTask::GETADDITIONAL) {
-                continue;
-            }
-
-            // If we were looking for answer data, not additional,
-            // and the name was not found, we need to find out whether
-            // there are any relevant wildcards.
-            bool wildcard_found = false;
-            result = tryWildcard(q, task, zoneinfo, wildcard_found);
-            if (result != SUCCESS) {
-                m.setRcode(Rcode::SERVFAIL());
-                logger.error(DATASRC_QUERY_WILDCARD_FAIL).arg(task->qname);
-                return;
-            }
-
-            if (wildcard_found) {
-                continue;
-            }
-
-            // If we've reached this point, there is definitely no answer.
-            // If we were chasing cnames or adding additional data, that's
-            // okay, but if we were doing an original query, reply with the
-            // SOA in the authority section.  For NAME_NOT_FOUND, set
-            // NXDOMAIN, and also add the previous NSEC to the authority
-            // section.  For TYPE_NOT_FOUND, do not set an error rcode,
-            // and send the current NSEC in the authority section.
-            if (task->state == QueryTask::GETANSWER) {
-                if ((task->flags & NAME_NOT_FOUND) != 0) {
-                    m.setRcode(Rcode::NXDOMAIN());
-                }
-
-                result = addSOA(q, zoneinfo);
-                if (result != SUCCESS) {
-                    logger.error(DATASRC_QUERY_MISSING_SOA).arg(*zonename);
-                    isc_throw(DataSourceError,
-                              "SOA RR not found in " << *zonename <<
-                              "/" << q.qclass());
-                }
-            }
-
-            Name nsecname(task->qname);
-            if ((task->flags & NAME_NOT_FOUND) != 0) {
-                const DataSrc* ds = zoneinfo.getDataSource();
-                ds->findPreviousName(task->qname, nsecname, zonename);
-            }
-
-            if (q.wantDnssec()) {
-                result = proveNX(q, task, zoneinfo, false);
-                if (result != DataSrc::SUCCESS) {
-                    m.setRcode(Rcode::SERVFAIL());
-                    logger.error(DATASRC_QUERY_PROVE_NX_FAIL).arg(task->qname);
-                    return;
-                }
-            }
-
-            return;
-        } else {
-            // Should never be reached!
-            m.setRcode(Rcode::SERVFAIL());
-            logger.error(DATASRC_QUERY_UNKNOWN_RESULT);
-            return;
-        }
-    }
-
-    // We're done, so now copy in the additional data:
-    // data first, then signatures.  (If we run out of
-    // space, signatures in additional section are
-    // optional.)
-    BOOST_FOREACH(RRsetPtr rrset, additional) {
-        addToMessage(q, Message::SECTION_ADDITIONAL, rrset, true);
-    }
-
-    if (q.wantDnssec()) {
-        BOOST_FOREACH(RRsetPtr rrset, additional) {
-            if (rrset->getRRsig()) {
-                addToMessage(q, Message::SECTION_ADDITIONAL, rrset->getRRsig(),
-                             true);
-            }
-        }
-    }
-}
-
-DataSrc::Result
-DataSrc::findAddrs(const Name& qname, const RRClass& qclass,
-                   RRsetList& target, uint32_t& flags,
-                   const Name* zonename) const
-{
-    Result r;
-    bool a = false, aaaa = false;
-
-    flags = 0;
-    r = findExactRRset(qname, qclass, RRType::A(), target, flags, zonename);
-    if (r == SUCCESS && flags == 0) {
-        a = true;
-    }
-
-    flags = 0;
-    r = findExactRRset(qname, qclass, RRType::AAAA(), target, flags,
-                       zonename);
-    if (r == SUCCESS && flags == 0) {
-        aaaa = true;
-    }
-
-    if (!a && !aaaa) {
-        flags = TYPE_NOT_FOUND;
-    } else {
-        flags = 0;
-    }
-
-    return (SUCCESS);
-}
-
-DataSrc::Result
-DataSrc::findReferral(const Name& qname, const RRClass& qclass,
-                      RRsetList& target, uint32_t& flags,
-                      const Name* zonename) const
-{
-    Result r;
-    bool ns = false, ds = false, dname = false;
-
-    flags = 0;
-    r = findExactRRset(qname, qclass, RRType::NS(), target, flags, zonename);
-    if (r == SUCCESS && flags == 0) {
-        ns = true;
-    } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
-        return (SUCCESS);
-    }
-
-    flags = 0;
-    r = findExactRRset(qname, qclass, RRType::DS(), target, flags, zonename);
-    if (r == SUCCESS && flags == 0) {
-        ds = true;
-    } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
-        return (SUCCESS);
-    }
-
-    flags = 0;
-    r = findExactRRset(qname, qclass, RRType::DNAME(), target, flags, zonename);
-    if (r == SUCCESS && flags == 0) {
-        dname = true;
-    } else if ((flags & (NO_SUCH_ZONE|NAME_NOT_FOUND))) {
-        return (SUCCESS);
-    }
-
-    if (!ns && !dname && !ds) {
-        flags = TYPE_NOT_FOUND;
-    } else {
-        flags = 0;
-    }
-
-    return (SUCCESS);
-}
-
-void
-MetaDataSrc::addDataSrc(ConstDataSrcPtr data_src) {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_ADD);
-    if (getClass() != RRClass::ANY() && data_src->getClass() != getClass()) {
-        logger.error(DATASRC_META_ADD_CLASS_MISMATCH).
-            arg(data_src->getClass()).arg(getClass());
-        isc_throw(Unexpected, "class mismatch");
-    }
-
-    data_sources.push_back(data_src);
-}
-
-void
-MetaDataSrc::removeDataSrc(ConstDataSrcPtr data_src) {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_META_REMOVE);
-    std::vector<ConstDataSrcPtr>::iterator it, itr;
-    for (it = data_sources.begin(); it != data_sources.end(); ++it) {
-        if (*it == data_src) {
-            itr = it;
-        }
-    }
-
-    data_sources.erase(itr);
-}
-
-void
-MetaDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
-    if (getClass() != match.getClass() &&
-        getClass() != RRClass::ANY() && match.getClass() != RRClass::ANY()) {
-        return;
-    }
-
-    BOOST_FOREACH (ConstDataSrcPtr data_src, data_sources) {
-        data_src->findClosestEnclosure(match);
-    }
-}
-
-DataSrcMatch::~DataSrcMatch() {
-    delete closest_name_;
-}
-
-void
-DataSrcMatch::update(const DataSrc& new_source, const Name& container) {
-    if (getClass() != new_source.getClass() && getClass() != RRClass::ANY() &&
-        new_source.getClass() != RRClass::ANY())
-    {
-        return;
-    }
-
-    if (closest_name_ == NULL) {
-        const NameComparisonResult::NameRelation cmp =
-            getName().compare(container).getRelation();
-        if (cmp != NameComparisonResult::EQUAL &&
-            cmp != NameComparisonResult::SUBDOMAIN)
-        {
-            return;
-        }
-
-        closest_name_ = new Name(container);
-        best_source_ = &new_source;
-        return;
-    }
-
-    if (container.compare(*closest_name_).getRelation() ==
-        NameComparisonResult::SUBDOMAIN) {
-        Name* newname = new Name(container);
-        delete closest_name_;
-        closest_name_ = newname;
-        best_source_ = &new_source;
-    }
-}
-
-Nsec3Param::Nsec3Param(const uint8_t a, const uint8_t f, const uint16_t i,
-                       const std::vector<uint8_t>& s) :
-    algorithm_(a), flags_(f), iterations_(i), salt_(s)
-{}
-
-string
-Nsec3Param::getHash(const Name& name) const {
-    OutputBuffer buf(0);
-    name.toWire(buf);
-
-    uint8_t digest[SHA1_HASHSIZE];
-    const uint8_t* input = static_cast<const uint8_t*>(buf.getData());
-    size_t inlength = buf.getLength();
-    const uint8_t saltlen = salt_.size();
-
-    int n = 0;
-    SHA1Context sha;
-    do {
-        SHA1Reset(&sha);
-        SHA1Input(&sha, input, inlength);
-        SHA1Input(&sha, &salt_[0], saltlen);
-        SHA1Result(&sha, digest);
-        input = digest;
-        inlength = SHA1_HASHSIZE;
-    } while (n++ < iterations_);
-
-    return (encodeBase32Hex(vector<uint8_t>(digest, digest + SHA1_HASHSIZE)));
-}
-
-DataSrc::Result
-DataSrc::init(isc::data::ConstElementPtr) {
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findRRset(const isc::dns::Name&,
-                       const isc::dns::RRClass&,
-                       const isc::dns::RRType&,
-                       isc::dns::RRsetList&,
-                       uint32_t&,
-                       const isc::dns::Name*) const
-{
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findExactRRset(const isc::dns::Name&,
-                            const isc::dns::RRClass&,
-                            const isc::dns::RRType&,
-                            isc::dns::RRsetList&,
-                            uint32_t&,
-                            const isc::dns::Name*) const
-{
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findAddrs(const isc::dns::Name&,
-                       const isc::dns::RRClass&,
-                       isc::dns::RRsetList&,
-                       uint32_t&,
-                       const isc::dns::Name*) const
-{
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findReferral(const isc::dns::Name&,
-                          const isc::dns::RRClass&,
-                          isc::dns::RRsetList&,
-                          uint32_t&,
-                          const isc::dns::Name*) const
-{
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findPreviousName(const isc::dns::Name&,
-                              isc::dns::Name&,
-                              const isc::dns::Name*) const
-{
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-MetaDataSrc::findCoveringNSEC3(const isc::dns::Name&,
-                               std::string&,
-                               isc::dns::RRsetList&) const
-{
-    return (NOT_IMPLEMENTED);
-}
-
-}
-}
diff --git a/src/lib/datasrc/data_source.h b/src/lib/datasrc/data_source.h
index c35f0d3..37c536e 100644
--- a/src/lib/datasrc/data_source.h
+++ b/src/lib/datasrc/data_source.h
@@ -38,13 +38,6 @@ class RRsetList;
 
 namespace datasrc {
 
-class DataSrcMatch;
-class Query;
-
-class DataSrc;
-typedef boost::shared_ptr<DataSrc> DataSrcPtr;
-typedef boost::shared_ptr<const DataSrc> ConstDataSrcPtr;
-
 /// This exception represents Backend-independent errors relating to
 /// data source operations.
 class DataSourceError : public Exception {
@@ -65,361 +58,6 @@ public:
         DataSourceError(file, line, what) {}
 };
 
-
-class AbstractDataSrc {
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private to make it explicit that this is a pure base class.
-private:
-    AbstractDataSrc(const AbstractDataSrc& source);
-    AbstractDataSrc& operator=(const AbstractDataSrc& source);
-protected:
-    /// \brief The default constructor.
-    ///
-    /// This is intentionally defined as \c protected as this base class should
-    /// never be instantiated (except as part of a derived class).
-    AbstractDataSrc() {}
-public:
-    /// \brief The destructor.
-    virtual ~AbstractDataSrc() {};
-    //@}
-
-    enum Result {
-        SUCCESS,
-        ERROR,
-        NOT_IMPLEMENTED
-    };
-
-    // These flags indicate conditions encountered while processing a query.
-    //
-    // REFERRAL:       The node contains an NS record
-    // CNAME_FOUND:    The node contains a CNAME record
-    // NAME_NOT_FOUND: The node does not exist in the data source.
-    // TYPE_NOT_FOUND: The node does not contain the requested RRType
-    // NO_SUCH_ZONE:   The zone does not exist in this data source.
-    //
-    // DATA_NOT_FOUND: A combination of the last three, for coding convenience
-    enum QueryResponseFlags {
-        REFERRAL = 0x01,
-        CNAME_FOUND = 0x02,
-        NAME_NOT_FOUND = 0x04,
-        TYPE_NOT_FOUND = 0x08,
-        NO_SUCH_ZONE = 0x10,
-        DATA_NOT_FOUND = (NAME_NOT_FOUND|TYPE_NOT_FOUND|NO_SUCH_ZONE)
-    };
-
-    // 'High-level' methods.  These will be implemented by the
-    // general DataSrc class, and SHOULD NOT be overwritten by subclasses.
-    virtual void doQuery(Query& query) = 0;
-
-    // XXX: High-level methods to be implemented later:
-    // virtual void doUpdate(Update update) = 0;
-    // virtual void doXfr(Query query) = 0;
-
-    // 'Medium-level' methods.  This will be implemented by the general
-    // DataSrc class but MAY be overwritten by subclasses.
-    virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
-
-    // Optional 'low-level' methods.  These will have stub implementations
-    // in the general DataSrc class but MAY be overwritten by subclasses
-    virtual Result init() = 0;
-    virtual Result init(isc::data::ConstElementPtr config) = 0;
-    virtual Result close() = 0;
-
-    // Mandatory 'low-level' methods: These will NOT be implemented by
-    // the general DataSrc class; subclasses MUST implement them.
-    virtual Result findRRset(const isc::dns::Name& qname,
-                             const isc::dns::RRClass& qclass,
-                             const isc::dns::RRType& qtype,
-                             isc::dns::RRsetList& target,
-                             uint32_t& flags,
-                             const isc::dns::Name* zonename) const = 0;
-
-    virtual Result findExactRRset(const isc::dns::Name& qname,
-                                  const isc::dns::RRClass& qclass,
-                                  const isc::dns::RRType& qtype,
-                                  isc::dns::RRsetList& target,
-                                  uint32_t& flags,
-                                  const isc::dns::Name* zonename) const = 0;
-
-    // These will have dumb implementations in the general DataSrc
-    // class, and SHOULD be overwritten by subclasses.
-    virtual Result findAddrs(const isc::dns::Name& qname,
-                             const isc::dns::RRClass& qclass,
-                             isc::dns::RRsetList& target,
-                             uint32_t& flags,
-                             const isc::dns::Name* zonename) const = 0;
-
-     virtual Result findReferral(const isc::dns::Name& qname,
-                                 const isc::dns::RRClass& qclass,
-                                 isc::dns::RRsetList& target,
-                                 uint32_t& flags,
-                                 const isc::dns::Name* zonename) const = 0;
-
-    // This MUST be implemented by concrete data sources which support
-    // DNSSEC, but is optional for others (e.g., the static data source).
-    virtual Result findPreviousName(const isc::dns::Name& qname,
-                                    isc::dns::Name& target,
-                                    const isc::dns::Name* zonename) const = 0;
-
-   // This MUST be implemented by concrete data sources which support
-   // NSEC3, but is optional for others
-   virtual Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                                    std::string& hash,
-                                    isc::dns::RRsetList& target) const = 0;
-};
-
-// Base class for a DNS Data Source
-class DataSrc : public AbstractDataSrc {
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-private:
-    DataSrc(const DataSrc& source);
-    DataSrc& operator=(const DataSrc& source);
-public:
-    DataSrc() : rrclass(isc::dns::RRClass::IN()) {}
-    DataSrc(const isc::dns::RRClass& c) : rrclass(c) {}
-    /// \brief The destructor.
-    virtual ~DataSrc() {};
-    //@}
-
-    virtual void doQuery(Query& q);
-
-    virtual void findClosestEnclosure(DataSrcMatch& match) const = 0;
-
-    const isc::dns::RRClass& getClass() const { return (rrclass); }
-    void setClass(isc::dns::RRClass& c) { rrclass = c; }
-    void setClass(const isc::dns::RRClass& c) { rrclass = c; }
-
-    virtual Result init() { return (NOT_IMPLEMENTED); }
-    virtual Result init(isc::data::ConstElementPtr config);
-    virtual Result close() { return (NOT_IMPLEMENTED); }
-
-    virtual Result findRRset(const isc::dns::Name& qname,
-                             const isc::dns::RRClass& qclass,
-                             const isc::dns::RRType& qtype,
-                             isc::dns::RRsetList& target,
-                             uint32_t& flags,
-                             const isc::dns::Name* zonename) const = 0;
-
-    virtual Result findExactRRset(const isc::dns::Name& qname,
-                                  const isc::dns::RRClass& qclass,
-                                  const isc::dns::RRType& qtype,
-                                  isc::dns::RRsetList& target,
-                                  uint32_t& flags,
-                                  const isc::dns::Name* zonename) const = 0;
-
-    virtual Result findAddrs(const isc::dns::Name& qname,
-                             const isc::dns::RRClass& qclass,
-                             isc::dns::RRsetList& target,
-                             uint32_t& flags,
-                             const isc::dns::Name* zonename) const;
-
-    virtual Result findReferral(const isc::dns::Name& qname,
-                                const isc::dns::RRClass& qclass,
-                                isc::dns::RRsetList& target,
-                                uint32_t& flags,
-                                const isc::dns::Name* zonename) const;
-
-    virtual Result findPreviousName(const isc::dns::Name& qname,
-                                    isc::dns::Name& target,
-                                    const isc::dns::Name* zonename) const = 0;
-
-   virtual Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                                    std::string& hash,
-                                    isc::dns::RRsetList& target) const = 0;
-
-private:
-    isc::dns::RRClass rrclass;
-};
-
-class MetaDataSrc : public DataSrc {
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-private:
-    MetaDataSrc(const MetaDataSrc& source);
-    MetaDataSrc& operator=(const MetaDataSrc& source);
-public:
-    MetaDataSrc() : DataSrc(isc::dns::RRClass::ANY()) {}
-    MetaDataSrc(const isc::dns::RRClass& c) : DataSrc(c) {}
-    /// \brief The destructor.
-    virtual ~MetaDataSrc() {}
-    //@}
-
-    void addDataSrc(ConstDataSrcPtr data_src);
-    void removeDataSrc(ConstDataSrcPtr data_src);
-    size_t dataSrcCount() { return (data_sources.size()); }
-
-    void findClosestEnclosure(DataSrcMatch& match) const;
-
-    // Actual queries for data should not be sent to a MetaDataSrc object,
-    // so we return NOT_IMPLEMENTED if we receive any.
-    //
-    // The proper way to use the MetaDataSrc is to run findClosestEnclosure()
-    // to get a pointer to the best concrete data source for the specified
-    // zone, then send all queries directly to that data source.
-
-    Result findRRset(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     const isc::dns::RRType& qtype,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findExactRRset(const isc::dns::Name& qname,
-                          const isc::dns::RRClass& qclass,
-                          const isc::dns::RRType& qtype,
-                          isc::dns::RRsetList& target,
-                          uint32_t& flags,
-                          const isc::dns::Name* zonename) const;
-
-    Result findAddrs(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findReferral(const isc::dns::Name& qname,
-                        const isc::dns::RRClass& qclass,
-                        isc::dns::RRsetList& target,
-                        uint32_t& flags,
-                        const isc::dns::Name* zonename) const;
-
-    virtual Result findPreviousName(const isc::dns::Name& qname,
-                                    isc::dns::Name& target,
-                                    const isc::dns::Name* zonename) const;
-
-   virtual Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                                    std::string& hash,
-                                    isc::dns::RRsetList& target) const;
-
-private:
-    std::vector<ConstDataSrcPtr> data_sources;
-};
-
-/// \brief Information about the zone along with the %data source that best
-/// matches a give name and RR class.
-///
-/// A \c DataSrcMatch object is created with a domain name and RR class to
-/// hold the search state of looking for the zone and the %data source that
-/// stores the zone that best match the given name and RR class.
-/// The application of this class passes an object of \c DataSrcMatch to
-/// one or more ^data sources via their \c findClosestEnclosure() method.
-/// The %data source searches its content for the given key, and update
-/// the state if it finds a better zone than the currently recorded one.
-///
-/// The state of a \c DataSrcMatch object should be updated if and only if:
-///  - The specified RR class and the RR class of the %data source are the
-//     same, or the specified RR class is ANY; and
-///  - There is no matching %data source and name found (which is probably
-///    wrong, see below), or the given enclosing name gives a longer match
-///    than the currently stored enclosing name against the specified name.
-class DataSrcMatch {
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are
-    /// intentionally defined as private.
-    //@{
-private:
-    DataSrcMatch(const DataSrcMatch& source);
-    DataSrcMatch& operator=(const DataSrcMatch& source);
-public:
-    /// \brief The constructor.
-    ///
-    /// This constructor normally doesn't throw an exception.  However,
-    /// it creates a copy of the given name object, which may require memory
-    /// allocation, and if it fails the corresponding standard exception will
-    /// be thrown.
-    ///
-    /// \param name The domain name to be matched.
-    /// \param rrclass The RR class to be matched
-    DataSrcMatch(const isc::dns::Name& name,
-                 const isc::dns::RRClass& rrclass) :
-        closest_name_(NULL), best_source_(NULL),
-        name_(name), rrclass_(rrclass)
-    {}
-    ~DataSrcMatch();
-    //@}
-
-    /// \name Getter and Setter Methods
-    //@{
-    /// \brief Returns the name to be matched.
-    const isc::dns::Name& getName() const { return (name_); }
-
-    /// \brief Returns the RR class to be matched.
-    ///
-    /// This method never throws an exception.
-    const isc::dns::RRClass& getClass() const { return (rrclass_); }
-
-    /// \brief Returns the best enclosing zone name found for the given
-    // name and RR class so far.
-    ///
-    /// \return A pointer to the zone apex \c Name, NULL if none found yet.
-    ///
-    /// This method never throws an exception.
-    const isc::dns::Name* getEnclosingZone() const { return (closest_name_); }
-
-    /// \brief Returns the best %data source found for the given name and
-    /// RR class so far.
-    ///
-    /// This method never throws an exception.
-    ///
-    /// \return A pointer to a concrete %data source, NULL if none found yet.
-    const DataSrc* getDataSource() const { return (best_source_); }
-    //@}
-
-    /// \brief Update the object state with better information if possible.
-    ///
-    /// This method is intended to be called by a concrete %data source's
-    /// \c findClosestEnclosure() method to store the best match for
-    /// the given name and class that has been found so far.
-    ///
-    /// It compares the best name (if found) and \c container, and if the
-    /// latter gives a longer match, it will install the given %data source
-    /// and the enclosing name as the best match;
-    /// if there is no known pair of %data source and enclosing name,
-    /// this method will install the given pair unconditionally.
-    /// (which is probably BAD);
-    /// otherwise this method does nothing.
-    ///
-    /// In any case, if a new pair of %data source and enclosing name are
-    /// installed, a new name object will be internally allocated.
-    /// And, if memory allocation fails the corresponding standard exception
-    /// will be thrown.
-    ///
-    /// \param new_source A candidate %data source that gives a better match.
-    /// \param container The enclosing name of the matching zone in
-    /// \c new_source.
-    void update(const DataSrc& new_source, const isc::dns::Name& container);
-
-private:
-    isc::dns::Name* closest_name_;
-    const DataSrc* best_source_;
-    const isc::dns::Name name_;
-    const isc::dns::RRClass& rrclass_;
-};
-
-class Nsec3Param {
-public:
-    Nsec3Param(uint8_t a, uint8_t f, uint16_t i, const std::vector<uint8_t>& s);
-    std::string getHash(const isc::dns::Name& name) const;
-private:
-    const uint8_t algorithm_;
-    const uint8_t flags_;
-    const uint16_t iterations_;
-    const std::vector<uint8_t> salt_;
-};
-
 }
 }
 
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 320fe51..b4947a7 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -386,10 +386,11 @@ DatabaseClient::Finder::findAll(const isc::dns::Name& name,
                                 std::vector<isc::dns::ConstRRsetPtr>& target,
                                 const FindOptions options)
 {
-    return (ZoneFinderContextPtr(new Context(*this, options,
-                                             findInternal(name, RRType::ANY(),
-                                                          &target, options),
-                                             target)));
+    return (ZoneFinderContextPtr(new GenericContext(
+                                     *this, options,
+                                     findInternal(name, RRType::ANY(),
+                                                  &target, options),
+                                     target)));
 }
 
 ZoneFinderContextPtr
@@ -400,9 +401,10 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     if (type == RRType::ANY()) {
         isc_throw(isc::Unexpected, "Use findAll to answer ANY");
     }
-    return (ZoneFinderContextPtr(new Context(*this, options,
-                                             findInternal(name, type, NULL,
-                                                          options))));
+    return (ZoneFinderContextPtr(new GenericContext(
+                                     *this, options,
+                                     findInternal(name, type, NULL,
+                                                  options))));
 }
 
 DatabaseClient::Finder::DelegationSearchResult
diff --git a/src/lib/datasrc/memory/Makefile.am b/src/lib/datasrc/memory/Makefile.am
index eea9c0b..743caa2 100644
--- a/src/lib/datasrc/memory/Makefile.am
+++ b/src/lib/datasrc/memory/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . tests benchmarks
+SUBDIRS = . benchmarks
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
@@ -20,6 +20,8 @@ libdatasrc_memory_la_SOURCES += memory_client.h memory_client.cc
 libdatasrc_memory_la_SOURCES += logger.h logger.cc
 libdatasrc_memory_la_SOURCES += zone_table.h zone_table.cc
 libdatasrc_memory_la_SOURCES += zone_finder.h zone_finder.cc
+libdatasrc_memory_la_SOURCES += zone_table_segment.h zone_table_segment.cc
+libdatasrc_memory_la_SOURCES += zone_table_segment_local.h zone_table_segment_local.cc
 nodist_libdatasrc_memory_la_SOURCES = memory_messages.h memory_messages.cc
 
 EXTRA_DIST  = rdata_serialization_priv.cc
diff --git a/src/lib/datasrc/memory/domaintree.h b/src/lib/datasrc/memory/domaintree.h
index b29f10f..20f4693 100644
--- a/src/lib/datasrc/memory/domaintree.h
+++ b/src/lib/datasrc/memory/domaintree.h
@@ -373,10 +373,23 @@ private:
         }
     }
 
+    /// \brief returns if the node is a subtree's root node
+    ///
+    /// This method takes a node and returns \c true if it is the root
+    /// node of the subtree it belongs to.
+    ///
+    /// This method never throws an exception.
     bool isSubTreeRoot() const {
         return ((flags_ & FLAG_SUBTREE_ROOT) != 0);
     }
 
+    /// \brief returns the root of its subtree
+    ///
+    /// This method takes a node and returns the root of its subtree.
+    ///
+    /// This method never throws an exception.
+    const DomainTreeNode<T>* getSubTreeRoot() const;
+
 public:
     /// \brief returns the parent of the root of its subtree
     ///
@@ -388,7 +401,6 @@ public:
     /// This method never throws an exception.
     const DomainTreeNode<T>* getUpperNode() const;
 
-private:
     /// \brief return the next node which is bigger than current node
     /// in the same subtree
     ///
@@ -423,6 +435,7 @@ private:
     /// This method never throws an exception.
     const DomainTreeNode<T>* predecessor() const;
 
+private:
     /// \brief private shared implementation of successor and predecessor
     ///
     /// As the two mentioned functions are merely mirror images of each other,
@@ -538,7 +551,7 @@ DomainTreeNode<T>::~DomainTreeNode() {
 
 template <typename T>
 const DomainTreeNode<T>*
-DomainTreeNode<T>::getUpperNode() const {
+DomainTreeNode<T>::getSubTreeRoot() const {
     const DomainTreeNode<T>* current = this;
 
     // current would never be equal to NULL here (in a correct tree
@@ -547,7 +560,13 @@ DomainTreeNode<T>::getUpperNode() const {
         current = current->getParent();
     }
 
-    return (current->getParent());
+    return (current);
+}
+
+template <typename T>
+const DomainTreeNode<T>*
+DomainTreeNode<T>::getUpperNode() const {
+    return (getSubTreeRoot()->getParent());
 }
 
 template <typename T>
@@ -555,7 +574,17 @@ isc::dns::LabelSequence
 DomainTreeNode<T>::getAbsoluteLabels(
     uint8_t buf[isc::dns::LabelSequence::MAX_SERIALIZED_LENGTH]) const
 {
-    isc::dns::LabelSequence result(getLabels(), buf);
+    // If the current node already has absolute labels, just return it.
+    // This should normally be the case for the origin node if this tree
+    // is used to represent a single DNS zone.
+    const isc::dns::LabelSequence cur_labels(getLabels());
+    if (cur_labels.isAbsolute()) {
+        return (cur_labels);
+    }
+
+    // Otherwise, build the absolute sequence traversing the tree of tree
+    // toward the top root.
+    isc::dns::LabelSequence result(cur_labels, buf);
     const DomainTreeNode<T>* upper = getUpperNode();
     while (upper != NULL) {
         result.extend(upper->getLabels(), buf);
@@ -672,14 +701,26 @@ public:
     /// The default constructor.
     ///
     /// \exception None
-    DomainTreeNodeChain() : node_count_(0), last_compared_(NULL),
+    DomainTreeNodeChain() : level_count_(0), last_compared_(NULL),
                         // XXX: meaningless initial values:
                         last_comparison_(0, 0,
                                          isc::dns::NameComparisonResult::EQUAL)
     {}
 
+    /// \brief Copy constructor.
+    ///
+    /// \exception None
+    DomainTreeNodeChain(const DomainTreeNodeChain<T>& other) :
+        level_count_(other.level_count_),
+        last_compared_(other.last_compared_),
+        last_comparison_(other.last_comparison_)
+    {
+        for (size_t i = 0; i < level_count_; i++) {
+            nodes_[i] = other.nodes_[i];
+        }
+    }
+
 private:
-    DomainTreeNodeChain(const DomainTreeNodeChain<T>&);
     DomainTreeNodeChain<T>& operator=(const DomainTreeNodeChain<T>&);
     //@}
 
@@ -691,7 +732,7 @@ public:
     ///
     /// \exception None
     void clear() {
-        node_count_ = 0;
+        level_count_ = 0;
         last_compared_ = NULL;
     }
 
@@ -732,7 +773,7 @@ public:
     /// chain, 0 will be returned.
     ///
     /// \exception None
-    unsigned int getLevelCount() const { return (node_count_); }
+    size_t getLevelCount() const { return (level_count_); }
 
     /// \brief return the absolute name for the node which this
     /// \c DomainTreeNodeChain currently refers to.
@@ -750,11 +791,11 @@ public:
 
         const DomainTreeNode<T>* top_node = top();
         isc::dns::Name absolute_name = top_node->getName();
-        int node_count = node_count_ - 1;
-        while (node_count > 0) {
-            top_node = nodes_[node_count - 1];
+        size_t level = level_count_ - 1;
+        while (level > 0) {
+            top_node = nodes_[level - 1];
             absolute_name = absolute_name.concatenate(top_node->getName());
-            --node_count;
+            --level;
         }
         return (absolute_name);
     }
@@ -768,7 +809,7 @@ private:
     /// \brief return whether node chain has node in it.
     ///
     /// \exception None
-    bool isEmpty() const { return (node_count_ == 0); }
+    bool isEmpty() const { return (level_count_ == 0); }
 
     /// \brief return the top node for the node chain
     ///
@@ -778,7 +819,7 @@ private:
     /// \exception None
     const DomainTreeNode<T>* top() const {
         assert(!isEmpty());
-        return (nodes_[node_count_ - 1]);
+        return (nodes_[level_count_ - 1]);
     }
 
     /// \brief pop the top node from the node chain
@@ -789,7 +830,7 @@ private:
     /// \exception None
     void pop() {
         assert(!isEmpty());
-        --node_count_;
+        --level_count_;
     }
 
     /// \brief add the node into the node chain
@@ -800,8 +841,8 @@ private:
     ///
     /// \exception None
     void push(const DomainTreeNode<T>* node) {
-        assert(node_count_ < RBT_MAX_LEVEL);
-        nodes_[node_count_++] = node;
+        assert(level_count_ < RBT_MAX_LEVEL);
+        nodes_[level_count_++] = node;
     }
 
 private:
@@ -810,7 +851,7 @@ private:
     // it's also equal to the possible maximum level.
     const static int RBT_MAX_LEVEL = isc::dns::Name::MAX_LABELS;
 
-    int node_count_;
+    size_t level_count_;
     const DomainTreeNode<T>* nodes_[RBT_MAX_LEVEL];
     const DomainTreeNode<T>* last_compared_;
     isc::dns::NameComparisonResult last_comparison_;
@@ -1265,12 +1306,20 @@ public:
     const DomainTreeNode<T>*
     previousNode(DomainTreeNodeChain<T>& node_path) const;
 
+    /// \brief return the largest node in the tree of trees.
+    ///
+    /// \throw none
+    ///
+    /// \return A \c DomainTreeNode that is the largest node in the
+    /// tree. If there are no nodes, then \c NULL is returned.
+    const DomainTreeNode<T>* largestNode() const;
+
     /// \brief Get the total number of nodes in the tree
     ///
     /// It includes nodes internally created as a result of adding a domain
     /// name that is a subdomain of an existing node of the tree.
     /// This function is mainly intended to be used for debugging.
-    int getNodeCount() const { return (node_count_); }
+    uint32_t getNodeCount() const { return (node_count_); }
 
     /// \name Debug function
     //@{
@@ -1402,8 +1451,15 @@ private:
     //@}
 
     typename DomainTreeNode<T>::DomainTreeNodePtr root_;
-    /// the node count of current tree
-    unsigned int node_count_;
+
+    /// the node count of current tree.
+    ///
+    /// Note: uint32_t may look awkward, but we intentionally choose it so
+    /// that needsReturnEmptyNode_ below won't make cause extra padding
+    /// in 64-bit machines (and we can minimize the total size of this class).
+    /// 2^32 - 1 should be a reasonable max of possible number of nodes.
+    uint32_t node_count_;
+
     /// search policy for domaintree
     const bool needsReturnEmptyNode_;
 };
@@ -1710,6 +1766,24 @@ DomainTree<T>::previousNode(DomainTreeNodeChain<T>& node_path) const {
 }
 
 template <typename T>
+const DomainTreeNode<T>*
+DomainTree<T>::largestNode() const {
+    const DomainTreeNode<T>* node = root_.get();
+    while (node != NULL) {
+        // We go right first, then down.
+        if (node->getRight() != NULL) {
+            node = node->getRight();
+        } else if (node->getDown() != NULL) {
+            node = node->getDown();
+        } else {
+            break;
+        }
+    }
+
+    return (node);
+}
+
+template <typename T>
 typename DomainTree<T>::Result
 DomainTree<T>::insert(util::MemorySegment& mem_sgmt,
                       const isc::dns::Name& target_name,
@@ -1826,7 +1900,7 @@ DomainTree<T>::nodeFission(util::MemorySegment& mem_sgmt,
             node.getParent()->down_ = up_node;
         }
     } else {
-        this->root_ = up_node;
+        root_ = up_node;
     }
 
     up_node->down_ = &node;
diff --git a/src/lib/datasrc/memory/memory_client.cc b/src/lib/datasrc/memory/memory_client.cc
index d70f345..5f6f510 100644
--- a/src/lib/datasrc/memory/memory_client.cc
+++ b/src/lib/datasrc/memory/memory_client.cc
@@ -22,6 +22,7 @@
 #include <datasrc/memory/domaintree.h>
 #include <datasrc/memory/segment_object_holder.h>
 #include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/zone_finder.h>
 
 #include <util/memory_segment_local.h>
 
@@ -41,6 +42,7 @@
 #include <boost/scoped_ptr.hpp>
 #include <boost/bind.hpp>
 #include <boost/foreach.hpp>
+#include <boost/noncopyable.hpp>
 
 #include <algorithm>
 #include <map>
@@ -99,9 +101,6 @@ public:
         FileNameTree::destroy(mem_sgmt_, file_name_tree_, deleter);
 
         ZoneTable::destroy(mem_sgmt_, zone_table_, rrclass_);
-
-        // see above for the assert().
-        assert(mem_sgmt_.allMemoryDeallocated());
     }
 
     util::MemorySegment& mem_sgmt_;
@@ -109,7 +108,6 @@ public:
     unsigned int zone_count_;
     ZoneTable* zone_table_;
     FileNameTree* file_name_tree_;
-    ConstRRsetPtr last_rrset_;
 
     // Common process for zone load.
     // rrset_installer is a functor that takes another functor as an argument,
@@ -127,7 +125,7 @@ public:
     //
     // In order for wildcard matching to work correctly in the zone finder,
     // we must ensure that a node for the wildcarding level exists in the
-    // backend RBTree.
+    // backend ZoneTree.
     // E.g. if the wildcard name is "*.sub.example." then we must ensure
     // that "sub.example." exists and is marked as a wildcard level.
     // Note: the "wildcarding level" is for the parent name of the wildcard
@@ -171,7 +169,8 @@ public:
      * If such condition is found, it throws AddError.
      */
     void contextCheck(const Name& zone_name, const AbstractRRset& rrset,
-                      const RdataSet* set) const {
+                      const RdataSet* set) const
+    {
         // Ensure CNAME and other type of RR don't coexist for the same
         // owner name except with NSEC, which is the only RR that can coexist
         // with CNAME (and also RRSIG, which is handled separately)
@@ -247,7 +246,23 @@ public:
                       << rrset->getName() << " which isn't supported");
         }
 
-        NameComparisonResult compare(zone_name.compare(rrset->getName()));
+        // For RRSIGs, check consistency of the type covered.
+        // We know the RRset isn't empty, so the following check is safe.
+        if (rrset->getType() == RRType::RRSIG()) {
+            RdataIteratorPtr rit = rrset->getRdataIterator();
+            const RRType covered = dynamic_cast<const generic::RRSIG&>(
+                rit->getCurrent()).typeCovered();
+            for (rit->next(); !rit->isLast(); rit->next()) {
+                if (dynamic_cast<const generic::RRSIG&>(
+                        rit->getCurrent()).typeCovered() != covered) {
+                    isc_throw(AddError, "RRSIG contains mixed covered types: "
+                              << rrset->toText());
+                }
+            }
+        }
+
+        const NameComparisonResult compare =
+            zone_name.compare(rrset->getName());
         if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
             compare.getRelation() != NameComparisonResult::EQUAL)
         {
@@ -262,10 +277,9 @@ public:
         // Even though the protocol specifically doesn't completely ban such
         // usage, we refuse to load a zone containing such RR in order to
         // keep the lookup logic simpler and more predictable.
-        // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
-        // for more technical background.  Note also that BIND 9 refuses
-        // NS at a wildcard, so in that sense we simply provide compatible
-        // behavior.
+        // See RFC4592 and (for DNAME) RFC6672 for more technical background.
+        // Note also that BIND 9 refuses NS at a wildcard, so in that sense
+        // we simply provide compatible behavior.
         if (rrset->getName().isWildcard()) {
             if (rrset->getType() == RRType::NS()) {
                 LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
@@ -299,7 +313,8 @@ public:
 
     void addNSEC3(const ConstRRsetPtr rrset,
                   const ConstRRsetPtr rrsig,
-                  ZoneData& zone_data) {
+                  ZoneData& zone_data)
+    {
         // We know rrset has exactly one RDATA
         const generic::NSEC3& nsec3_rdata =
             dynamic_cast<const generic::NSEC3&>(
@@ -309,6 +324,7 @@ public:
         if (nsec3_data == NULL) {
             nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
             zone_data.setNSEC3Data(nsec3_data);
+            zone_data.setSigned(true);
         } else {
             size_t salt_len = nsec3_data->getSaltLen();
             const uint8_t* salt_data = nsec3_data->getSaltData();
@@ -316,7 +332,12 @@ public:
 
             if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
                 (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
-                (salt_data_2.size() != salt_len) ||
+                (salt_data_2.size() != salt_len)) {
+                isc_throw(AddError,
+                          "NSEC3 with inconsistent parameters: " <<
+                          rrset->toText());
+            }
+            if ((salt_len > 0) &&
                 (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
                 isc_throw(AddError,
                           "NSEC3 with inconsistent parameters: " <<
@@ -324,17 +345,10 @@ public:
             }
         }
 
-        string fst_label = rrset->getName().split(0, 1).toText(true);
-        transform(fst_label.begin(), fst_label.end(), fst_label.begin(),
-                  ::toupper);
-
         ZoneNode* node;
-        nsec3_data->insertName(mem_sgmt_, Name(fst_label), &node);
+        nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
 
         RdataEncoder encoder;
-
-        // We assume that rrsig has already been checked to match rrset
-        // by the caller.
         RdataSet* set = RdataSet::create(mem_sgmt_, encoder, rrset, rrsig);
         RdataSet* old_set = node->setData(set);
         if (old_set != NULL) {
@@ -343,101 +357,63 @@ public:
     }
 
     void addRdataSet(const Name& zone_name, ZoneData& zone_data,
-                     const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig) {
-        // Only one of these can be passed at a time.
-        assert(!(rrset && rrsig));
-
-        // If rrsig is passed, validate it against the last-saved rrset.
-        if (rrsig) {
-            // The covered RRset should have been saved by now.
-            if (!last_rrset_) {
-                isc_throw(AddError,
-                          "RRSIG is being added, "
-                          "but doesn't follow its covered RR: "
-                          << rrsig->getName());
-            }
-
-            if (rrsig->getName() != last_rrset_->getName()) {
-                isc_throw(AddError,
-                          "RRSIG is being added, "
-                          "but doesn't match the last RR's name: "
-                          << last_rrset_->getName() << " vs. "
-                          << rrsig->getName());
-            }
-
-            // Consistency of other types in rrsig are checked in addRRsig().
-            RdataIteratorPtr rit = rrsig->getRdataIterator();
-            const RRType covered = dynamic_cast<const generic::RRSIG&>(
-                rit->getCurrent()).typeCovered();
-
-            if (covered != last_rrset_->getType()) {
-                isc_throw(AddError,
-                          "RRSIG is being added, "
-                          "but doesn't match the last RR's type: "
-                          << last_rrset_->getType() << " vs. "
-                          << covered);
-            }
-        }
-
-        if (!last_rrset_) {
-            last_rrset_ = rrset;
-            return;
-        }
-
-        if (last_rrset_->getType() == RRType::NSEC3()) {
-            addNSEC3(last_rrset_, rrsig, zone_data);
+                     const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig)
+    {
+        if (rrset->getType() == RRType::NSEC3()) {
+            addNSEC3(rrset, rrsig, zone_data);
         } else {
             ZoneNode* node;
-            zone_data.insertName(mem_sgmt_, last_rrset_->getName(), &node);
+            zone_data.insertName(mem_sgmt_, rrset->getName(), &node);
 
-            RdataSet* set = node->getData();
+            RdataSet* rdataset_head = node->getData();
 
             // Checks related to the surrounding data.
             // Note: when the check fails and the exception is thrown,
             // it may break strong exception guarantee.  At the moment
             // we prefer code simplicity and don't bother to introduce
             // complicated recovery code.
-            contextCheck(zone_name, *last_rrset_, set);
+            contextCheck(zone_name, *rrset, rdataset_head);
 
-            if (RdataSet::find(set, last_rrset_->getType()) != NULL) {
+            if (RdataSet::find(rdataset_head, rrset->getType()) != NULL) {
                 isc_throw(AddError,
                           "RRset of the type already exists: "
-                          << last_rrset_->getName() << " (type: "
-                          << last_rrset_->getType() << ")");
+                          << rrset->getName() << " (type: "
+                          << rrset->getType() << ")");
             }
 
             RdataEncoder encoder;
-            RdataSet *new_set = RdataSet::create(mem_sgmt_, encoder,
-                                                 last_rrset_, rrsig);
-            new_set->next = set;
-            node->setData(new_set);
+            RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder, rrset,
+                                                  rrsig);
+            rdataset->next = rdataset_head;
+            node->setData(rdataset);
 
             // Ok, we just put it in
 
             // If this RRset creates a zone cut at this node, mark the
             // node indicating the need for callback in find().
-            if (last_rrset_->getType() == RRType::NS() &&
-                last_rrset_->getName() != zone_name) {
+            if (rrset->getType() == RRType::NS() &&
+                rrset->getName() != zone_name) {
                 node->setFlag(ZoneNode::FLAG_CALLBACK);
                 // If it is DNAME, we have a callback as well here
-            } else if (last_rrset_->getType() == RRType::DNAME()) {
+            } else if (rrset->getType() == RRType::DNAME()) {
                 node->setFlag(ZoneNode::FLAG_CALLBACK);
             }
 
             // If we've added NSEC3PARAM at zone origin, set up NSEC3
             // specific data or check consistency with already set up
             // parameters.
-            if (last_rrset_->getType() == RRType::NSEC3PARAM() &&
-                last_rrset_->getName() == zone_name) {
+            if (rrset->getType() == RRType::NSEC3PARAM() &&
+                rrset->getName() == zone_name) {
                 // We know rrset has exactly one RDATA
                 const generic::NSEC3PARAM& param =
                     dynamic_cast<const generic::NSEC3PARAM&>
-                      (last_rrset_->getRdataIterator()->getCurrent());
+                      (rrset->getRdataIterator()->getCurrent());
 
                 NSEC3Data* nsec3_data = zone_data.getNSEC3Data();
                 if (nsec3_data == NULL) {
                     nsec3_data = NSEC3Data::create(mem_sgmt_, param);
                     zone_data.setNSEC3Data(nsec3_data);
+                    zone_data.setSigned(true);
                 } else {
                     size_t salt_len = nsec3_data->getSaltLen();
                     const uint8_t* salt_data = nsec3_data->getSaltData();
@@ -445,96 +421,160 @@ public:
 
                     if ((param.getHashalg() != nsec3_data->hashalg) ||
                         (param.getIterations() != nsec3_data->iterations) ||
-                        (salt_data_2.size() != salt_len) ||
+                        (salt_data_2.size() != salt_len)) {
+                        isc_throw(AddError,
+                                  "NSEC3PARAM with inconsistent parameters: "
+                                  << rrset->toText());
+                    }
+
+                    if ((salt_len > 0) &&
                         (std::memcmp(&salt_data_2[0],
                                      salt_data, salt_len) != 0)) {
                         isc_throw(AddError,
                                   "NSEC3PARAM with inconsistent parameters: "
-                                  << last_rrset_->toText());
+                                  << rrset->toText());
                     }
                 }
-            } else if (last_rrset_->getType() == RRType::NSEC()) {
-                // If it is NSEC signed zone, so we put a flag there
-                // (flag is enough)
+            } else if (rrset->getType() == RRType::NSEC()) {
+                // If it is NSEC signed zone, we mark the zone as signed
+                // (conceptually "signed" is a broader notion but our current
+                // zone finder implementation regards "signed" as "NSEC
+                // signed")
                 zone_data.setSigned(true);
             }
         }
-
-        last_rrset_ = rrset;
-    }
-
-    result::Result addRRsig(const ConstRRsetPtr sig_rrset,
-                            const Name& zone_name, ZoneData& zone_data)
-    {
-        // Check consistency of the type covered.
-        // We know the RRset isn't empty, so the following check is safe.
-        RdataIteratorPtr rit = sig_rrset->getRdataIterator();
-        const RRType covered = dynamic_cast<const generic::RRSIG&>(
-            rit->getCurrent()).typeCovered();
-        for (rit->next(); !rit->isLast(); rit->next()) {
-            if (dynamic_cast<const generic::RRSIG&>(
-                    rit->getCurrent()).typeCovered() != covered) {
-                isc_throw(AddError, "RRSIG contains mixed covered types: "
-                          << sig_rrset->toText());
-            }
-        }
-
-        addRdataSet(zone_name, zone_data, ConstRRsetPtr(), sig_rrset);
-        return (result::SUCCESS);
     }
 
-    /*
-     * Implementation of longer methods. We put them here, because the
-     * access is without the impl_-> and it will get inlined anyway.
-     */
-
     // Implementation of InMemoryClient::add()
-    result::Result add(const ConstRRsetPtr& rrset,
-                       const Name& zone_name, ZoneData& zone_data)
+    void add(const ConstRRsetPtr& rrset, const ConstRRsetPtr& sig_rrset,
+             const Name& zone_name, ZoneData& zone_data)
     {
         // Sanitize input.  This will cause an exception to be thrown
         // if the input RRset is empty.
         addValidation(zone_name, rrset);
+        if (sig_rrset) {
+            addValidation(zone_name, sig_rrset);
+        }
 
         // OK, can add the RRset.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).
             arg(rrset->getName()).arg(rrset->getType()).arg(zone_name);
 
-        if (rrset->getType() == RRType::NSEC3()) {
-            addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
-            return (result::SUCCESS);
+        // Add wildcards possibly contained in the owner name to the domain
+        // tree.  This can only happen for the normal (non-NSEC3) tree.
+        // Note: this can throw an exception, breaking strong exception
+        // guarantee.  (see also the note for the call to contextCheck()
+        // above).
+        if (rrset->getType() != RRType::NSEC3()) {
+            addWildcards(zone_name, zone_data, rrset->getName());
         }
 
-        // RRSIGs are special in various points, so we handle it in a
-        // separate dedicated method.
-        if (rrset->getType() == RRType::RRSIG()) {
-            return (addRRsig(rrset, zone_name, zone_data));
+        addRdataSet(zone_name, zone_data, rrset, sig_rrset);
+    }
+};
+
+// A helper internal class for load().  make it non-copyable to avoid
+// accidental copy.
+//
+// The current internal implementation expects that both a normal
+// (non RRSIG) RRset and (when signed) its RRSIG are added at once.
+// Also in the current implementation, the input sequence of RRsets
+// are grouped with their owner name (so once a new owner name is encountered,
+// no subsequent RRset has the previous owner name), but the ordering
+// in the same group is not fixed.  So we hold all RRsets of the same
+// owner name in node_rrsets_ and node_rrsigsets_, and add the matching
+// pairs of RRsets to the zone when we see a new owner name.
+//
+// The caller is responsible for adding the RRsets of the last group
+// in the input sequence by explicitly calling flushNodeRRsets() at the
+// end.  It's cleaner and more robust if we let the destructor of this class
+// do it, but since we cannot guarantee the adding operation is exception free,
+// we don't choose that option to maintain the common expectation for
+// destructors.
+class InMemoryClient::Loader : boost::noncopyable {
+    typedef std::map<RRType, ConstRRsetPtr> NodeRRsets;
+    typedef NodeRRsets::value_type NodeRRsetsVal;
+public:
+    Loader(InMemoryClientImpl* client_impl, const Name& zone_name,
+           ZoneData& zone_data) :
+        client_impl_(client_impl), zone_name_(zone_name), zone_data_(zone_data)
+    {}
+    void addFromLoad(const ConstRRsetPtr& rrset) {
+        // If we see a new name, flush the temporary holders, adding the
+        // pairs of RRsets and RRSIGs of the previous name to the zone.
+        if ((!node_rrsets_.empty() || !node_rrsigsets_.empty()) &&
+            getCurrentName() != rrset->getName()) {
+            flushNodeRRsets();
         }
 
-        // Add wildcards possibly contained in the owner name to the domain
-        // tree.
-        // Note: this can throw an exception, breaking strong exception
-        // guarantee.  (see also the note for contextCheck() below).
-        addWildcards(zone_name, zone_data, rrset->getName());
+        // Store this RRset until it can be added to the zone.  The current
+        // implementation requires RRs of the same RRset should be added at
+        // once, so we check the "duplicate" here.
+        const bool is_rrsig = rrset->getType() == RRType::RRSIG();
+        NodeRRsets& node_rrsets = is_rrsig ? node_rrsigsets_ : node_rrsets_;
+        const RRType& rrtype = is_rrsig ?
+            getCoveredType(rrset) : rrset->getType();
+        if (!node_rrsets.insert(NodeRRsetsVal(rrtype, rrset)).second) {
+            isc_throw(AddError,
+                      "Duplicate add of the same type of"
+                      << (is_rrsig ? " RRSIG" : "") << " RRset: "
+                      << rrset->getName() << "/" << rrtype);
+        }
+    }
+    void flushNodeRRsets() {
+        BOOST_FOREACH(NodeRRsetsVal val, node_rrsets_) {
+            // Identify the corresponding RRSIG for the RRset, if any.
+            // If found add both the RRset and its RRSIG at once.
+            ConstRRsetPtr sig_rrset;
+            NodeRRsets::iterator sig_it =
+                node_rrsigsets_.find(val.first);
+            if (sig_it != node_rrsigsets_.end()) {
+                sig_rrset = sig_it->second;
+                node_rrsigsets_.erase(sig_it);
+            }
+            client_impl_->add(val.second, sig_rrset, zone_name_, zone_data_);
+        }
 
-        addRdataSet(zone_name, zone_data, rrset, ConstRRsetPtr());
+        // Right now, we don't accept RRSIG without covered RRsets (this
+        // should eventually allowed, but to do so we'll need to update the
+        // finder).
+        if (!node_rrsigsets_.empty()) {
+            isc_throw(AddError, "RRSIG is added without covered RRset for "
+                      << getCurrentName());
+        }
 
-        return (result::SUCCESS);
+        node_rrsets_.clear();
+        node_rrsigsets_.clear();
     }
-
-    /*
-     * Wrapper around above.
-     */
-    void addFromLoad(const ConstRRsetPtr& set,
-                     const Name& zone_name, ZoneData* zone_data)
-    {
-        switch (add(set, zone_name, *zone_data)) {
-        case result::SUCCESS:
-            return;
-        default:
-            assert(0);
+private:
+    // A helper to identify the covered type of an RRSIG.
+    static RRType getCoveredType(const ConstRRsetPtr& sig_rrset) {
+        RdataIteratorPtr it = sig_rrset->getRdataIterator();
+        // Empty RRSIG shouldn't be passed either via a master file or another
+        // data source iterator, but it could still happen if the iterator
+        // has a bug.  We catch and reject such cases.
+        if (it->isLast()) {
+            isc_throw(isc::Unexpected,
+                      "Empty RRset is passed in-memory loader, name: "
+                      << sig_rrset->getName());
         }
+        return (dynamic_cast<const generic::RRSIG&>(it->getCurrent()).
+                typeCovered());
     }
+    const Name& getCurrentName() const {
+        if (!node_rrsets_.empty()) {
+            return (node_rrsets_.begin()->second->getName());
+        }
+        assert(!node_rrsigsets_.empty());
+        return (node_rrsigsets_.begin()->second->getName());
+    }
+
+private:
+    InMemoryClientImpl* client_impl_;
+    const Name& zone_name_;
+    ZoneData& zone_data_;
+    NodeRRsets node_rrsets_;
+    NodeRRsets node_rrsigsets_;
 };
 
 result::Result
@@ -544,31 +584,17 @@ InMemoryClient::InMemoryClientImpl::load(
     boost::function<void(LoadCallback)> rrset_installer)
 {
     SegmentObjectHolder<ZoneData, RRClass> holder(
-        mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name),
-        rrclass_);
-
-    assert(!last_rrset_);
-
-    try {
-        rrset_installer(boost::bind(&InMemoryClientImpl::addFromLoad, this,
-                                    _1, zone_name, holder.get()));
-        // Add any last RRset that was left
-        addRdataSet(zone_name, *holder.get(),
-                    ConstRRsetPtr(), ConstRRsetPtr());
-    } catch (...) {
-        last_rrset_ = ConstRRsetPtr();
-        throw;
-    }
+        mem_sgmt_, ZoneData::create(mem_sgmt_, zone_name), rrclass_);
 
-    assert(!last_rrset_);
+    Loader loader(this, zone_name, *holder.get());
+    rrset_installer(boost::bind(&Loader::addFromLoad, &loader, _1));
+    // Add any last RRsets that were left
+    loader.flushNodeRRsets();
 
     const ZoneNode* origin_node = holder.get()->getOriginNode();
     const RdataSet* set = origin_node->getData();
     // If the zone is NSEC3-signed, check if it has NSEC3PARAM
     if (holder.get()->isNSEC3Signed()) {
-        // Note: origin_data_ is set on creation of ZoneData, and the load
-        // process only adds new nodes (and their data), so this assertion
-        // should hold.
         if (RdataSet::find(set, RRType::NSEC3PARAM()) == NULL) {
             LOG_WARN(logger, DATASRC_MEMORY_MEM_NO_NSEC3PARAM).
                 arg(zone_name).arg(rrclass_);
@@ -584,7 +610,7 @@ InMemoryClient::InMemoryClientImpl::load(
     }
 
     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEMORY_MEM_ADD_ZONE).
-        arg(zone_name).arg(rrclass_.toText());
+        arg(zone_name).arg(rrclass_);
 
     // Set the filename in file_name_tree_ now, so that getFileName()
     // can use it (during zone reloading).
@@ -639,30 +665,8 @@ masterLoadWrapper(const char* const filename, const Name& origin,
 void
 generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
     ConstRRsetPtr rrset;
-    vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
-
-    // The current internal implementation assumes an RRSIG is always added
-    // after the RRset they cover.  So we store any RRSIGs in 'rrsigs' until
-    // it's safe to add them; based on our assumption if the owner name
-    // changes, all covered RRsets of the previous name should have been
-    // installed and any pending RRSIGs can be added at that point.  RRSIGs
-    // of the last name from the iterator must be added separately.
     while ((rrset = iterator->getNextRRset()) != NULL) {
-        if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
-            BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
-                callback(sig_rrset);
-            }
-            rrsigs.clear();
-        }
-        if (rrset->getType() == RRType::RRSIG()) {
-            rrsigs.push_back(rrset);
-        } else {
-            callback(rrset);
-        }
-    }
-
-    BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
-        callback(sig_rrset);
+        callback(rrset);
     }
 }
 }
@@ -686,21 +690,25 @@ InMemoryClient::getZoneCount() const {
     return (impl_->zone_count_);
 }
 
-isc::datasrc::memory::ZoneTable::FindResult
-InMemoryClient::findZone2(const isc::dns::Name& zone_name) const {
+isc::datasrc::DataSourceClient::FindResult
+InMemoryClient::findZone(const isc::dns::Name& zone_name) const {
     LOG_DEBUG(logger, DBG_TRACE_DATA,
               DATASRC_MEMORY_MEM_FIND_ZONE).arg(zone_name);
+
     ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
-    return (result);
+
+    ZoneFinderPtr finder;
+    if (result.code != result::NOTFOUND) {
+        finder.reset(new InMemoryZoneFinder(*result.zone_data, getClass()));
+    }
+
+    return (DataSourceClient::FindResult(result.code, finder));
 }
 
-isc::datasrc::DataSourceClient::FindResult
-InMemoryClient::findZone(const isc::dns::Name&) const {
-    // This variant of findZone() is not implemented and should be
-    // removed eventually. It currently throws an exception. It is
-    // required right now to derive from DataSourceClient.
-    isc_throw(isc::NotImplemented,
-              "This variant of findZone() is not implemented.");
+const ZoneData*
+InMemoryClient::findZoneData(const isc::dns::Name& zone_name) {
+    ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+    return (result.zone_data);
 }
 
 result::Result
@@ -736,29 +744,20 @@ InMemoryClient::getFileName(const isc::dns::Name& zone_name) const {
 
 result::Result
 InMemoryClient::add(const isc::dns::Name& zone_name,
-                    const ConstRRsetPtr& rrset) {
-    assert(!impl_->last_rrset_);
-
-    ZoneTable::FindResult result(impl_->zone_table_->findZone(zone_name));
+                    const ConstRRsetPtr& rrset)
+{
+    const ZoneTable::FindResult result =
+        impl_->zone_table_->findZone(zone_name);
     if (result.code != result::SUCCESS) {
         isc_throw(DataSourceError, "No such zone: " + zone_name.toText());
     }
 
-    result::Result ret(impl_->add(rrset, zone_name, *result.zone_data));
-    // Add any associated RRSIG too. This has to be done here, as both
-    // the RRset and its RRSIG have to be passed when constructing an
-    // RdataSet.
-    if ((ret == result::SUCCESS) && rrset->getRRsig()) {
-        impl_->add(rrset->getRRsig(), zone_name, *result.zone_data);
-    }
-
-    // Add any last RRset that was left
-    impl_->addRdataSet(zone_name, *result.zone_data,
-                       ConstRRsetPtr(), ConstRRsetPtr());
-
-    assert(!impl_->last_rrset_);
+    const ConstRRsetPtr sig_rrset =
+        rrset ? rrset->getRRsig() : ConstRRsetPtr();
+    impl_->add(rrset, sig_rrset, zone_name, *result.zone_data);
 
-    return (ret);
+    // add() doesn't allow duplicate add, so we always return SUCCESS.
+    return (result::SUCCESS);
 }
 
 namespace {
diff --git a/src/lib/datasrc/memory/memory_client.h b/src/lib/datasrc/memory/memory_client.h
index d2ec4c3..330d62e 100644
--- a/src/lib/datasrc/memory/memory_client.h
+++ b/src/lib/datasrc/memory/memory_client.h
@@ -20,11 +20,7 @@
 #include <datasrc/iterator.h>
 #include <datasrc/client.h>
 #include <datasrc/memory/zone_table.h>
-
-// for isc::datasrc::ZoneTable::FindResult returned by findZone(). This
-// variant of findZone() is not implemented and should be removed
-// eventually.
-#include <datasrc/zonetable.h>
+#include <datasrc/memory/zone_data.h>
 
 #include <string>
 
@@ -206,19 +202,22 @@ public:
         { }
     };
 
-    /// Returns a \c ZoneTable result that best matches the given name.
+    /// Returns a \c ZoneFinder result that best matches the given name.
     ///
     /// This derived version of the method never throws an exception.
     /// For other details see \c DataSourceClient::findZone().
-    virtual isc::datasrc::memory::ZoneTable::FindResult
-    findZone2(const isc::dns::Name& name) const;
-
-    // This variant of findZone() is not implemented and should be
-    // removed eventually. It currently throws an exception. It is
-    // required right now to derive from DataSourceClient.
     virtual isc::datasrc::DataSourceClient::FindResult
     findZone(const isc::dns::Name& name) const;
 
+    /// Returns a \c ZoneData in the result that best matches the given
+    /// name.
+    ///
+    /// This is mainly intended for use in unit tests and should not be
+    /// used in other code.
+    ///
+    /// \throws none
+    const ZoneData* findZoneData(const isc::dns::Name& name);
+
     /// \brief Implementation of the getIterator method
     virtual isc::datasrc::ZoneIteratorPtr
     getIterator(const isc::dns::Name& name, bool separate_rrs = false) const;
@@ -244,6 +243,10 @@ private:
     // directly any more (it should be handled through DataSourceClient)?
     class InMemoryClientImpl;
     InMemoryClientImpl* impl_;
+
+    // A helper internal class used by load().  It maintains some intermediate
+    // states while loading RRs of the zone.
+    class Loader;
 };
 
 } // namespace memory
diff --git a/src/lib/datasrc/memory/tests/.gitignore b/src/lib/datasrc/memory/tests/.gitignore
deleted file mode 100644
index d6d1ec8..0000000
--- a/src/lib/datasrc/memory/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/run_unittests
diff --git a/src/lib/datasrc/memory/tests/Makefile.am b/src/lib/datasrc/memory/tests/Makefile.am
deleted file mode 100644
index 74b1a3d..0000000
--- a/src/lib/datasrc/memory/tests/Makefile.am
+++ /dev/null
@@ -1,50 +0,0 @@
-SUBDIRS = testdata .
-
-AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
-AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
-AM_CPPFLAGS += $(BOOST_INCLUDES)
-AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
-
-AM_CXXFLAGS = $(B10_CXXFLAGS)
-
-if USE_STATIC_LINK
-AM_LDFLAGS = -static
-endif
-
-CLEANFILES = *.gcno *.gcda
-
-TESTS_ENVIRONMENT = \
-	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
-
-TESTS =
-if HAVE_GTEST
-TESTS += run_unittests
-
-run_unittests_SOURCES = run_unittests.cc
-run_unittests_SOURCES += rdata_serialization_unittest.cc
-run_unittests_SOURCES += rdataset_unittest.cc
-run_unittests_SOURCES += domaintree_unittest.cc
-run_unittests_SOURCES += treenode_rrset_unittest.cc
-run_unittests_SOURCES += zone_table_unittest.cc
-run_unittests_SOURCES += zone_data_unittest.cc
-run_unittests_SOURCES += zone_finder_unittest.cc
-run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
-run_unittests_SOURCES += memory_segment_test.h
-run_unittests_SOURCES += segment_object_holder_unittest.cc
-run_unittests_SOURCES += memory_client_unittest.cc
-
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
-run_unittests_LDADD = $(builddir)/../libdatasrc_memory.la
-run_unittests_LDADD += $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
-run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
-run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
-run_unittests_LDADD += $(GTEST_LDADD)
-endif
-
-noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/memory/tests/domaintree_unittest.cc b/src/lib/datasrc/memory/tests/domaintree_unittest.cc
deleted file mode 100644
index ef90050..0000000
--- a/src/lib/datasrc/memory/tests/domaintree_unittest.cc
+++ /dev/null
@@ -1,1230 +0,0 @@
-// Copyright (C) 2010  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 <gtest/gtest.h>
-
-#include <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/memory/domaintree.h>
-
-#include <dns/tests/unittest_util.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dns;
-using isc::UnitTestUtil;
-using namespace isc::datasrc::memory;
-
-// XXX: some compilers cannot find class static constants used in
-// EXPECT_xxx macros, for which we need an explicit empty definition.
-const size_t Name::MAX_LABELS;
-
-/* The initial structure of dtree
- *
- *             .
- *             |
- *             b
- *           /   \
- *          a    d.e.f
- *              /  |   \
- *             c   |    g.h
- *                 |     |
- *                w.y    i
- *              /  |  \   \
- *             x   |   z   k
- *                 |   |
- *                 p   j
- *               /   \
- *              o     q
- */
-
-namespace {
-
-void deleteData(int* i) {
-    delete i;
-}
-
-typedef DomainTree<int> TestDomainTree;
-typedef DomainTreeNode<int> TestDomainTreeNode;
-typedef DomainTreeNodeChain<int> TestDomainTreeNodeChain;
-
-class TreeHolder {
-public:
-    TreeHolder(util::MemorySegment& mem_sgmt, TestDomainTree* tree) :
-        mem_sgmt_(mem_sgmt), tree_(tree)
-    {}
-    ~TreeHolder() {
-        TestDomainTree::destroy(mem_sgmt_, tree_, deleteData);
-    }
-    TestDomainTree* get() { return (tree_); }
-private:
-    util::MemorySegment& mem_sgmt_;
-    TestDomainTree* tree_;
-};
-
-class DomainTreeTest : public::testing::Test {
-protected:
-    DomainTreeTest() :
-        dtree_holder_(mem_sgmt_, TestDomainTree::create(mem_sgmt_)),
-        dtree_expose_empty_node_holder_(mem_sgmt_,
-                                         TestDomainTree::create(mem_sgmt_, true)),
-        dtree(*dtree_holder_.get()),
-        dtree_expose_empty_node(*dtree_expose_empty_node_holder_.get()),
-        cdtnode(NULL)
-    {
-        const char* const domain_names[] = {
-            "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
-            "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
-        int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
-        for (int i = 0; i < name_count; ++i) {
-            dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
-            // Check the node doesn't have any data initially.
-            EXPECT_EQ(static_cast<int*>(NULL),
-                      dtnode->setData(new int(i + 1)));
-
-            dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
-                                            &dtnode);
-            EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
-        }
-    }
-
-    util::MemorySegmentLocal mem_sgmt_;
-    TreeHolder dtree_holder_;
-    TreeHolder dtree_expose_empty_node_holder_;
-    TestDomainTree& dtree;
-    TestDomainTree& dtree_expose_empty_node;
-    TestDomainTreeNode* dtnode;
-    const TestDomainTreeNode* cdtnode;
-    uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
-};
-
-TEST_F(DomainTreeTest, nodeCount) {
-    EXPECT_EQ(15, dtree.getNodeCount());
-
-    // Delete all nodes, then the count should be set to 0.  This also tests
-    // the behavior of deleteAllNodes().
-    dtree.deleteAllNodes(mem_sgmt_, deleteData);
-    EXPECT_EQ(0, dtree.getNodeCount());
-}
-
-TEST_F(DomainTreeTest, setGetData) {
-    // set new data to an existing node.  It should have some data.
-    int* newdata = new int(11);
-    int* olddata = dtnode->setData(newdata);
-    EXPECT_NE(static_cast<int*>(NULL), olddata);
-    deleteData(olddata);
-    EXPECT_EQ(11, *(dtnode->getData()));
-
-    // clear the node.  we should get the new data back we just passed.
-    olddata = dtnode->setData(NULL);
-    EXPECT_EQ(newdata, olddata);
-    deleteData(olddata);
-}
-
-TEST_F(DomainTreeTest, insertNames) {
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
-                                                        Name("d.e.f"),
-                                                        &dtnode));
-    EXPECT_EQ(Name("d.e.f"), dtnode->getName());
-    EXPECT_EQ(15, dtree.getNodeCount());
-
-    // insert not exist node
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("0"),
-                                                  &dtnode));
-    EXPECT_EQ(Name("0"), dtnode->getName());
-    EXPECT_EQ(16, dtree.getNodeCount());
-
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
-                                                  Name("example.com"),
-                                                  &dtnode));
-    EXPECT_EQ(17, dtree.getNodeCount());
-    // add data to it; also make sure it doesn't have data right now
-    // (otherwise it would leak)
-    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(12)));
-
-    // return ALREADYEXISTS, since node "example.com" already has
-    // been explicitly inserted
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
-                                                        Name("example.com"),
-                                                        &dtnode));
-    EXPECT_EQ(17, dtree.getNodeCount());
-
-    // split the node "d.e.f"
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k.e.f"),
-                                                  &dtnode));
-    EXPECT_EQ(Name("k"), dtnode->getName());
-    EXPECT_EQ(19, dtree.getNodeCount());
-
-    // split the node "g.h"
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("h"),
-                                                        &dtnode));
-    EXPECT_EQ(Name("h"), dtnode->getName());
-    EXPECT_EQ(20, dtree.getNodeCount());
-
-    // add child domain
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
-                                                  Name("m.p.w.y.d.e.f"),
-                                                  &dtnode));
-    EXPECT_EQ(Name("m"), dtnode->getName());
-    EXPECT_EQ(21, dtree.getNodeCount());
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
-                                                  Name("n.p.w.y.d.e.f"),
-                                                  &dtnode));
-    EXPECT_EQ(Name("n"), dtnode->getName());
-    EXPECT_EQ(22, dtree.getNodeCount());
-
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l.a"),
-                                                  &dtnode));
-    EXPECT_EQ(Name("l"), dtnode->getName());
-    EXPECT_EQ(23, dtree.getNodeCount());
-
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("r.d.e.f"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("s.d.e.f"),
-                                                  &dtnode));
-    EXPECT_EQ(25, dtree.getNodeCount());
-
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
-                                                  Name("h.w.y.d.e.f"),
-                                                  &dtnode));
-
-    // add more nodes one by one to cover leftRotate and rightRotate
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("f"),
-                                                        &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("m"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("nm"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("om"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("fe"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ge"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("i"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ae"),
-                                                  &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("n"),
-                                                  &dtnode));
-}
-
-TEST_F(DomainTreeTest, subTreeRoot) {
-    // This is a testcase for a particular issue that went unchecked in
-    // #2089's implementation, but was fixed in #2092. The issue was
-    // that when a node was fissioned, FLAG_SUBTREE_ROOT was not being
-    // copied correctly.
-
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("d.e.f"),
-                                              &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
-                                              &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
-                                              &dtnode));
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
-                                              &dtnode));
-    EXPECT_EQ(TestDomainTree::SUCCESS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("k.e.f"),
-                                              &dtnode));
-
-    // "g.h" is not a subtree root
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name("g.h"), &dtnode));
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
-    // fission the node "g.h"
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("h"),
-                                              &dtnode));
-
-    // the node "h" (h.down_ -> "g") should not be a subtree root. "g"
-    // should be a subtree root.
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
-    // "g.h" should be a subtree root now.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name("g.h"), &dtnode));
-    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-}
-
-TEST_F(DomainTreeTest, additionalNodeFission) {
-    // These are additional nodeFission tests added by #2054's rewrite
-    // of DomainTree::nodeFission(). These test specific corner cases that
-    // are not covered by other tests.
-
-    // Insert "t.0" (which becomes the left child of its parent)
-    EXPECT_EQ(TestDomainTree::SUCCESS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("t.0"),
-                                             &dtnode));
-
-    // "t.0" is not a subtree root
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name("t.0"), &dtnode));
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
-    // fission the node "t.0"
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
-              dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
-                                             &dtnode));
-
-    // the node "0" ("0".down_ -> "t") should not be a subtree root. "t"
-    // should be a subtree root.
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-
-    // "t.0" should be a subtree root now.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name("t.0"), &dtnode));
-    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
-}
-
-TEST_F(DomainTreeTest, findName) {
-    // find const dtnode
-    // exact match
-    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode));
-    EXPECT_EQ(Name("a"), cdtnode->getName());
-
-    // not found
-    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("d.e.f"), &cdtnode));
-    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("y.d.e.f"), &cdtnode));
-    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("x"), &cdtnode));
-    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("m.n"), &cdtnode));
-
-    // if we expose empty node, we can get the empty node created during insert
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name("d.e.f"), &cdtnode));
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name("w.y.d.e.f"), &cdtnode));
-
-    // partial match
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH, dtree.find(Name("m.b"), &cdtnode));
-    EXPECT_EQ(Name("b"), cdtnode->getName());
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode));
-
-    // find dtnode
-    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"),
-                                                   &dtnode));
-    EXPECT_EQ(Name("q"), dtnode->getName());
-}
-
-TEST_F(DomainTreeTest, findError) {
-    // For the version that takes a node chain, the chain must be empty.
-    TestDomainTreeNodeChain chain;
-    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode,
-                                                   chain));
-    // trying to reuse the same chain.  it should result in an exception.
-    EXPECT_THROW(dtree.find(Name("a"), &cdtnode, chain),
-                 BadValue);
-}
-
-TEST_F(DomainTreeTest, flags) {
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
-                                                  Name("flags.example"),
-                                                  &dtnode));
-
-    // by default, flags are all off
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // set operation, by default it enables the flag
-    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
-    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // try disable the flag explicitly
-    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // try enable the flag explicitly
-    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, true);
-    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // setting an unknown flag will trigger an exception
-    EXPECT_THROW(dtnode->setFlag(static_cast<TestDomainTreeNode::Flags>(2), true),
-                 isc::InvalidParameter);
-}
-
-bool
-testCallback(const TestDomainTreeNode&, bool* callback_checker) {
-    *callback_checker = true;
-    return (false);
-}
-
-template <typename T>
-void
-performCallbackTest(TestDomainTree& dtree,
-                    util::MemorySegmentLocal& mem_sgmt,
-                    const T& name_called,
-                    const T& name_not_called)
-{
-    TestDomainTreeNode* dtnode;
-    const TestDomainTreeNode* cdtnode;
-
-    // by default callback isn't enabled
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
-                                                  Name("callback.example"),
-                                                  &dtnode));
-    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // enable/re-disable callback
-    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
-    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
-    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // enable again for subsequent tests
-    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
-    // add more levels below and above the callback node for partial match.
-    TestDomainTreeNode* subdtnode;
-    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
-                                                  Name("sub.callback.example"),
-                                                  &subdtnode));
-    EXPECT_EQ(static_cast<int*>(NULL), subdtnode->setData(new int(2)));
-    TestDomainTreeNode* parentdtnode;
-    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt,
-                                                        Name("example"),
-                                                        &parentdtnode));
-    // the child/parent nodes shouldn't "inherit" the callback flag.
-    // "dtnode" may be invalid due to the insertion, so we need to re-find
-    // it.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"),
-                                                   &dtnode));
-    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-    EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-    EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
-
-    // check if the callback is called from find()
-    TestDomainTreeNodeChain node_path1;
-    bool callback_called = false;
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree.find(name_called, &cdtnode, node_path1,
-                          testCallback, &callback_called));
-    EXPECT_TRUE(callback_called);
-
-    // enable callback at the parent node, but it doesn't have data so
-    // the callback shouldn't be called.
-    TestDomainTreeNodeChain node_path2;
-    parentdtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
-    callback_called = false;
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree.find(name_not_called, &cdtnode, node_path2,
-                          testCallback, &callback_called));
-    EXPECT_FALSE(callback_called);
-}
-
-TEST_F(DomainTreeTest, callbackName) {
-    const Name n1("sub.callback.example");
-    const Name n2("callback.example");
-
-    performCallbackTest(dtree, mem_sgmt_, n1, n2);
-}
-
-TEST_F(DomainTreeTest, callbackLabelSequence) {
-    const Name n1("sub.callback.example");
-    const Name n2("callback.example");
-    const LabelSequence ls1(n1);
-    const LabelSequence ls2(n2);
-
-    performCallbackTest(dtree, mem_sgmt_, ls1, ls2);
-}
-
-TEST_F(DomainTreeTest, findInSubTree) {
-    // For the version that takes a node chain, the chain must be empty.
-    DomainTreeNodeChain<int> chain;
-    bool flag;
-
-    // Searching for a non-absolute (right-stripped) label sequence when
-    // chain is empty should throw.
-    const Name n0("w.y.d.e.f");
-    LabelSequence ls0(n0);
-    ls0.stripRight(1);
-    EXPECT_THROW(dtree_expose_empty_node.find(ls0, &cdtnode, chain,
-                                              testCallback, &flag),
-                 isc::BadValue);
-
-    // First, find a sub-tree node
-    chain.clear();
-    const LabelSequence ls1(n0);
-    DomainTree<int>::Result result =
-        dtree_expose_empty_node.find(ls1, &cdtnode, chain,
-                                     testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n0, chain.getAbsoluteName());
-
-    // Searching for an absolute label sequence when chain is already
-    // populated should throw.
-    const Name n2a("o");
-    const LabelSequence ls2a(n2a);
-    EXPECT_THROW(dtree_expose_empty_node.find(ls2a, &cdtnode, chain,
-                                              testCallback, &flag),
-                 isc::BadValue);
-
-    // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
-    // suffix to "o" (non-absolute).
-    const Name n2("o.w.y.d.e.f");
-    LabelSequence ls2(n2);
-    ls2.stripRight(6);
-    EXPECT_EQ("o", ls2.toText());
-
-    result = dtree_expose_empty_node.find(ls2, &cdtnode, chain,
-                                          testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n2, chain.getAbsoluteName());
-
-    // Another test. Start with "d.e.f." node.
-    chain.clear();
-    const Name n3("d.e.f");
-    const LabelSequence ls3(n3);
-    result =
-        dtree_expose_empty_node.find(ls3, &cdtnode, chain,
-                                     testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n3, chain.getAbsoluteName());
-
-    // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
-    // suffix to "o.w.y" (non-absolute).
-    const Name n4("o.w.y.d.e.f");
-    LabelSequence ls4(n2);
-    ls4.stripRight(4);
-    EXPECT_EQ("o.w.y", ls4.toText());
-
-    result = dtree_expose_empty_node.find(ls4, &cdtnode, chain,
-                                          testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n4, chain.getAbsoluteName());
-}
-
-TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) {
-    // For the version that takes a node chain, the chain must be empty.
-    DomainTreeNodeChain<int> chain;
-    bool flag;
-
-    const Name n1("c.g.h");
-
-    // First insert a "c.g.h." node.
-    dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode);
-
-    /* Now, the tree looks like:
-     *
-     *             .
-     *             |
-     *             b
-     *           /   \
-     *          a    d.e.f
-     *              /  |  \____
-     *             c   |       \
-     *                 |        g.h
-     *                 |         |
-     *                w.y        i
-     *              /  |  \     / \
-     *             x   |   z   c   k
-     *                 |   |
-     *                 p   j
-     *               /   \
-     *              o     q
-     */
-
-    // Make a non-absolute label sequence. We will search for this same
-    // sequence in two places in the tree.
-    LabelSequence ls1(n1);
-    ls1.stripRight(3);
-    EXPECT_EQ("c", ls1.toText());
-
-    // First, find "g.h."
-    const Name n2("g.h");
-    const LabelSequence ls2(n2);
-    DomainTree<int>::Result result =
-        dtree_expose_empty_node.find(ls2, &cdtnode, chain,
-                                     testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n2, chain.getAbsoluteName());
-
-    // Now, find "c.g.h." by searching just the non-absolute ls1 label
-    // sequence.
-    result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
-                                          testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n1, chain.getAbsoluteName());
-
-    // Now, find "." (the root node)
-    chain.clear();
-    const Name n3(".");
-    const LabelSequence ls3(n3);
-    result =
-        dtree_expose_empty_node.find(ls3, &cdtnode, chain,
-                                     testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(n3, chain.getAbsoluteName());
-
-    // Now, find "c." by searching just the non-absolute ls1 label
-    // sequence.
-    result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
-                                          testCallback, &flag);
-    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
-    EXPECT_EQ(Name("c."), chain.getAbsoluteName());
-}
-
-TEST_F(DomainTreeTest, chainLevel) {
-    TestDomainTreeNodeChain chain;
-
-    // by default there should be no level in the chain.
-    EXPECT_EQ(0, chain.getLevelCount());
-
-    // insert one node to the tree and find it.  there should be exactly
-    // one level in the chain.
-    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
-    TestDomainTree& tree(*tree_holder.get());
-    Name node_name(Name::ROOT_NAME());
-    EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
-                                                &dtnode));
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              tree.find(node_name, &cdtnode, chain));
-    EXPECT_EQ(1, chain.getLevelCount());
-
-    // Check the name of the found node (should have '.' as both non-absolute
-    // and absolute name
-    EXPECT_EQ(".", cdtnode->getLabels().toText());
-    EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
-
-    /*
-     * Now creating a possibly deepest tree with MAX_LABELS levels.
-     * it should look like:
-     *           (.)
-     *            |
-     *            a
-     *            |
-     *            a
-     *            : (MAX_LABELS - 1) "a"'s
-     *
-     * then confirm that find() for the deepest name succeeds without any
-     * disruption, and the resulting chain has the expected level.
-     * Note that the root name (".") solely belongs to a single level,
-     * so the levels begin with 2.
-     */
-    for (unsigned int i = 2; i <= Name::MAX_LABELS; ++i) {
-        node_name = Name("a.").concatenate(node_name);
-        EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
-                                                    &dtnode));
-        TestDomainTreeNodeChain found_chain;
-        EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  tree.find(node_name, &cdtnode, found_chain));
-        EXPECT_EQ(i, found_chain.getLevelCount());
-
-        // The non-absolute name should only have the first label
-        EXPECT_EQ("a", cdtnode->getLabels().toText());
-        // But the absolute name should have all labels
-        EXPECT_EQ(node_name.toText(),
-                  cdtnode->getAbsoluteLabels(buf).toText());
-    }
-
-    // Confirm the last inserted name has the possible maximum length with
-    // maximum label count.  This confirms the dtree and chain level cannot
-    // be larger.
-    EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
-    EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
-}
-
-TEST_F(DomainTreeTest, getAbsoluteNameError) {
-    // an empty chain isn't allowed.
-    TestDomainTreeNodeChain chain;
-    EXPECT_THROW(chain.getAbsoluteName(), BadValue);
-}
-
-/*
- * The domain order should be:
- * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
- * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
- *             . (no data, can't be found)
- *             |
- *             b
- *           /   \
- *          a    d.e.f
- *              /  |   \
- *             c   |    g.h
- *                 |     |
- *                w.y    i
- *              /  |  \   \
- *             x   |   z   k
- *                 |   |
- *                 p   j
- *               /   \
- *              o     q
- */
-const char* const names[] = {
-    "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
-    "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
-    "g.h", "i.g.h", "k.g.h"};
-const size_t name_count(sizeof(names) / sizeof(*names));
-
-const char* const upper_node_names[] = {
-    ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
-    "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
-    ".", "g.h", "g.h"};
-
-TEST_F(DomainTreeTest, getUpperNode) {
-    TestDomainTreeNodeChain node_path;
-    const TestDomainTreeNode* node = NULL;
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree_expose_empty_node.find(Name(names[0]),
-                                            &node,
-                                            node_path));
-    for (int i = 0; i < name_count; ++i) {
-        EXPECT_NE(static_cast<void*>(NULL), node);
-
-        const TestDomainTreeNode* upper_node = node->getUpperNode();
-        if (upper_node_names[i] != NULL) {
-            const TestDomainTreeNode* upper_node2 = NULL;
-            EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                      dtree_expose_empty_node.find(Name(upper_node_names[i]),
-                                                    &upper_node2));
-            EXPECT_NE(static_cast<void*>(NULL), upper_node2);
-            EXPECT_EQ(upper_node, upper_node2);
-        } else {
-            EXPECT_EQ(static_cast<void*>(NULL), upper_node);
-        }
-
-        node = dtree_expose_empty_node.nextNode(node_path);
-    }
-
-    // We should have reached the end of the tree.
-    EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-TEST_F(DomainTreeTest, nextNode) {
-    TestDomainTreeNodeChain node_path;
-    const TestDomainTreeNode* node = NULL;
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              dtree.find(Name(names[0]), &node, node_path));
-    for (int i = 0; i < name_count; ++i) {
-        EXPECT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
-        node = dtree.nextNode(node_path);
-    }
-
-    // We should have reached the end of the tree.
-    EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-// Just walk using previousNode until the beginning of the tree and check it is
-// OK
-//
-// dtree - the tree to walk
-// node - result of previous call to find(), starting position of the walk
-// node_path - the path from the previous call to find(), will be modified
-// chain_length - the number of names that should be in the chain to be walked
-//   (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
-//   this is always from the beginning of the names[] list).
-// skip_first - if this is false, the node should already contain the node with
-//   the first name of the chain. If it is true, the node should be NULL
-//   (true is for finds that return no match, false for the ones that return
-//   match)
-void
-previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
-             TestDomainTreeNodeChain& node_path, size_t chain_length,
-             bool skip_first)
-{
-    if (skip_first) {
-        // If the first is not found, this is supposed to be NULL and we skip
-        // it in our checks.
-        EXPECT_EQ(static_cast<void*>(NULL), node);
-        node = dtree.previousNode(node_path);
-    }
-    for (size_t i(chain_length); i > 0; --i) {
-        EXPECT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
-        // Find the node at the path and check the value is the same
-        // (that it really returns the correct corresponding node)
-        //
-        // The "empty" nodes can not be found
-        if (node->getData()) {
-            const TestDomainTreeNode* node2(NULL);
-            TestDomainTreeNodeChain node_path2;
-            EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                      dtree.find(Name(names[i - 1]), &node2, node_path2));
-            EXPECT_EQ(node, node2);
-        }
-        node = dtree.previousNode(node_path);
-    }
-
-    // We should have reached the start of the tree.
-    ASSERT_NE(static_cast<void*>(NULL), node);
-    EXPECT_EQ(".", node->getLabels().toText());
-
-    // With one more call it results in NULL
-    node = dtree.previousNode(node_path);
-    EXPECT_EQ(static_cast<void*>(NULL), node);
-
-    // Calling previousNode() yet again should still return NULL without
-    // fail.
-    node = dtree.previousNode(node_path);
-    EXPECT_EQ(static_cast<void*>(NULL), node);
-}
-
-// Check the previousNode
-TEST_F(DomainTreeTest, previousNode) {
-    // First, iterate the whole tree from the end to the beginning.
-    TestDomainTreeNodeChain node_path;
-    EXPECT_THROW(dtree.previousNode(node_path), isc::BadValue) <<
-        "Throw before a search was done on the path";
-    const TestDomainTreeNode* node(NULL);
-    {
-        SCOPED_TRACE("Iterate through");
-        EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  dtree.find(Name(names[name_count - 1]), &node, node_path));
-        previousWalk(dtree, node, node_path, name_count, false);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Iterate from the middle");
-        // Now, start somewhere in the middle, but within the real node.
-        EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  dtree.find(Name(names[4]), &node, node_path));
-        previousWalk(dtree, node, node_path, 5, false);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start at the first");
-        // If we start at the lowest (which is "a"), we get to the beginning
-        // right away.
-        EXPECT_EQ(TestDomainTree::EXACTMATCH,
-                  dtree.find(Name(names[0]), &node, node_path));
-        EXPECT_NE(static_cast<void*>(NULL), node);
-        node = dtree.previousNode(node_path);
-        ASSERT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(".", node->getLabels().toText());
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start before the first");
-        // If we start before the lowest (. < 0. < a.), we should not get a
-        // node.  Its previous node should be the root.
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  dtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
-        EXPECT_EQ(static_cast<void*>(NULL), node);
-        node = dtree.previousNode(node_path);
-        ASSERT_NE(static_cast<void*>(NULL), node);
-        EXPECT_EQ(".", node->getLabels().toText());
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start after the last");
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  dtree.find(Name("z"), &node, node_path));
-        previousWalk(dtree, node, node_path, name_count, true);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start below a leaf");
-        // We exit a leaf by going down. We should start by the one
-        // we exited - 'c' (actually, we should get it by the find, as partial
-        // match).
-        EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-                  dtree.find(Name("b.c"), &node, node_path));
-        previousWalk(dtree, node, node_path, 3, false);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start to the right of a leaf");
-        // When searching for this, we exit the 'x' node to the right side,
-        // so we should go x afterwards.
-
-        // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
-        // and not PARTIALMATCH.
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  dtree.find(Name("xy.d.e.f"), &node, node_path));
-        previousWalk(dtree, node, node_path, 5, true);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start to the left of a leaf");
-        // This is similar to the previous, but we exit the 'z' leaf to the
-        // left side, so should not visit z at all then.
-
-        // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
-        // and not PARTIALMATCH.
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  dtree.find(Name("yz.d.e.f"), &node, node_path));
-        previousWalk(dtree, node, node_path, 9, true);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start to the right of a parent");
-        // When searching for this, we exit the 'g.h' node to the right
-        // side, so we should go to g.h's children afterwards.
-
-        // 'g.h' is an empty node, so we get a NOTFOUND and not
-        // PARTIALMATCH.
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  dtree.find(Name("x.h"), &node, node_path));
-        // 'g.h' is the COMMONANCESTOR.
-        EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
-        EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
-                  node_path.getLastComparisonResult().getRelation());
-        // find() exits to the right of 'g.h'
-        EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
-        // We then descend into 'i.g.h' and walk all the nodes in the
-        // tree.
-        previousWalk(dtree, node, node_path, name_count, true);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Start inside a wrong node");
-        // The d.e.f is a single node, but we want only part of it. We
-        // should start iterating before it.
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  dtree.find(Name("e.f"), &node, node_path));
-        previousWalk(dtree, node, node_path, 3, true);
-        node = NULL;
-        node_path.clear();
-    }
-
-    {
-        SCOPED_TRACE("Lookup in empty tree");
-        // Just check it doesn't crash, etc.
-        TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
-        TestDomainTree& empty_tree(*tree_holder.get());
-        EXPECT_EQ(TestDomainTree::NOTFOUND,
-                  empty_tree.find(Name("x"), &node, node_path));
-        EXPECT_EQ(static_cast<void*>(NULL), node);
-        EXPECT_EQ(static_cast<void*>(NULL),
-                  empty_tree.previousNode(node_path));
-        node = NULL;
-        node_path.clear();
-    }
-}
-
-TEST_F(DomainTreeTest, nextNodeError) {
-    // Empty chain for nextNode() is invalid.
-    TestDomainTreeNodeChain chain;
-    EXPECT_THROW(dtree.nextNode(chain), BadValue);
-}
-
-// A helper function for getLastComparedNode() below.
-void
-comparisonChecks(const TestDomainTreeNodeChain& chain,
-                 int expected_order, int expected_common_labels,
-                 NameComparisonResult::NameRelation expected_reln)
-{
-    if (expected_order > 0) {
-        EXPECT_LT(0, chain.getLastComparisonResult().getOrder());
-    } else if (expected_order < 0) {
-        EXPECT_GT(0, chain.getLastComparisonResult().getOrder());
-    } else {
-        EXPECT_EQ(0, chain.getLastComparisonResult().getOrder());
-    }
-    EXPECT_EQ(expected_common_labels,
-              chain.getLastComparisonResult().getCommonLabels());
-    EXPECT_EQ(expected_reln,
-              chain.getLastComparisonResult().getRelation());
-}
-
-TEST_F(DomainTreeTest, getLastComparedNode) {
-    TestDomainTree& tree = dtree_expose_empty_node; // use the "empty OK" mode
-    TestDomainTreeNodeChain chain;
-
-    // initially there should be no 'last compared'.
-    EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
-
-    // A search for an empty tree should result in no 'last compared', too.
-    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
-    TestDomainTree& empty_tree(*tree_holder.get());
-    EXPECT_EQ(TestDomainTree::NOTFOUND,
-              empty_tree.find(Name("a"), &cdtnode, chain));
-    EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
-    chain.clear();
-
-    const TestDomainTreeNode* expected_node = NULL;
-
-    // Exact match case.  The returned node should be last compared.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              tree.find(Name("x.d.e.f"), &expected_node, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // 1 = # labels of "x" (note: excluding ".")
-    comparisonChecks(chain, 0, 1, NameComparisonResult::EQUAL);
-    chain.clear();
-
-    // Partial match, search stopped at the matching node, which should be
-    // the last compared node.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              tree.find(Name("k.g.h"), &expected_node));
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("x.k.g.h"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // k.g.h < x.k.g.h, 1 = # labels of "k"
-    comparisonChecks(chain, 1, 1, NameComparisonResult::SUBDOMAIN);
-    chain.clear();
-
-    // Partial match, search stopped in the subtree below the matching node
-    // after following a left branch.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              tree.find(Name("x.d.e.f"), &expected_node));
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("a.d.e.f"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // a < x, no common labels
-    comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
-    chain.clear();
-
-    // Partial match, search stopped in the subtree below the matching node
-    // after following a right branch.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              tree.find(Name("z.d.e.f"), &expected_node));
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("zz.d.e.f"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // zz > z, no common label
-    comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
-    chain.clear();
-
-    // Partial match, search stopped at a node for a super domain of the
-    // search name in the subtree below the matching node.
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              tree.find(Name("w.y.d.e.f"), &expected_node));
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("y.d.e.f"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // y < w.y, 1 = # labels of "y"
-    comparisonChecks(chain, -1, 1, NameComparisonResult::SUPERDOMAIN);
-    chain.clear();
-
-    // Partial match, search stopped at a node that share a common ancestor
-    // with the search name in the subtree below the matching node.
-    // (the expected node is the same as the previous case)
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("z.y.d.e.f"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // z.y > w.y, 1 = # labels of "y"
-    comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
-    chain.clear();
-
-    // Search stops in the highest level (under ".") after following a left
-    // branch. (find() still returns PARTIALMATCH due to the top level ".")
-    EXPECT_EQ(TestDomainTree::EXACTMATCH, tree.find(Name("c"), &expected_node));
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("bb"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // bb < c, no common label
-    comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
-    chain.clear();
-
-    // Search stops in the highest level (under ".") after following a right
-    // branch. (the expected node is the same as the previous case)
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              tree.find(Name("d"), &cdtnode, chain));
-    EXPECT_EQ(expected_node, chain.getLastComparedNode());
-    // d > c, no common label
-    comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
-    chain.clear();
-}
-
-TEST_F(DomainTreeTest, dumpTree) {
-    std::ostringstream str;
-    std::ostringstream str2;
-    dtree.dumpTree(str);
-    str2 << "tree has 15 node(s)\n"
-            ". (black) [invisible] [subtreeroot]\n"
-            "     begin down from .\n"
-            "     b (black) [subtreeroot]\n"
-            "          a (black)\n"
-            "               NULL\n"
-            "               NULL\n"
-            "          d.e.f (black) [invisible]\n"
-            "               begin down from d.e.f\n"
-            "               w.y (black) [invisible] [subtreeroot]\n"
-            "                    begin down from w.y\n"
-            "                    p (black) [subtreeroot]\n"
-            "                         o (red)\n"
-            "                              NULL\n"
-            "                              NULL\n"
-            "                         q (red)\n"
-            "                              NULL\n"
-            "                              NULL\n"
-            "                    end down from w.y\n"
-            "                    x (red)\n"
-            "                         NULL\n"
-            "                         NULL\n"
-            "                    z (red)\n"
-            "                         begin down from z\n"
-            "                         j (black) [subtreeroot]\n"
-            "                              NULL\n"
-            "                              NULL\n"
-            "                         end down from z\n"
-            "                         NULL\n"
-            "                         NULL\n"
-            "               end down from d.e.f\n"
-            "               c (red)\n"
-            "                    NULL\n"
-            "                    NULL\n"
-            "               g.h (red)\n"
-            "                    begin down from g.h\n"
-            "                    i (black) [subtreeroot]\n"
-            "                         NULL\n"
-            "                         k (red)\n"
-            "                              NULL\n"
-            "                              NULL\n"
-            "                    end down from g.h\n"
-            "                    NULL\n"
-            "                    NULL\n"
-            "     end down from .\n"
-            "     NULL\n"
-            "     NULL\n";
-    EXPECT_EQ(str2.str(), str.str());
-}
-
-TEST_F(DomainTreeTest, swap) {
-    // Store info about the first tree
-    std::ostringstream str1;
-    dtree.dumpTree(str1);
-    size_t count1(dtree.getNodeCount());
-
-    // Create second one and store state
-    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
-    TestDomainTree& tree2(*tree_holder.get());
-    TestDomainTreeNode* node;
-    tree2.insert(mem_sgmt_, Name("second"), &node);
-    std::ostringstream str2;
-    tree2.dumpTree(str2);
-
-    // Swap them
-    ASSERT_NO_THROW(tree2.swap(dtree));
-
-    // Check their sizes
-    ASSERT_EQ(1, dtree.getNodeCount());
-    ASSERT_EQ(count1, tree2.getNodeCount());
-
-    // And content
-    std::ostringstream out;
-    dtree.dumpTree(out);
-    ASSERT_EQ(str2.str(), out.str());
-    out.str("");
-    tree2.dumpTree(out);
-    ASSERT_EQ(str1.str(), out.str());
-}
-
-// Matching in the "root zone" may be special (e.g. there's no parent,
-// any domain names should be considered a subdomain of it), so it makes
-// sense to test cases with the root zone explicitly.
-TEST_F(DomainTreeTest, root) {
-    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
-    TestDomainTree& root(*tree_holder.get());
-    root.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
-    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
-
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              root.find(Name::ROOT_NAME(), &cdtnode));
-    EXPECT_EQ(dtnode, cdtnode);
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              root.find(Name("example.com"), &cdtnode));
-    EXPECT_EQ(dtnode, cdtnode);
-
-    // Insert a new name that better matches the query name.  find() should
-    // find the better one.
-    root.insert(mem_sgmt_, Name("com"), &dtnode);
-    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(2)));
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              root.find(Name("example.com"), &cdtnode));
-    EXPECT_EQ(dtnode, cdtnode);
-
-    // Perform the same tests for the tree that allows matching against empty
-    // nodes.
-    TreeHolder tree_holder_emptyok(mem_sgmt_,
-                                   TestDomainTree::create(mem_sgmt_, true));
-    TestDomainTree& root_emptyok(*tree_holder_emptyok.get());
-    root_emptyok.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
-    EXPECT_EQ(TestDomainTree::EXACTMATCH,
-              root_emptyok.find(Name::ROOT_NAME(), &cdtnode));
-    EXPECT_EQ(dtnode, cdtnode);
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              root_emptyok.find(Name("example.com"), &cdtnode));
-    EXPECT_EQ(dtnode, cdtnode);
-
-    root.insert(mem_sgmt_, Name("com"), &dtnode);
-    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
-              root.find(Name("example.com"), &cdtnode));
-    EXPECT_EQ(dtnode, cdtnode);
-}
-
-TEST_F(DomainTreeTest, getAbsoluteLabels) {
-    // The full absolute names of the nodes in the tree
-    // with the addition of the explicit root node
-    const char* const domain_names[] = {
-        "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
-        "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
-    // The names of the nodes themselves, as they end up in the tree
-    const char* const first_labels[] = {
-        "c", "b", "a", "x", "z", "g.h", "i", "o",
-        "j", "p", "q", "k"};
-
-    const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
-    for (int i = 0; i < name_count; ++i) {
-        EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
-                  &cdtnode));
-
-        // First make sure the names themselves are not absolute
-        const LabelSequence ls(cdtnode->getLabels());
-        EXPECT_EQ(first_labels[i], ls.toText());
-        EXPECT_FALSE(ls.isAbsolute());
-
-        // Now check the absolute names
-        const LabelSequence abs_ls(cdtnode->getAbsoluteLabels(buf));
-        EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
-        EXPECT_TRUE(abs_ls.isAbsolute());
-    }
-
-    // Explicitly add and find a root node, to see that getAbsoluteLabels
-    // also works when getLabels() already returns an absolute LabelSequence
-    dtree.insert(mem_sgmt_, Name("."), &dtnode);
-    dtnode->setData(new int(1));
-
-    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("."), &cdtnode));
-
-    EXPECT_TRUE(cdtnode->getLabels().isAbsolute());
-    EXPECT_EQ(".", cdtnode->getLabels().toText());
-    EXPECT_TRUE(cdtnode->getAbsoluteLabels(buf).isAbsolute());
-    EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
-}
-}
diff --git a/src/lib/datasrc/memory/tests/memory_client_unittest.cc b/src/lib/datasrc/memory/tests/memory_client_unittest.cc
deleted file mode 100644
index d5aa431..0000000
--- a/src/lib/datasrc/memory/tests/memory_client_unittest.cc
+++ /dev/null
@@ -1,740 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-#include <dns/masterload.h>
-#include <dns/nsec3hash.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrttl.h>
-#include <dns/masterload.h>
-
-#include <datasrc/result.h>
-#include <datasrc/data_source.h>
-#include <datasrc/memory/zone_data.h>
-#include <datasrc/memory/zone_table.h>
-#include <datasrc/memory/memory_client.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <new>                  // for bad_alloc
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::datasrc::memory;
-using namespace isc::testutils;
-
-namespace {
-// Memory segment specified for tests.  It normally behaves like a "local"
-// memory segment.  If "throw count" is set to non 0 via setThrowCount(),
-// it continues the normal behavior up to the specified number of calls to
-// allocate(), and throws an exception at the next call.
-class TestMemorySegment : public isc::util::MemorySegmentLocal {
-public:
-    TestMemorySegment() : throw_count_(0) {}
-    virtual void* allocate(size_t size) {
-        if (throw_count_ > 0) {
-            if (--throw_count_ == 0) {
-                throw std::bad_alloc();
-            }
-        }
-        return (isc::util::MemorySegmentLocal::allocate(size));
-    }
-    void setThrowCount(size_t count) { throw_count_ = count; }
-
-private:
-    size_t throw_count_;
-};
-
-const char* rrset_data[] = {
-    "example.org. 3600 IN SOA   ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600",
-    "a.example.org.		   	 3600 IN A	192.168.0.1",
-    "a.example.org.		   	 3600 IN MX	10 mail.example.org.",
-    NULL
-};
-
-class MockIterator : public ZoneIterator {
-private:
-    MockIterator() :
-        rrset_data_ptr_(rrset_data)
-    {
-    }
-
-    const char** rrset_data_ptr_;
-
-public:
-    virtual ConstRRsetPtr getNextRRset() {
-        if (*rrset_data_ptr_ == NULL) {
-             return (ConstRRsetPtr());
-        }
-
-        RRsetPtr result(textToRRset(*rrset_data_ptr_,
-                                    RRClass::IN(), Name("example.org")));
-        rrset_data_ptr_++;
-
-        return (result);
-    }
-
-    virtual ConstRRsetPtr getSOA() const {
-        isc_throw(isc::NotImplemented, "Not implemented");
-    }
-
-    static ZoneIteratorPtr makeIterator(void) {
-        return (ZoneIteratorPtr(new MockIterator()));
-    }
-};
-
-class MemoryClientTest : public ::testing::Test {
-protected:
-    MemoryClientTest() : zclass_(RRClass::IN()),
-                         client_(new InMemoryClient(mem_sgmt_, zclass_))
-    {}
-    ~MemoryClientTest() {
-        if (client_ != NULL) {
-            delete client_;
-        }
-    }
-    void TearDown() {
-        delete client_;
-        client_ = NULL;
-        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
-    }
-    const RRClass zclass_;
-    TestMemorySegment mem_sgmt_;
-    InMemoryClient* client_;
-};
-
-TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) {
-    // Attempting to load example.org to example.com zone should result
-    // in an exception.
-    EXPECT_THROW(client_->load(Name("example.com"),
-                               TEST_DATA_DIR "/example.org-empty.zone"),
-                 MasterLoadError);
-}
-
-TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak1) {
-    // Attempting to load broken example.org zone should result in an
-    // exception. This should not leak ZoneData and other such
-    // allocations.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR "/example.org-broken1.zone"),
-                 MasterLoadError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak2) {
-    // Attempting to load broken example.org zone should result in an
-    // exception. This should not leak ZoneData and other such
-    // allocations.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR "/example.org-broken2.zone"),
-                 MasterLoadError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNonExistentZoneFile) {
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR "/somerandomfilename"),
-                 MasterLoadError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
-    // When an empty zone file is loaded, the origin doesn't even have
-    // an SOA RR. This condition should be avoided, and hence load()
-    // should throw when an empty zone is loaded.
-
-    EXPECT_EQ(0, client_->getZoneCount());
-
-    EXPECT_THROW(client_->load(Name("."),
-                               TEST_DATA_DIR "/empty.zone"),
-                 InMemoryClient::EmptyZone);
-
-    EXPECT_EQ(0, client_->getZoneCount());
-
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, load) {
-    // This is a simple load check for a "full" and correct zone that
-    // should not result in any exceptions.
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org.zone");
-}
-
-TEST_F(MemoryClientTest, loadFromIterator) {
-    client_->load(Name("example.org"),
-                  *MockIterator::makeIterator());
-
-    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
-    // First we have the SOA
-    ConstRRsetPtr rrset(iterator->getNextRRset());
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-
-    // RRType::MX() RRset
-    rrset = iterator->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::MX(), rrset->getType());
-
-    // RRType::A() RRset
-    rrset = iterator->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::A(), rrset->getType());
-
-    // There's nothing else in this iterator
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-
-    // Iterating past the end should result in an exception
-    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
-}
-
-TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
-    // Just to check that things get cleaned up
-
-    for (int i = 1; i < 16; i++) {
-        mem_sgmt_.setThrowCount(i);
-        EXPECT_THROW(client_->load(Name("example.org"),
-                                   TEST_DATA_DIR "/example.org.zone"),
-                     std::bad_alloc);
-    }
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNSEC3Signed) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-nsec3-signed.zone");
-}
-
-TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
-}
-
-TEST_F(MemoryClientTest, loadReloadZone) {
-    // Because we reload the same zone, also check that the zone count
-    // doesn't increase.
-    EXPECT_EQ(0, client_->getZoneCount());
-
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-empty.zone");
-    EXPECT_EQ(1, client_->getZoneCount());
-
-    // Reload zone with same data
-
-    client_->load(Name("example.org"),
-                  client_->getFileName(Name("example.org")));
-    EXPECT_EQ(1, client_->getZoneCount());
-
-    isc::datasrc::memory::ZoneTable::FindResult
-        result(client_->findZone2(Name("example.org")));
-    EXPECT_EQ(result::SUCCESS, result.code);
-    EXPECT_NE(static_cast<ZoneData*>(NULL),
-              result.zone_data);
-
-    /* Check SOA */
-    const ZoneNode* node = result.zone_data->getOriginNode();
-    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
-    const RdataSet* set = node->getData();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::SOA(), set->type);
-
-    set = set->getNext();
-    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
-    /* Check ns1.example.org */
-    const ZoneTree& tree = result.zone_data->getZoneTree();
-    ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
-    EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
-
-    // Reload zone with different data
-
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-rrsigs.zone");
-    EXPECT_EQ(1, client_->getZoneCount());
-
-    isc::datasrc::memory::ZoneTable::FindResult
-        result2(client_->findZone2(Name("example.org")));
-    EXPECT_EQ(result::SUCCESS, result2.code);
-    EXPECT_NE(static_cast<ZoneData*>(NULL),
-              result2.zone_data);
-
-    /* Check SOA */
-    node = result2.zone_data->getOriginNode();
-    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
-    set = node->getData();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::SOA(), set->type);
-
-    set = set->getNext();
-    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
-    /* Check ns1.example.org */
-    const ZoneTree& tree2 = result2.zone_data->getZoneTree();
-    ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
-    EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
-    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
-    set = node->getData();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::AAAA(), set->type);
-
-    set = set->getNext();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::A(), set->type);
-
-    set = set->getNext();
-    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDuplicateType) {
-    // This should not result in any exceptions:
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-duplicate-type.zone");
-
-    // This should throw:
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-duplicate-type-bad.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
-    // Multiple CNAME RRs should throw.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-multiple-cname.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
-    // Multiple DNAME RRs should throw.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-multiple-dname.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
-    // Multiple NSEC3 RRs should throw.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-multiple-nsec3.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
-    // Multiple NSEC3PARAM RRs should throw.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-multiple-nsec3param.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadOutOfZoneThrows) {
-    // Out of zone names should throw.
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-out-of-zone.zone"),
-                 MasterLoadError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadWildcardNSThrows) {
-    // Wildcard NS names should throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-wildcard-ns.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
-    // Wildcard DNAME names should throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-wildcard-dname.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
-    // Wildcard NSEC3 names should throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-wildcard-nsec3.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
-    // NSEC3 names with labels != (origin_labels + 1) should throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-nsec3-fewer-labels.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
-    // NSEC3 names with labels != (origin_labels + 1) should throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-nsec3-more-labels.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
-    // CNAME and not NSEC should throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-cname-and-not-nsec-1.zone"),
-                 InMemoryClient::AddError);
-
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-cname-and-not-nsec-2.zone"),
-                 InMemoryClient::AddError);
-
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) {
-    // DNAME + NS (apex) is OK
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR
-                  "/example.org-dname-ns-apex-1.zone");
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) {
-    // DNAME + NS (apex) is OK (reverse order)
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR
-                  "/example.org-dname-ns-apex-2.zone");
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
-    // DNAME + NS (non-apex) must throw
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-dname-ns-nonapex-1.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
-    // DNAME + NS (non-apex) must throw (reverse order)
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-dname-ns-nonapex-2.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-rrsig-follows-nothing.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGNameUnmatched) {
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-rrsig-name-unmatched.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGTypeUnmatched) {
-    EXPECT_THROW(client_->load(Name("example.org"),
-                               TEST_DATA_DIR
-                               "/example.org-rrsig-type-unmatched.zone"),
-                 InMemoryClient::AddError);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, loadRRSIGs) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-rrsigs.zone");
-    EXPECT_EQ(1, client_->getZoneCount());
-}
-
-TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-rrsigs.zone");
-
-    RRsetPtr rrset(new RRset(Name("example.org"),
-                             RRClass::IN(), RRType::A(), RRTTL(3600)));
-    rrset->addRdata(in::A("192.0.2.1"));
-    rrset->addRdata(in::A("192.0.2.2"));
-
-    RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
-                             RRType::RRSIG(), RRTTL(300)));
-    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
-                                   "12345 example.org. FAKEFAKEFAKE"));
-    rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
-                                   "54321 example.org. FAKEFAKEFAKEFAKE"));
-    rrset->addRRsig(rrsig);
-
-    EXPECT_THROW(client_->add(Name("example.org"), rrset),
-                 InMemoryClient::AddError);
-
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, getZoneCount) {
-    EXPECT_EQ(0, client_->getZoneCount());
-    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-    EXPECT_EQ(1, client_->getZoneCount());
-}
-
-TEST_F(MemoryClientTest, getFileNameForNonExistentZone) {
-    // Zone "example.org." doesn't exist
-    EXPECT_TRUE(client_->getFileName(Name("example.org.")).empty());
-}
-
-TEST_F(MemoryClientTest, getFileName) {
-    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-    EXPECT_EQ(TEST_DATA_DIR "/example.org-empty.zone",
-              client_->getFileName(Name("example.org")));
-}
-
-TEST_F(MemoryClientTest, getIteratorForNonExistentZone) {
-    // Zone "." doesn't exist
-    EXPECT_THROW(client_->getIterator(Name(".")), DataSourceError);
-}
-
-TEST_F(MemoryClientTest, getIterator) {
-    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
-    // First we have the SOA
-    ConstRRsetPtr rrset_soa(iterator->getNextRRset());
-    EXPECT_TRUE(rrset_soa);
-    EXPECT_EQ(RRType::SOA(), rrset_soa->getType());
-
-    // There's nothing else in this iterator
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-
-    // Iterating past the end should result in an exception
-    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
-}
-
-TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-multiple.zone");
-
-    // separate_rrs = false
-    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
-    // First we have the SOA
-    ConstRRsetPtr rrset(iterator->getNextRRset());
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-
-    // Only one RRType::A() RRset
-    rrset = iterator->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::A(), rrset->getType());
-
-    // There's nothing else in this zone
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-
-
-    // separate_rrs = true
-    ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true));
-
-    // First we have the SOA
-    rrset = iterator2->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-
-    // First RRType::A() RRset
-    rrset = iterator2->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::A(), rrset->getType());
-
-    // Second RRType::A() RRset
-    rrset = iterator2->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::A(), rrset->getType());
-
-    // There's nothing else in this iterator
-    EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
-}
-
-TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
-    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
-    // This method is not implemented.
-    EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
-    // The zone "example.org" doesn't exist, so we can't add an RRset to
-    // it.
-    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
-                               RRTTL(300)));
-    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
-    EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
-}
-
-TEST_F(MemoryClientTest, addOutOfZoneThrows) {
-    // Out of zone names should throw.
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-empty.zone");
-
-    RRsetPtr rrset_a(new RRset(Name("a.example.com"),
-                               RRClass::IN(), RRType::A(), RRTTL(300)));
-    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
-
-    EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
-                 OutOfZone);
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, addNullRRsetThrows) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-rrsigs.zone");
-
-    EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
-                 InMemoryClient::NullRRset);
-
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-rrsigs.zone");
-
-    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
-                               RRTTL(300)));
-    EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
-                 InMemoryClient::AddError);
-
-    // Teardown checks for memory segment leaks
-}
-
-TEST_F(MemoryClientTest, add) {
-    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
-
-    // Add another RRset
-    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
-                               RRTTL(300)));
-    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
-    client_->add(Name("example.org"), rrset_a);
-
-    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
-
-    // First we have the SOA
-    ConstRRsetPtr rrset(iterator->getNextRRset());
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::A(), rrset->getType());
-
-    rrset = iterator->getNextRRset();
-    EXPECT_TRUE(rrset);
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-
-    // There's nothing else in this zone
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
-}
-
-TEST_F(MemoryClientTest, findZoneThrowsNotImplemented) {
-    // This method is not implemented.
-    EXPECT_THROW(client_->findZone(Name(".")),
-                 isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, findZone2) {
-    client_->load(Name("example.org"),
-                  TEST_DATA_DIR "/example.org-rrsigs.zone");
-
-    isc::datasrc::memory::ZoneTable::FindResult
-        result(client_->findZone2(Name("example.com")));
-    EXPECT_EQ(result::NOTFOUND, result.code);
-    EXPECT_EQ(static_cast<ZoneData*>(NULL),
-              result.zone_data);
-
-    isc::datasrc::memory::ZoneTable::FindResult
-        result2(client_->findZone2(Name("example.org")));
-    EXPECT_EQ(result::SUCCESS, result2.code);
-    EXPECT_NE(static_cast<ZoneData*>(NULL),
-              result2.zone_data);
-
-    /* Check SOA */
-    const ZoneNode* node = result2.zone_data->getOriginNode();
-    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
-    const RdataSet* set = node->getData();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::SOA(), set->type);
-
-    set = set->getNext();
-    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-
-    /* Check ns1.example.org */
-    const ZoneTree& tree = result2.zone_data->getZoneTree();
-    ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
-    EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
-    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
-
-    set = node->getData();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::AAAA(), set->type);
-
-    set = set->getNext();
-    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
-    EXPECT_EQ(RRType::A(), set->type);
-
-    set = set->getNext();
-    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
-}
-
-TEST_F(MemoryClientTest, getUpdaterThrowsNotImplemented) {
-    // This method is not implemented.
-    EXPECT_THROW(client_->getUpdater(Name("."), false, false),
-                 isc::NotImplemented);
-}
-
-TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
-    // This method is not implemented.
-    EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
-                 isc::NotImplemented);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/memory_segment_test.h b/src/lib/datasrc/memory/tests/memory_segment_test.h
deleted file mode 100644
index 3195a9b..0000000
--- a/src/lib/datasrc/memory/tests/memory_segment_test.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef DATASRC_MEMORY_SEGMENT_TEST_H
-#define DATASRC_MEMORY_SEGMENT_TEST_H 1
-
-#include <util/memory_segment_local.h>
-
-#include <cstddef>              // for size_t
-#include <new>                  // for bad_alloc
-
-namespace isc {
-namespace datasrc {
-namespace memory {
-namespace test {
-
-// A special memory segment that can be used for tests.  It normally behaves
-// like a "local" memory segment.  If "throw count" is set to non 0 via
-// setThrowCount(), it continues the normal behavior until the specified
-// number of calls to allocate(), exclusive, and throws an exception at the
-// next call.  For example, if count is set to 3, the next two calls to
-// allocate() will succeed, and the 3rd call will fail with an exception.
-// This segment object can be used after the exception is thrown, and the
-// count is internally reset to 0.
-class MemorySegmentTest : public isc::util::MemorySegmentLocal {
-public:
-    MemorySegmentTest() : throw_count_(0) {}
-    virtual void* allocate(std::size_t size) {
-        if (throw_count_ > 0) {
-            if (--throw_count_ == 0) {
-                throw std::bad_alloc();
-            }
-        }
-        return (isc::util::MemorySegmentLocal::allocate(size));
-    }
-    void setThrowCount(std::size_t count) { throw_count_ = count; }
-
-private:
-    std::size_t throw_count_;
-};
-
-} // namespace test
-} // namespace memory
-} // namespace datasrc
-} // namespace isc
-
-#endif // DATASRC_MEMORY_SEGMENT_TEST_H
-
-// Local Variables:
-// mode: c++
-// End:
diff --git a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc b/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
deleted file mode 100644
index 12b613a..0000000
--- a/src/lib/datasrc/memory/tests/rdata_serialization_unittest.cc
+++ /dev/null
@@ -1,839 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/messagerenderer.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-
-#include <datasrc/memory/rdata_serialization.h>
-
-#include <util/unittests/wiredata.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
-#include <cstring>
-#include <set>
-#include <string>
-#include <vector>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-
-using isc::util::unittests::matchWireData;
-using std::string;
-using std::vector;
-
-// A trick to steal some private definitions of the implementation we use here
-
-namespace isc {
-namespace datasrc{
-namespace memory {
-
-#include "../rdata_serialization_priv.cc"
-
-}
-}
-}
-
-namespace {
-// This defines a tuple of test data used in test_rdata_list below.
-struct TestRdata {
-    const char* const rrclass;  // RR class, textual form
-    const char* const rrtype;   // RR type, textual form
-    const char* const rdata;    // textual RDATA
-    const size_t n_varlen_fields; // expected # of variable-len fields
-};
-
-// This test data consist of (almost) all supported types of RDATA (+ some
-// unusual and corner cases).
-const TestRdata test_rdata_list[] = {
-    {"IN", "A", "192.0.2.1", 0},
-    {"IN", "NS", "ns.example.com", 0},
-    {"IN", "CNAME", "cname.example.com", 0},
-    {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
-    {"IN", "PTR", "reverse.example.com", 0},
-    {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
-    {"IN", "MINFO", "root.example.com mbox.example.com", 0},
-    {"IN", "MX", "10 mx.example.com", 0},
-    {"IN", "TXT", "\"test1\" \"test 2\"", 1},
-    {"IN", "RP", "root.example.com. rp-text.example.com", 0},
-    {"IN", "AFSDB", "1 afsdb.example.com", 0},
-    {"IN", "AAAA", "2001:db8::1", 0},
-    {"IN", "SRV", "1 0 10 target.example.com", 0},
-    {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
-    {"IN", "DNAME", "dname.example.com", 0},
-    {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
-    {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
-    // We handle RRSIG separately, so it's excluded from the list
-    {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
-    {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
-    {"IN", "DHCID", "FAKEFAKE", 1},
-    {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
-    {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
-    {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
-    {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
-    {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
-    {"IN", "TYPE65535", "\\# 0", 1},        // max RR type, 0-length RDATA
-    {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
-    {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
-    {"CH", "TXT", "BIND10", 1},        // ditto
-    {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
-    {NULL, NULL, NULL, 0}
-};
-
-// The following two functions will be used to generate wire format data
-// from encoded representation of each RDATA.
-void
-renderNameField(MessageRenderer* renderer, bool additional_required,
-                const LabelSequence& labels, RdataNameAttributes attributes)
-{
-    EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
-    renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
-}
-
-void
-renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
-    renderer->writeData(data, data_len);
-}
-
-class RdataSerializationTest : public ::testing::Test {
-protected:
-    RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
-                                                    "192.0.2.53")),
-                         aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
-                                                 "2001:db8::53")),
-                         rrsig_rdata_(createRdata(
-                                          RRType::RRSIG(), RRClass::IN(),
-                                          "A 5 2 3600 20120814220826 "
-                                          "20120715220826 12345 com. FAKE"))
-    {}
-
-    // A wraper for RdataEncoder::encode() with buffer overrun check.
-    void encodeWrapper(size_t data_len);
-
-    // Some commonly used RDATA
-    const ConstRdataPtr a_rdata_;
-    const ConstRdataPtr aaaa_rdata_;
-    const ConstRdataPtr rrsig_rdata_;
-
-    RdataEncoder encoder_;
-    vector<uint8_t> encoded_data_;
-    MessageRenderer expected_renderer_;
-    MessageRenderer actual_renderer_;
-    vector<ConstRdataPtr> rdata_list_;
-};
-
-// There are several ways to decode the data. For one, there are
-// more interfaces uses for RdataReader, and we use our own decoder,
-// to check the actual encoded data.
-//
-// These decoding ways are provided by the template parameter.
-template<class DecoderStyle>
-class RdataEncodeDecodeTest : public RdataSerializationTest {
-public:
-    // This helper test method encodes the given list of RDATAs
-    // (in rdata_list), and then iterates over the data, rendering the fields
-    // in the wire format.  It then compares the wire data with the one
-    // generated by the normal libdns++ interface to see the encoding/decoding
-    // works as intended.
-    void checkEncode(RRClass rrclass, RRType rrtype,
-                     const vector<ConstRdataPtr>& rdata_list,
-                     size_t expected_varlen_fields,
-                     const vector<ConstRdataPtr>& rrsig_list =
-                     vector<ConstRdataPtr>());
-
-    void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
-    void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
-};
-
-// Used across more classes and scopes. But it's just uninteresting
-// constant.
-const Name& dummyName2() {
-    static const Name result("example.com");
-    return (result);
-}
-
-bool
-additionalRequired(const RRType& type) {
-    // The set of RR types that require additional section processing.
-    // We'll use it to determine what value should the renderNameField get
-    // and, if the stored attributes are as expected.
-    static std::set<RRType> need_additionals;
-    if (need_additionals.empty()) {
-        need_additionals.insert(RRType::NS());
-        need_additionals.insert(RRType::MX());
-        need_additionals.insert(RRType::SRV());
-    }
-
-    return (need_additionals.find(type) != need_additionals.end());
-}
-
-// A decoder that does not use RdataReader. Not recommended for use,
-// but it allows the tests to check the internals of the data.
-class ManualDecoderStyle {
-public:
-    static void foreachRdataField(RRClass rrclass, RRType rrtype,
-                                  size_t rdata_count,
-                                  const vector<uint8_t>& encoded_data,
-                                  const vector<uint16_t>& varlen_list,
-                                  RdataReader::NameAction name_callback,
-                                  RdataReader::DataAction data_callback)
-    {
-        const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass,
-                                                                rrtype);
-
-        size_t off = 0;
-        size_t varlen_count = 0;
-        size_t name_count = 0;
-        for (size_t count = 0; count < rdata_count; ++count) {
-            for (size_t i = 0; i < encode_spec.field_count; ++i) {
-                const RdataFieldSpec& field_spec = encode_spec.fields[i];
-                switch (field_spec.type) {
-                    case RdataFieldSpec::FIXEDLEN_DATA:
-                        if (data_callback) {
-                            data_callback(&encoded_data.at(off),
-                                          field_spec.fixeddata_len);
-                        }
-                        off += field_spec.fixeddata_len;
-                        break;
-                    case RdataFieldSpec::VARLEN_DATA:
-                        {
-                            const size_t varlen = varlen_list.at(varlen_count);
-                            if (data_callback && varlen > 0) {
-                                data_callback(&encoded_data.at(off), varlen);
-                            }
-                            off += varlen;
-                            ++varlen_count;
-                            break;
-                        }
-                    case RdataFieldSpec::DOMAIN_NAME:
-                        {
-                            ++name_count;
-                            const LabelSequence labels(&encoded_data.at(off));
-                            if (name_callback) {
-                                name_callback(labels,
-                                              field_spec.name_attributes);
-                            }
-                            off += labels.getSerializedLength();
-                            break;
-                        }
-                }
-            }
-        }
-        assert(name_count == encode_spec.name_count * rdata_count);
-        assert(varlen_count == encode_spec.varlen_count * rdata_count);
-    }
-
-    static void foreachRRSig(const vector<uint8_t>& encoded_data,
-                             const vector<uint16_t>& rrsiglen_list,
-                             RdataReader::DataAction data_callback)
-    {
-        size_t rrsig_totallen = 0;
-        for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
-             it != rrsiglen_list.end();
-             ++it) {
-            rrsig_totallen += *it;
-        }
-        assert(encoded_data.size() >= rrsig_totallen);
-
-        const uint8_t* dp = &encoded_data[encoded_data.size() -
-            rrsig_totallen];
-        for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
-            data_callback(dp, rrsiglen_list[i]);
-            dp += rrsiglen_list[i];
-        }
-    }
-
-    static void decode(const isc::dns::RRClass& rrclass,
-                       const isc::dns::RRType& rrtype,
-                       size_t rdata_count,
-                       size_t rrsig_count,
-                       size_t expected_varlen_fields,
-                       // Warning: this test actualy might change the
-                       // encoded_data !
-                       vector<uint8_t>& encoded_data, size_t,
-                       MessageRenderer& renderer)
-    {
-        // If this type of RDATA is expected to contain variable-length fields,
-        // we brute force the encoded data, exploiting our knowledge of actual
-        // encoding, then adjust the encoded data excluding the list of length
-        // fields.  This is ugly, but for tests only.
-        vector<uint16_t> varlen_list;
-        if (expected_varlen_fields > 0) {
-            const size_t varlen_list_size =
-                rdata_count * expected_varlen_fields * sizeof(uint16_t);
-            ASSERT_LE(varlen_list_size, encoded_data.size());
-            varlen_list.resize(rdata_count * expected_varlen_fields);
-            std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
-            encoded_data.assign(encoded_data.begin() + varlen_list_size,
-                                encoded_data.end());
-        }
-
-        // If RRSIGs are given, we need to extract the list of the RRSIG
-        // lengths and adjust encoded_data_ further.
-        vector<uint16_t> rrsiglen_list;
-        if (rrsig_count > 0) {
-            const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
-            ASSERT_LE(rrsig_len_size, encoded_data.size());
-            rrsiglen_list.resize(rrsig_count * rrsig_len_size);
-            std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
-            encoded_data.assign(encoded_data.begin() + rrsig_len_size,
-                                encoded_data.end());
-        }
-
-        // Create wire-format data from the encoded data
-        foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
-                          varlen_list,
-                          boost::bind(renderNameField, &renderer,
-                                      additionalRequired(rrtype), _1, _2),
-                          boost::bind(renderDataField, &renderer, _1, _2));
-
-        // 2nd dummy name
-        renderer.writeName(dummyName2());
-        // Finally, dump any RRSIGs in wire format.
-        foreachRRSig(encoded_data, rrsiglen_list,
-                     boost::bind(renderDataField, &renderer, _1, _2));
-    }
-};
-
-// Check using callbacks and calling next until the end.
-class CallbackDecoder {
-public:
-    static void decode(const isc::dns::RRClass& rrclass,
-                       const isc::dns::RRType& rrtype,
-                       size_t rdata_count, size_t sig_count, size_t,
-                       const vector<uint8_t>& encoded_data, size_t,
-                       MessageRenderer& renderer)
-    {
-        RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
-                           sig_count,
-                           boost::bind(renderNameField, &renderer,
-                                       additionalRequired(rrtype), _1, _2),
-                           boost::bind(renderDataField, &renderer, _1, _2));
-        while (reader.next() != RdataReader::RRSET_BOUNDARY) {}
-        renderer.writeName(dummyName2());
-        while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
-    }
-};
-
-// Check using callbacks and calling iterate.
-class IterateDecoder {
-public:
-    static void decode(const isc::dns::RRClass& rrclass,
-                       const isc::dns::RRType& rrtype,
-                       size_t rdata_count, size_t sig_count, size_t,
-                       const vector<uint8_t>& encoded_data, size_t,
-                       MessageRenderer& renderer)
-    {
-        RdataReader reader(rrclass, rrtype, &encoded_data[0],
-                           rdata_count, sig_count,
-                           boost::bind(renderNameField, &renderer,
-                                       additionalRequired(rrtype), _1, _2),
-                           boost::bind(renderDataField, &renderer, _1, _2));
-        reader.iterate();
-        renderer.writeName(dummyName2());
-        reader.iterateAllSigs();
-    }
-};
-
-namespace {
-
-// Render the data to renderer, if one is set, or put it inside
-// a data buffer.
-void
-appendOrRenderData(vector<uint8_t>* where, MessageRenderer** renderer,
-                   const void* data, size_t size)
-{
-    if (*renderer != NULL) {
-        (*renderer)->writeData(data, size);
-    } else {
-        where->insert(where->end(), reinterpret_cast<const uint8_t*>(data),
-                      reinterpret_cast<const uint8_t*>(data) + size);
-    }
-}
-
-}
-
-// Similar to IterateDecoder, but it first iterates a little and rewinds
-// before actual rendering.
-class RewindAndDecode {
-private:
-    static void writeName(MessageRenderer** renderer,
-                          const LabelSequence& labels,
-                          RdataNameAttributes attributes)
-    {
-        (*renderer)->writeName(labels,
-                               (attributes & NAMEATTR_COMPRESSIBLE) != 0);
-    }
-public:
-    static void decode(const isc::dns::RRClass& rrclass,
-                       const isc::dns::RRType& rrtype,
-                       size_t rdata_count, size_t sig_count, size_t,
-                       const vector<uint8_t>& encoded_data, size_t,
-                       MessageRenderer& renderer)
-    {
-        MessageRenderer dump; // A place to dump the extra data from before
-                              // actual rendering.
-        MessageRenderer* current = &dump;
-        vector<uint8_t> placeholder; // boost::bind does not like NULL
-        RdataReader reader(rrclass, rrtype, &encoded_data[0],
-                           rdata_count, sig_count,
-                           boost::bind(writeName, &current, _1, _2),
-                           boost::bind(appendOrRenderData, &placeholder,
-                                       &current, _1, _2));
-        // Iterate a little and rewind
-        reader.next();
-        reader.nextSig();
-        reader.rewind();
-        // Do the actual rendering
-        current = &renderer;
-        reader.iterate();
-        renderer.writeName(dummyName2());
-        reader.iterateAllSigs();
-    }
-};
-
-// Decode using the iteration over one rdata each time.
-// We also count there's the correct count of Rdatas.
-class SingleIterateDecoder {
-public:
-    static void decode(const isc::dns::RRClass& rrclass,
-                       const isc::dns::RRType& rrtype,
-                       size_t rdata_count, size_t sig_count, size_t,
-                       const vector<uint8_t>& encoded_data, size_t,
-                       MessageRenderer& renderer)
-    {
-        RdataReader reader(rrclass, rrtype, &encoded_data[0],
-                           rdata_count, sig_count,
-                           boost::bind(renderNameField, &renderer,
-                                       additionalRequired(rrtype), _1, _2),
-                           boost::bind(renderDataField, &renderer, _1, _2));
-        size_t actual_count = 0;
-        while (reader.iterateRdata()) {
-            ++actual_count;
-        }
-        EXPECT_EQ(rdata_count, actual_count);
-        actual_count = 0;
-        renderer.writeName(dummyName2());
-        while (reader.iterateSingleSig()) {
-            ++actual_count;
-        }
-        EXPECT_EQ(sig_count, actual_count);
-    }
-};
-
-// This one does not adhere to the usual way the reader is used, trying
-// to confuse it. It iterates part of the data manually and then reads
-// the rest through iterate. It also reads the signatures in the middle
-// of rendering.
-template<bool start_data, bool start_sig>
-class HybridDecoder {
-public:
-    static void decode(const isc::dns::RRClass& rrclass,
-                       const isc::dns::RRType& rrtype,
-                       size_t rdata_count, size_t sig_count, size_t,
-                       const vector<uint8_t>& encoded_data,
-                       size_t encoded_data_len,
-                       MessageRenderer& renderer)
-    {
-        vector<uint8_t> data;
-        MessageRenderer* current;
-        RdataReader reader(rrclass, rrtype, &encoded_data[0],
-                           rdata_count, sig_count,
-                           boost::bind(renderNameField, &renderer,
-                                       additionalRequired(rrtype), _1, _2),
-                           boost::bind(appendOrRenderData, &data, &current, _1,
-                                       _2));
-        // The size matches
-        EXPECT_EQ(encoded_data_len, reader.getSize());
-        if (start_sig) {
-            current = NULL;
-            reader.nextSig();
-        }
-        // Render first part of data. If there's none, return empty Result and
-        // do nothing.
-        if (start_data) {
-            current = &renderer;
-            reader.next();
-        }
-        // Now, we let all sigs to be copied to data. We disable the
-        // renderer for this.
-        current = NULL;
-        reader.iterateAllSigs();
-        // Now return the renderer and render the rest of the data
-        current = &renderer;
-        reader.iterate();
-        // Now, this should not break anything and should be valid, but should
-        // return ends.
-        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
-        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
-        // Render the name and the sigs
-        renderer.writeName(dummyName2());
-        renderer.writeData(&data[0], data.size());
-        // The size matches even after use
-        EXPECT_EQ(encoded_data_len, reader.getSize());
-    }
-};
-
-typedef ::testing::Types<ManualDecoderStyle,
-                         CallbackDecoder, IterateDecoder, SingleIterateDecoder,
-                         HybridDecoder<true, true>, HybridDecoder<true, false>,
-                         HybridDecoder<false, true>,
-                         HybridDecoder<false, false> >
-    DecoderStyles;
-// Each decoder style must contain a decode() method. Such method is expected
-// to decode the passed data, first render the Rdata into the passed renderer,
-// then write the dummyName2() there and write the RRSig data after that.
-// It may do other checks too.
-//
-// There are some slight differences to how to do the decoding, that's why we
-// have the typed test.
-TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
-
-void
-RdataSerializationTest::encodeWrapper(size_t data_len) {
-    // make sure the data buffer is large enough for the canary
-    encoded_data_.resize(data_len + 2);
-    // set the canary data
-    encoded_data_.at(data_len) = 0xde;
-    encoded_data_.at(data_len + 1) = 0xad;
-    // encode, then check the canary is intact
-    encoder_.encode(&encoded_data_[0], data_len);
-    EXPECT_EQ(0xde, encoded_data_.at(data_len));
-    EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
-    // shrink the data buffer to the originally expected size (some tests
-    // expect that).  the actual encoded data should be intact.
-    encoded_data_.resize(data_len);
-}
-
-template<class DecoderStyle>
-void
-RdataEncodeDecodeTest<DecoderStyle>::
-checkEncode(RRClass rrclass, RRType rrtype,
-            const vector<ConstRdataPtr>& rdata_list,
-            size_t expected_varlen_fields,
-            const vector<ConstRdataPtr>& rrsig_list)
-{
-    // These two names will be rendered before and after the test RDATA,
-    // to check in case the RDATA contain a domain name whether it's
-    // compressed or not correctly.  The names in the RDATA should basically
-    // a subdomain of example.com, so it can be compressed due to dummyName2().
-    // Likewise, dummyName2() should be able to be fully compressed due to
-    // the name in the RDATA.
-    const Name dummy_name("com");
-
-    expected_renderer_.clear();
-    actual_renderer_.clear();
-    encoded_data_.clear();
-
-    // Build expected wire-format data
-    expected_renderer_.writeName(dummy_name);
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
-        rdata->toWire(expected_renderer_);
-    }
-    expected_renderer_.writeName(dummyName2());
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
-        rdata->toWire(expected_renderer_);
-    }
-
-    // Then build wire format data using the encoded data.
-    // 1st dummy name
-    actual_renderer_.writeName(dummy_name);
-
-    // Create encoded data
-    encoder_.start(rrclass, rrtype);
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
-        encoder_.addRdata(*rdata);
-    }
-    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
-        encoder_.addSIGRdata(*rdata);
-    }
-    const size_t storage_len = encoder_.getStorageLength();
-    encodeWrapper(storage_len);
-
-    DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
-                         expected_varlen_fields, encoded_data_, storage_len,
-                         actual_renderer_);
-
-    // Two sets of wire-format data should be identical.
-    matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
-                  actual_renderer_.getData(), actual_renderer_.getLength());
-}
-
-template<class DecoderStyle>
-void
-RdataEncodeDecodeTest<DecoderStyle>::
-addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
-    // Basic check on the encoded data for (most of) all supported RR types,
-    // in a comprehensive manner.
-    for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
-        SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
-                     test_rdata_list[i].rrtype);
-        const RRClass rrclass(test_rdata_list[i].rrclass);
-        const RRType rrtype(test_rdata_list[i].rrtype);
-        const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
-                                                test_rdata_list[i].rdata);
-        rdata_list_.clear();
-        rdata_list_.push_back(rdata);
-        checkEncode(rrclass, rrtype, rdata_list_,
-                    test_rdata_list[i].n_varlen_fields, rrsigs);
-    }
-}
-
-TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
-    vector<ConstRdataPtr> rrsigs;
-    this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
-
-    // Test with RRSIGs (covered type doesn't always match, but the encoder
-    // doesn't check that)
-    rrsigs.push_back(this->rrsig_rdata_);
-    this->addRdataCommon(rrsigs);
-}
-
-template<class DecoderStyle>
-void
-RdataEncodeDecodeTest<DecoderStyle>::
-addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
-    // Similar to addRdata(), but test with multiple RDATAs.
-    // Four different cases are tested: a single fixed-len RDATA (A),
-    // fixed-len data + domain name (MX), variable-len data only (TXT),
-    // variable-len data + domain name (NAPTR).
-    ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
-                                         "192.0.2.54");
-    rdata_list_.clear();
-    rdata_list_.push_back(a_rdata_);
-    rdata_list_.push_back(a_rdata2);
-    checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
-
-    ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
-                                          "5 mx1.example.com");
-    ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
-                                          "10 mx2.example.com");
-    rdata_list_.clear();
-    rdata_list_.push_back(mx_rdata1);
-    rdata_list_.push_back(mx_rdata2);
-    checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
-
-    ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
-                                           "foo bar baz");
-    ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
-                                          "another text data");
-    rdata_list_.clear();
-    rdata_list_.push_back(txt_rdata1);
-    rdata_list_.push_back(txt_rdata2);
-    checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
-
-    ConstRdataPtr naptr_rdata1 =
-        createRdata(RRType::NAPTR(), RRClass::IN(),
-                    "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
-    ConstRdataPtr naptr_rdata2 =
-        createRdata(RRType::NAPTR(), RRClass::IN(),
-                    "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
-    rdata_list_.clear();
-    rdata_list_.push_back(naptr_rdata1);
-    rdata_list_.push_back(naptr_rdata2);
-    checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
-}
-
-void ignoreName(const LabelSequence&, unsigned) {
-}
-
-void
-checkLargeData(const in::DHCID* decoded, bool* called, const void* encoded,
-               size_t length)
-{
-    EXPECT_FALSE(*called); // Called exactly once
-    *called = true;
-
-    // Reconstruct the Rdata and check it.
-    isc::util::InputBuffer ib(encoded, length);
-    const in::DHCID reconstructed(ib, ib.getLength());
-    EXPECT_EQ(0, reconstructed.compare(*decoded));
-}
-
-TEST_F(RdataSerializationTest, encodeLargeRdata) {
-    // There should be no reason for a large RDATA to fail in encoding,
-    // but we check such a case explicitly.
-
-    encoded_data_.resize(65535); // max unsigned 16-bit int
-    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
-    const in::DHCID large_dhcid(buffer, encoded_data_.size());
-
-    encoder_.start(RRClass::IN(), RRType::DHCID());
-    encoder_.addRdata(large_dhcid);
-    encodeWrapper(encoder_.getStorageLength());
-
-    // The encoded data should be identical to the original one.
-    bool called = false;
-    RdataReader reader(RRClass::IN(), RRType::DHCID(), &encoded_data_[0], 1, 0,
-                       ignoreName, boost::bind(checkLargeData, &large_dhcid,
-                                               &called, _1, _2));
-    reader.iterate();
-    EXPECT_TRUE(called);
-    called = false;
-    reader.iterateAllSigs();
-    EXPECT_FALSE(called);
-}
-
-TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
-    vector<ConstRdataPtr> rrsigs;
-    this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
-
-    // Tests with two RRSIGs
-    rrsigs.push_back(this->rrsig_rdata_);
-    rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
-                                 "A 5 2 3600 20120814220826 "
-                                 "20120715220826 54321 com. FAKE"));
-    this->addRdataMultiCommon(rrsigs);
-}
-
-TEST_F(RdataSerializationTest, badAddRdata) {
-    // Some operations must follow start().
-    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
-    EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
-    // will allocate space of some arbitrary size (256 bytes)
-    EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
-
-    // Bad buffer for encode
-    encoder_.start(RRClass::IN(), RRType::A());
-    encoder_.addRdata(*a_rdata_);
-    const size_t buf_len = encoder_.getStorageLength();
-    // NULL buffer for encode
-    EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
-    // buffer length is too short (we don't use the wrraper because we don't
-    // like to tweak the length arg to encode()).
-    encoded_data_.resize(buf_len - 1);
-    EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
-                 isc::BadValue);
-
-    // Type of RDATA and the specified RR type don't match.  addRdata() should
-    // detect this inconsistency.
-    encoder_.start(RRClass::IN(), RRType::AAAA());
-    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
-
-    // Likewise.
-    encoder_.start(RRClass::IN(), RRType::A());
-    EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
-
-    // Likewise.  The encoder expects the first name completes the data, and
-    // throws on the second due as an unexpected name field.
-    const ConstRdataPtr rp_rdata =
-        createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
-    encoder_.start(RRClass::IN(), RRType::NS());
-    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
-    // Likewise.  The encoder considers the name data a variable length data
-    // field, and throws on the first name.
-    encoder_.start(RRClass::IN(), RRType::DHCID());
-    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
-
-    // Likewise.  The text RDATA (2 bytes) will be treated as MX preference,
-    // and the encoder will still expect to see a domain name.
-    const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
-                                                "a");
-    encoder_.start(RRClass::IN(), RRType::MX());
-    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
-    // Similar to the previous one, but in this case there's no data field
-    // in the spec.
-    encoder_.start(RRClass::IN(), RRType::NS());
-    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
-
-    // Likewise.  Inconsistent name compression policy.
-    const ConstRdataPtr ns_rdata =
-        createRdata(RRType::NS(), RRClass::IN(), "ns.example");
-    encoder_.start(RRClass::IN(), RRType::DNAME());
-    EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
-
-    // Same as the previous one, opposite inconsistency.
-    const ConstRdataPtr dname_rdata =
-        createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
-    encoder_.start(RRClass::IN(), RRType::NS());
-    EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
-
-    // RDATA len exceeds the 16-bit range.  Technically not invalid, but
-    // we don't support that (and it's practically useless anyway).
-    encoded_data_.resize(65536); // use encoded_data_ for placeholder
-    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
-    encoder_.start(RRClass::IN(), RRType::DHCID());
-    EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
-                                   RdataEncodingError);
-
-    // RRSIG cannot be used as the main RDATA type (can only be added as
-    // a signature for some other type of RDATAs).
-    EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
-                 isc::BadValue);
-}
-
-void
-checkSigData(const ConstRdataPtr& decoded, bool* called, const void* encoded,
-             size_t length)
-{
-    EXPECT_FALSE(*called); // Called exactly once
-    *called = true;
-
-    // Reconstruct the RRSig and check it.
-    isc::util::InputBuffer ib(encoded, length);
-    const generic::RRSIG reconstructed(ib, ib.getLength());
-    EXPECT_EQ(0, reconstructed.compare(*decoded));
-}
-
-TEST_F(RdataSerializationTest, addSIGRdataOnly) {
-    // Encoded data that only contain RRSIGs.  Mostly useless, but can happen
-    // (in a partially broken zone) and it's accepted.
-    encoder_.start(RRClass::IN(), RRType::A());
-    encoder_.addSIGRdata(*rrsig_rdata_);
-    encodeWrapper(encoder_.getStorageLength());
-    ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
-
-    bool called = false;
-    RdataReader reader(RRClass::IN(), RRType::A(), &encoded_data_[0], 0, 1,
-                       ignoreName, boost::bind(checkSigData, rrsig_rdata_,
-                                               &called, _1, _2));
-    reader.iterate();
-    EXPECT_FALSE(called);
-    reader.iterateAllSigs();
-    EXPECT_TRUE(called);
-}
-
-TEST_F(RdataSerializationTest, badAddSIGRdata) {
-    // try adding SIG before start
-    EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
-
-    // Very big RRSIG.  This implementation rejects it.
-    isc::util::OutputBuffer ob(0);
-    rrsig_rdata_->toWire(ob);
-    // append dummy trailing signature to make it too big
-    vector<uint8_t> dummy_sig(65536 - ob.getLength());
-    ob.writeData(&dummy_sig[0], dummy_sig.size());
-    ASSERT_EQ(65536, ob.getLength());
-
-    isc::util::InputBuffer ib(ob.getData(), ob.getLength());
-    const generic::RRSIG big_sigrdata(ib, ob.getLength());
-    encoder_.start(RRClass::IN(), RRType::A());
-    EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/rdataset_unittest.cc b/src/lib/datasrc/memory/tests/rdataset_unittest.cc
deleted file mode 100644
index 897e53c..0000000
--- a/src/lib/datasrc/memory/tests/rdataset_unittest.cc
+++ /dev/null
@@ -1,298 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/buffer.h>
-#include <util/memory_segment_local.h>
-
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrset.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/memory/rdataset.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/lexical_cast.hpp>
-
-#include <string>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::testutils;
-using boost::lexical_cast;
-
-namespace {
-
-class RdataSetTest : public ::testing::Test {
-protected:
-    RdataSetTest() :
-        // 1076895760 = 0x40302010.  Use this so we fill in all 8-bit "field"
-        // of the 32-bit TTL
-        a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
-        rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
-                                 "A 5 2 3600 20120814220826 20120715220826 "
-                                 "1234 example.com. FAKE"))
-    {}
-    void TearDown() {
-        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
-    }
-
-    ConstRRsetPtr a_rrset_, rrsig_rrset_;
-    isc::util::MemorySegmentLocal mem_sgmt_;
-    RdataEncoder encoder_;
-};
-
-// Convert the given 32-bit integer (network byte order) to the corresponding
-// RRTTL object.
-RRTTL
-restoreTTL(const void* ttl_data) {
-    isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
-    return (RRTTL(b));
-}
-
-// A helper callback for checkRdataSet.  This confirms the given data
-// is the expected in::A RDATA (the value is taken from the RdataSetTest
-// constructor).
-void
-checkData(const void* data, size_t size) {
-    isc::util::InputBuffer b(data, size);
-    EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
-}
-
-// This is a set of checks for an RdataSet created with some simple
-// conditions.  with_rrset/with_rrsig is true iff the RdataSet is supposed to
-// contain normal/RRSIG RDATA.
-void
-checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
-    EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
-    EXPECT_EQ(RRType::A(), rdataset.type);
-    // See the RdataSetTest constructor for the magic number.
-    EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
-    EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
-    EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
-
-    // A simple test for the data content.  Details tests for the encoder/
-    // reader should be basically sufficient for various cases of the data,
-    // and the fact that this test doesn't detect memory leak should be
-    // reasonably sufficient that the implementation handles the data region
-    // correctly.  Here we check one simple case for a simple form of RDATA,
-    // mainly for checking the behavior of getDataBuf().
-    RdataReader reader(RRClass::IN(), RRType::A(),
-                       reinterpret_cast<const uint8_t*>(
-                           rdataset.getDataBuf()),
-                       rdataset.getRdataCount(), rdataset.getSigRdataCount(),
-                       &RdataReader::emptyNameAction, checkData);
-    reader.iterate();
-}
-
-TEST_F(RdataSetTest, create) {
-    // A simple case of creating an RdataSet.  Confirming the resulting
-    // fields have the expected values, and then destroying it (TearDown()
-    // would detect any memory leak)
-    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                          ConstRRsetPtr());
-    checkRdataSet(*rdataset, true, false);
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-TEST_F(RdataSetTest, getNext) {
-    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                          ConstRRsetPtr());
-
-    // By default, the next pointer should be NULL (already tested in other
-    // test cases), which should be the case with getNext().  We test both
-    // mutable and immutable versions of getNext().
-    EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
-    EXPECT_EQ(static_cast<const RdataSet*>(NULL),
-              static_cast<const RdataSet*>(rdataset)->getNext());
-
-    // making a link (it would form an infinite loop, but it doesn't matter
-    // in this test), and check the pointer returned by getNext().
-    rdataset->next = rdataset;
-    EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
-
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-// A helper function to create an RRset containing the given number of
-// unique RDATAs.
-ConstRRsetPtr
-getRRsetWithRdataCount(size_t rdata_count) {
-    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
-                             RRTTL(3600)));
-    for (size_t i = 0; i < rdata_count; ++i) {
-        rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
-                                           lexical_cast<std::string>(i)));
-    }
-    return (rrset);
-}
-
-TEST_F(RdataSetTest, createManyRRs) {
-    // RRset with possible maximum number of RDATAs
-    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
-                                          getRRsetWithRdataCount(8191),
-                                          ConstRRsetPtr());
-    EXPECT_EQ(8191, rdataset->getRdataCount());
-    EXPECT_EQ(0, rdataset->getSigRdataCount());
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
-    // Exceeding that will result in an exception.
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
-                                  getRRsetWithRdataCount(8192),
-                                  ConstRRsetPtr()),
-                 RdataSetError);
-    // To be very sure even try larger number than the threshold
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
-                                  getRRsetWithRdataCount(65535),
-                                  ConstRRsetPtr()),
-                 RdataSetError);
-}
-
-TEST_F(RdataSetTest, createWithRRSIG) {
-    // Normal case.
-    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                          rrsig_rrset_);
-    checkRdataSet(*rdataset, true, true);
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
-    // Unusual case: TTL doesn't match.  This implementation accepts that,
-    // using the TTL of the covered RRset.
-    ConstRRsetPtr rrsig_badttl(textToRRset(
-                                   "www.example.com. 3600 IN RRSIG "
-                                   "A 5 2 3600 20120814220826 "
-                                   "20120715220826 1234 example.com. FAKE"));
-    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
-    checkRdataSet(*rdataset, true, true);
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-// A helper function to create an RRSIG RRset containing the given number of
-// unique RDATAs.
-ConstRRsetPtr
-getRRSIGWithRdataCount(size_t sig_count) {
-    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
-                             RRType::RRSIG(), RRTTL(3600)));
-    // We use a base wire-format image and tweak the original TTL field to
-    // generate unique RDATAs in the loop.  (Creating them from corresponding
-    // text is simpler, but doing so for a large number of RRSIGs is
-    // relatively heavy and could be too long for unittests).
-    ConstRdataPtr rrsig_base =
-        rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
-                           "A 5 2 3600 20120814220826 20120715220826 1234 "
-                           "example.com. FAKE");
-    isc::util::OutputBuffer ob(0);
-    rrsig_base->toWire(ob);
-    for (size_t i = 0; i < sig_count; ++i) {
-        ob.writeUint16At((i >> 16) & 0xffff, 4);
-        ob.writeUint16At(i & 0xffff, 6);
-        isc::util::InputBuffer ib(ob.getData(), ob.getLength());
-        rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
-                                           ib, ib.getLength()));
-    }
-    return (rrset);
-}
-
-TEST_F(RdataSetTest, createManyRRSIGs) {
-    // 7 has a special meaning in the implementation: if the number of the
-    // RRSIGs reaches this value, an extra 'sig count' field will be created.
-    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                          getRRSIGWithRdataCount(7));
-    EXPECT_EQ(7, rdataset->getSigRdataCount());
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
-    // 8 would cause overflow in the normal 3-bit field if there were no extra
-    // count field.
-    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                getRRSIGWithRdataCount(8));
-    EXPECT_EQ(8, rdataset->getSigRdataCount());
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
-    // Up to 2^16-1 RRSIGs are allowed (although that would be useless
-    // in practice)
-    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                getRRSIGWithRdataCount(65535));
-    EXPECT_EQ(65535, rdataset->getSigRdataCount());
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-
-    // Exceeding this limit will result in an exception.
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                  getRRSIGWithRdataCount(65536)),
-                 RdataSetError);
-    // To be very sure even try larger number than the threshold
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                  getRRSIGWithRdataCount(70000)),
-                 RdataSetError);
-}
-
-TEST_F(RdataSetTest, createWithRRSIGOnly) {
-    // A rare, but allowed, case: RdataSet without the main RRset but with
-    // RRSIG.
-    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
-                                          rrsig_rrset_);
-    checkRdataSet(*rdataset, false, true);
-    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
-}
-
-TEST_F(RdataSetTest, badCeate) {
-    // Neither the RRset nor RRSIG RRset is given
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
-                                  ConstRRsetPtr()), isc::BadValue);
-
-    // Empty RRset (An RRset without RDATA)
-    ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
-                                        RRType::A(), RRTTL(3600)));
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
-                                  ConstRRsetPtr()), isc::BadValue);
-    ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
-                                        RRType::RRSIG(), RRTTL(3600)));
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
-                                  empty_rrsig), isc::BadValue);
-
-    // The RRset type and RRSIG's type covered don't match
-    ConstRRsetPtr bad_rrsig(textToRRset(
-                                "www.example.com. 1076895760 IN RRSIG "
-                                "NS 5 2 3600 20120814220826 20120715220826 "
-                                "1234 example.com. FAKE"));
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
-                 isc::BadValue);
-
-    // Pass non RRSIG for the sig parameter
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
-                 isc::BadValue);
-
-    // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
-                                  rrsig_rrset_),
-                 isc::BadValue);
-
-    // RR class doesn't match between RRset and RRSIG
-    ConstRRsetPtr badclass_rrsig(textToRRset(
-                                     "www.example.com. 1076895760 CH RRSIG "
-                                     "A 5 2 3600 20120814220826 "
-                                     "20120715220826 1234 example.com. FAKE",
-                                     RRClass::CH()));
-    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                  badclass_rrsig),
-                 isc::BadValue);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/run_unittests.cc b/src/lib/datasrc/memory/tests/run_unittests.cc
deleted file mode 100644
index 6321976..0000000
--- a/src/lib/datasrc/memory/tests/run_unittests.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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 <gtest/gtest.h>
-#include <util/unittests/run_all.h>
-#include <log/logger_support.h>
-
-int
-main(int argc, char* argv[]) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    isc::log::initLogger();
-
-    return (isc::util::unittests::run_all());
-}
diff --git a/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc b/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
deleted file mode 100644
index d27e364..0000000
--- a/src/lib/datasrc/memory/tests/segment_object_holder_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/memory_segment_local.h>
-
-#include <datasrc/memory/segment_object_holder.h>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util;
-using namespace isc::datasrc::memory;
-using namespace isc::datasrc::memory::detail;
-
-namespace {
-const int TEST_ARG_VAL = 42;    // arbitrary chosen magic number
-
-class TestObject {
-public:
-    static void destroy(MemorySegment& sgmt, TestObject* obj, int arg) {
-        sgmt.deallocate(obj, sizeof(*obj));
-        EXPECT_EQ(TEST_ARG_VAL, arg);
-    }
-};
-
-void
-useHolder(MemorySegment& sgmt, TestObject* obj, bool release) {
-    // Create a holder object, check the return value of get(), and,
-    // if requested, release the held object.  At the end of function
-    // the holder is destructed, and if the object hasn't been released by
-    // then, it should be deallocated.  Passed argument is checked in its
-    // deallocate().
-
-    typedef SegmentObjectHolder<TestObject, int> HolderType;
-    HolderType holder(sgmt, obj, TEST_ARG_VAL);
-    EXPECT_EQ(obj, holder.get());
-    if (release) {
-        EXPECT_EQ(obj, holder.release());
-    }
-}
-
-TEST(SegmentObjectHolderTest, foo) {
-    MemorySegmentLocal sgmt;
-    void* p = sgmt.allocate(sizeof(TestObject));
-    TestObject* obj = new(p) TestObject;
-
-    // Use holder, and release the content.  The memory shouldn't be
-    // deallocated.
-    useHolder(sgmt, obj, true);
-    EXPECT_FALSE(sgmt.allMemoryDeallocated());
-
-    // Use holder, and let it deallocate the object.  The memory segment
-    // should now be empty.
-    useHolder(sgmt, obj, false);
-    EXPECT_TRUE(sgmt.allMemoryDeallocated());
-}
-}
diff --git a/src/lib/datasrc/memory/tests/testdata/Makefile.am b/src/lib/datasrc/memory/tests/testdata/Makefile.am
deleted file mode 100644
index 9fb9986..0000000
--- a/src/lib/datasrc/memory/tests/testdata/Makefile.am
+++ /dev/null
@@ -1,32 +0,0 @@
-CLEANFILES = *.copied
-
-EXTRA_DIST =  empty.zone
-EXTRA_DIST += example.org.zone
-EXTRA_DIST += example.org-empty.zone
-
-EXTRA_DIST += example.org-broken1.zone
-EXTRA_DIST += example.org-broken2.zone
-EXTRA_DIST += example.org-cname-and-not-nsec-1.zone
-EXTRA_DIST += example.org-cname-and-not-nsec-2.zone
-EXTRA_DIST += example.org-dname-ns-apex-1.zone
-EXTRA_DIST += example.org-dname-ns-apex-2.zone
-EXTRA_DIST += example.org-dname-ns-nonapex-1.zone
-EXTRA_DIST += example.org-dname-ns-nonapex-2.zone
-EXTRA_DIST += example.org-duplicate-type-bad.zone
-EXTRA_DIST += example.org-duplicate-type.zone
-EXTRA_DIST += example.org-multiple-cname.zone
-EXTRA_DIST += example.org-multiple-dname.zone
-EXTRA_DIST += example.org-multiple-nsec3.zone
-EXTRA_DIST += example.org-multiple-nsec3param.zone
-EXTRA_DIST += example.org-multiple.zone
-EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
-EXTRA_DIST += example.org-nsec3-signed-no-param.zone
-EXTRA_DIST += example.org-nsec3-signed.zone
-EXTRA_DIST += example.org-out-of-zone.zone
-EXTRA_DIST += example.org-rrsig-follows-nothing.zone
-EXTRA_DIST += example.org-rrsig-name-unmatched.zone
-EXTRA_DIST += example.org-rrsig-type-unmatched.zone
-EXTRA_DIST += example.org-rrsigs.zone
-EXTRA_DIST += example.org-wildcard-dname.zone
-EXTRA_DIST += example.org-wildcard-ns.zone
-EXTRA_DIST += example.org-wildcard-nsec3.zone
diff --git a/src/lib/datasrc/memory/tests/testdata/empty.zone b/src/lib/datasrc/memory/tests/testdata/empty.zone
deleted file mode 100644
index e69de29..0000000
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
deleted file mode 100644
index 317095d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-broken1.zone
+++ /dev/null
@@ -1 +0,0 @@
-This is a broken zone that should not parse.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
deleted file mode 100644
index 2f9fd98..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-broken2.zone
+++ /dev/null
@@ -1,5 +0,0 @@
-;; broken example.org zone, where some RRs are OK, but others aren't
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 73 3600 300 3600000 3600
-ns1.example.org.		      3600 IN A		192.0.2.1
-ns2.example.org.		      3600 IN A		192.0.2.2
-ns2.a.example.com.		      3600 IN AAAA
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
deleted file mode 100644
index 5533663..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-1.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; CNAME + other is an error
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200
-a.example.org.				      7200  IN A	192.168.0.1
-a.example.org.				      3600  IN CNAME	foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
deleted file mode 100644
index 966aeeb..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-cname-and-not-nsec-2.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; CNAME + other is an error
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200
-a.example.org.				      3600  IN CNAME	foo.example.com.
-a.example.org.				      7200  IN A	192.168.0.1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
deleted file mode 100644
index f57c25d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-1.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (apex)
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
-example.org.				      3600  IN DNAME	foo.example.com.
-example.org.				      3600  IN NS	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
deleted file mode 100644
index bb3f191..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-apex-2.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (apex)
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200
-example.org.				      3600  IN NS	bar.example.com.
-example.org.				      3600  IN DNAME	foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
deleted file mode 100644
index 68a9805..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-1.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (non-apex)
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200
-ns1.example.org.			      3600  IN DNAME	foo.example.com.
-ns1.example.org.			      3600  IN NS	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone b/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
deleted file mode 100644
index 1b671dd..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-dname-ns-nonapex-2.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; DNAME + NS (non-apex)
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
-ns1.example.org.			      3600  IN NS	bar.example.com.
-ns1.example.org.			      3600  IN DNAME	foo.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
deleted file mode 100644
index 06c7dff..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type-bad.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 77 3600 300 3600000 3600
-ns1.example.org.		      3600 IN A	 	192.168.0.1
-ns1.example.org.		      3600 IN AAAA 	::1
-ns1.example.org.		      3600 IN A	 	192.168.0.2
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone b/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
deleted file mode 100644
index 6e5c1b8..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-duplicate-type.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 76 3600 300 3600000 3600
-ns1.example.org.		      3600 IN A	 	192.168.0.1
-ns1.example.org.		      3600 IN A	 	192.168.0.2
-ns1.example.org.		      3600 IN AAAA 	::1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone b/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
deleted file mode 100644
index f11b9b8..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-empty.zone
+++ /dev/null
@@ -1,2 +0,0 @@
-;; empty example.org zone
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
deleted file mode 100644
index 0a0c983..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-cname.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
-ns1.example.org.		      3600 IN CNAME	foo.example.com.
-ns1.example.org.		      3600 IN CNAME	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
deleted file mode 100644
index 3d581d0..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-dname.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
-ns1.example.org.		      3600 IN DNAME	foo.example.com.
-ns1.example.org.		      3600 IN DNAME	bar.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
deleted file mode 100644
index 874023a..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012090702 7200 3600 2592000 1200
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
deleted file mode 100644
index 5e69518..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple-nsec3param.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012090700 7200 3600 2592000 1200
-example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
-example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone b/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
deleted file mode 100644
index f473ae6..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-multiple.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; Multiple RDATA for testing separate RRs iterator
-example.org.  		   	 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
-a.example.org.		   	 3600 IN A	192.168.0.1
-a.example.org.		   	 3600 IN A	192.168.0.2
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
deleted file mode 100644
index 0221269..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-fewer-labels.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-;; NSEC3 names with labels != (origin_labels + 1)
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200
-example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
deleted file mode 100644
index efebcfb..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-more-labels.zone
+++ /dev/null
@@ -1,3 +0,0 @@
-;; NSEC3 names with labels != (origin_labels + 1)
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200
-a.b.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
deleted file mode 100644
index 5caa308..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed-no-param.zone
+++ /dev/null
@@ -1,15 +0,0 @@
-;; This file intentionally removes NSEC3PARAM from example.org.nsec3-signed
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
-example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
-example.org.				      86400 IN NS	ns.example.org.
-example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
-example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
-example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
-;; example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
-;; example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
-ns.example.org.				      86400 IN A	192.0.2.1
-ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone b/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
deleted file mode 100644
index 9c1195f..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-nsec3-signed.zone
+++ /dev/null
@@ -1,14 +0,0 @@
-example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
-example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
-example.org.				      86400 IN NS	ns.example.org.
-example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
-example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
-example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
-example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
-example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
-ns.example.org.				      86400 IN A	192.0.2.1
-ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
-09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
-RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone b/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
deleted file mode 100644
index e3afb74..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-out-of-zone.zone
+++ /dev/null
@@ -1,5 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 75 3600 300 3600000 3600
-a.example.com.	  		      3600 IN A	 	192.168.0.1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
deleted file mode 100644
index ef5f887..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-follows-nothing.zone
+++ /dev/null
@@ -1,5 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
-ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
deleted file mode 100644
index dc1d728..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-name-unmatched.zone
+++ /dev/null
@@ -1,6 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 70 3600 300 3600000 3600
-ns1.example.org.		      3600 IN A		192.0.2.1
-ns2.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
deleted file mode 100644
index 300e124..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsig-type-unmatched.zone
+++ /dev/null
@@ -1,6 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 72 3600 300 3600000 3600
-ns1.example.org.		      3600 IN AAAA	2001:db8::1
-ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone b/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
deleted file mode 100644
index 1c780b1..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-rrsigs.zone
+++ /dev/null
@@ -1,8 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 74 3600 300 3600000 3600
-ns1.example.org.		      3600 IN A	 	192.168.0.1
-ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
-ns1.example.org.		      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
-ns1.example.org.		      3600 IN AAAA 	::1
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
deleted file mode 100644
index 0d03b0d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-dname.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; test zone file with wildcard DNAME names
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
-*.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
deleted file mode 100644
index 2933515..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-ns.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; test zone file with wildcard NS names
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
-*.example.org.			      3600 IN NS	ns1.example.org.
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone b/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
deleted file mode 100644
index feee116..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org-wildcard-nsec3.zone
+++ /dev/null
@@ -1,4 +0,0 @@
-;; test zone file with wildcard NS names
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
-*.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/memory/tests/testdata/example.org.zone b/src/lib/datasrc/memory/tests/testdata/example.org.zone
deleted file mode 100644
index e499e0d..0000000
--- a/src/lib/datasrc/memory/tests/testdata/example.org.zone
+++ /dev/null
@@ -1,81 +0,0 @@
-;; test zone file used for ZoneFinderContext tests.
-;; RRSIGs are (obviouslly) faked ones for testing.
-
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 67 3600 300 3600000 3600
-example.org.			      3600 IN NS	ns1.example.org.
-example.org.			      3600 IN NS	ns2.example.org.
-example.org.			      3600 IN MX	1 mx1.example.org.
-example.org.			      3600 IN MX	2 mx2.example.org.
-example.org.			      3600 IN MX	3 mx.a.example.org.
-
-ns1.example.org.		      3600 IN A		192.0.2.1
-ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
-ns1.example.org.		      3600 IN AAAA	2001:db8::1
-ns1.example.org.		      3600 IN RRSIG	AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
-ns2.example.org.		      3600 IN A		192.0.2.2
-ns2.example.org.		      3600 IN TXT	"text data"
-
-mx1.example.org.		      3600 IN A		192.0.2.10
-mx2.example.org.		      3600 IN AAAA	2001:db8::10
-
-;; delegation
-a.example.org.			      3600 IN NS	ns1.a.example.org.
-a.example.org.			      3600 IN NS	ns2.a.example.org.
-a.example.org.			      3600 IN NS	ns.example.com.
-
-ns1.a.example.org.		      3600 IN A		192.0.2.5
-ns2.a.example.org.		      3600 IN A		192.0.2.6
-ns2.a.example.org.		      3600 IN AAAA	2001:db8::6
-mx.a.example.org.		      3600 IN A		192.0.2.7
-
-;; delegation, one of its NS names is at zone cut.
-b.example.org.			      3600 IN NS	ns.b.example.org.
-b.example.org.			      3600 IN NS	b.example.org.
-b.example.org.			      3600 IN AAAA	2001:db8::8
-
-ns.b.example.org.		      3600 IN A		192.0.2.9
-
-;; The MX name is at a zone cut.  shouldn't be included in the
-;; additional section.
-mxatcut.example.org.		      3600 IN MX	1 b.example.org.
-
-;; delegation, one of its NS names is under a DNAME delegation point;
-;; another is at that point; and yet another is under DNAME below a
-;; zone cut.
-c.example.org. 	      	      3600 IN NS	ns.dname.example.org.
-c.example.org. 	      	      3600 IN NS	dname.example.org.
-c.example.org.      	      3600 IN NS	ns.deepdname.example.org.
-ns.dname.example.org.		      3600 IN A		192.0.2.11
-dname.example.org.		      3600 IN A		192.0.2.12
-ns.deepdname.example.org.	      3600 IN AAAA	2001:db8::9
-
-;; delegation, one of its NS name is at an empty non terminal.
-d.example.org. 	      	      3600 IN NS	ns.empty.example.org.
-d.example.org. 	      	      3600 IN NS	ns1.example.org.
-;; by adding these two we can create an empty RB node for
-;; ns.empty.example.org in the in-memory zone
-foo.ns.empty.example.org.     3600 IN A		192.0.2.13
-bar.ns.empty.example.org.     3600 IN A		192.0.2.14
-
-;; delegation; the NS name matches a wildcard (and there's no exact
-;; match).  One of the NS names matches an empty wildcard node, for
-;; which no additional record should be provided (or any other
-;; disruption should happen).
-e.example.org. 	      	      3600 IN NS	ns.wild.example.org.
-e.example.org. 	      	      3600 IN NS	ns.emptywild.example.org.
-e.example.org. 	      	      3600 IN NS	ns2.example.org.
-*.wild.example.org.	      3600 IN A		192.0.2.15
-a.*.emptywild.example.org.    3600 IN AAAA	2001:db8::2
-
-;; additional for an answer RRset (MX) as a result of wildcard
-;; expansion
-*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
-
-;; CNAME
-alias.example.org. 3600 IN CNAME cname.example.org.
-
-;; DNAME
-dname.example.org. 3600 IN DNAME dname.example.com.
-
-;; DNAME under a NS (strange one)
-deepdname.c.example.org. 3600 IN DNAME deepdname.example.com.
diff --git a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc b/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
deleted file mode 100644
index 182cca6..0000000
--- a/src/lib/datasrc/memory/tests/treenode_rrset_unittest.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/buffer.h>
-#include <util/memory_segment_local.h>
-
-#include <datasrc/memory/treenode_rrset.h>
-#include <datasrc/memory/rdataset.h>
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/memory/zone_data.h>
-
-#include <util/unittests/wiredata.h>
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <string>
-#include <vector>
-
-using std::vector;
-using std::string;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::testutils;
-using isc::util::unittests::matchWireData;
-using isc::util::OutputBuffer;
-
-namespace {
-
-class TreeNodeRRsetTest : public ::testing::Test {
-protected:
-    TreeNodeRRsetTest() :
-        rrclass_(RRClass::IN()),
-        origin_name_("example.com"), www_name_("www.example.com"),
-        wildcard_name_("*.example.com"), match_name_("match.example.com"),
-        ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
-        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
-                             "www.example.com. 3600 IN A 192.0.2.2")),
-        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
-                                "2001:db8::1\n")),
-        dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
-        a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
-                                   "A 5 2 3600 20120814220826 20120715220826 "
-                                   "1234 example.com. FAKE")),
-        aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
-                                      " 3600 20120814220826 20120715220826 "
-                                      "1234 example.com. FAKE\n"
-                                      "www.example.com. 3600 IN RRSIG AAAA 5 2"
-                                      " 3600 20120814220826 20120715220826 "
-                                      "4321 example.com. FAKE\n")),
-        txt_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG TXT 5 2"
-                                     " 3600 20120814220826 20120715220826 "
-                                     "1234 example.com. FAKE\n")),
-        wildmatch_rrset_(textToRRset(
-                             "match.example.com. 3600 IN A 192.0.2.1\n"
-                             "match.example.com. 3600 IN A 192.0.2.2")),
-        wildmatch_rrsig_rrset_(textToRRset(
-                                   "match.example.com. 3600 IN RRSIG "
-                                   "A 5 2 3600 20120814220826 20120715220826 "
-                                   "1234 example.com. FAKE")),
-        zone_data_(NULL), origin_node_(NULL), www_node_(NULL),
-        wildcard_node_(NULL), ns_rdataset_(NULL), dname_rdataset_(NULL),
-        a_rdataset_(NULL), aaaa_rdataset_(NULL), rrsig_only_rdataset_(NULL),
-        wildcard_rdataset_(NULL)
-    {}
-    void SetUp() {
-        // We create some common test data here in SetUp() so it will be
-        // as exception safe as possible.
-
-        zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
-
-        zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
-        ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
-                                        ConstRRsetPtr());
-        origin_node_->setData(ns_rdataset_);
-        dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
-                                           ConstRRsetPtr());
-        ns_rdataset_->next = dname_rdataset_;
-
-        zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
-        a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                       a_rrsig_rrset_);
-        www_node_->setData(a_rdataset_);
-
-        aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
-                                          aaaa_rrsig_rrset_);
-        a_rdataset_->next = aaaa_rdataset_;
-
-        // A rare (half broken) case of RRSIG-only set
-        rrsig_only_rdataset_ = RdataSet::create(mem_sgmt_, encoder_,
-                                                ConstRRsetPtr(),
-                                                txt_rrsig_rrset_);
-        aaaa_rdataset_->next = rrsig_only_rdataset_;
-
-        zone_data_->insertName(mem_sgmt_, wildcard_name_, &wildcard_node_);
-        wildcard_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
-                                              a_rrsig_rrset_);
-        wildcard_node_->setData(wildcard_rdataset_);
-    }
-    void TearDown() {
-        ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
-        // detect any memory leak
-        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
-    }
-
-    const RRClass rrclass_;
-    const Name origin_name_, www_name_, wildcard_name_, match_name_;
-    isc::util::MemorySegmentLocal mem_sgmt_;
-    RdataEncoder encoder_;
-    MessageRenderer renderer_, renderer_expected_;
-    ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
-        a_rrsig_rrset_, aaaa_rrsig_rrset_, txt_rrsig_rrset_,
-        wildmatch_rrset_, wildmatch_rrsig_rrset_;
-    ZoneData* zone_data_;
-    ZoneNode* origin_node_;
-    ZoneNode* www_node_;
-    ZoneNode* wildcard_node_;
-    RdataSet* ns_rdataset_;
-    RdataSet* dname_rdataset_;
-    RdataSet* a_rdataset_;
-    RdataSet* aaaa_rdataset_;
-    RdataSet* rrsig_only_rdataset_;
-    RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
-};
-
-// Check some trivial fields of a constructed TreeNodeRRset (passed as
-// AbstractRRset as we'd normally use it in polymorphic way).
-// Other complicated fields are checked through rendering tests.
-void
-checkBasicFields(const AbstractRRset& actual_rrset, const Name& expected_name,
-                 const RRClass& expected_class, const RRType& expected_type,
-                 const uint32_t expected_ttl,
-                 size_t expected_rdatacount, size_t expected_sigcount)
-{
-    EXPECT_EQ(expected_name, actual_rrset.getName());
-    EXPECT_EQ(expected_class, actual_rrset.getClass());
-    EXPECT_EQ(expected_type, actual_rrset.getType());
-    EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
-    EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
-    EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
-}
-
-// The following two are trivial wrapper to create a shared pointer
-// version of TreeNodeRRset object in order to work around dubious
-// behavior of some C++ compiler: they reject getting a const reference to
-// a temporary non-copyable object.
-boost::shared_ptr<TreeNodeRRset>
-createRRset(const RRClass& rrclass, const ZoneNode* node,
-            const RdataSet* rdataset, bool dnssec_ok)
-{
-    return (boost::shared_ptr<TreeNodeRRset>(
-                new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok)));
-}
-
-boost::shared_ptr<TreeNodeRRset>
-createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
-            const RdataSet* rdataset, bool dnssec_ok)
-{
-    return (boost::shared_ptr<TreeNodeRRset>(
-                new TreeNodeRRset(realname, rrclass, node, rdataset,
-                                  dnssec_ok)));
-}
-
-TEST_F(TreeNodeRRsetTest, create) {
-    // Constructed with RRSIG, and it should be visible.
-    checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
-                     www_name_, rrclass_, RRType::A(), 3600, 2, 1);
-    // Constructed with RRSIG, and it should be invisible.
-    checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
-                     www_name_, rrclass_, RRType::A(), 3600, 2, 0);
-    // Constructed without RRSIG, and it would be visible (but of course won't)
-    checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
-                     origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
-    // Constructed without RRSIG, and it should be visible
-    checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
-                     origin_name_, rrclass_, RRType::NS(), 3600, 1, 0);
-    // RRSIG-only case (note the RRset's type is covered type)
-    checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
-                                  true),
-                     www_name_, rrclass_, RRType::TXT(), 3600, 0, 1);
-    // RRSIG-only case (note the RRset's type is covered type), but it's
-    // invisible
-    checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
-                                  false),
-                     www_name_, rrclass_, RRType::TXT(), 3600, 0, 0);
-    // Wildcard substitution
-    checkBasicFields(*createRRset(match_name_, rrclass_,
-                                  wildcard_node_, wildcard_rdataset_,
-                                  true),
-                     match_name_, rrclass_, RRType::A(), 3600, 2, 1);
-}
-
-// The following two templated functions are helper to encapsulate the
-// concept truncation and handle MessageRenderer and OutputBuffer transparently
-// in templated test cases.
-template <typename OutputType>
-void
-setOutputLengthLimit(OutputType& output, size_t len_limit) {
-    output.setLengthLimit(len_limit);
-}
-template <>
-void
-setOutputLengthLimit<OutputBuffer>(OutputBuffer&, size_t) {
-}
-
-template <typename OutputType>
-bool
-isOutputTruncated(OutputType& output) {
-    return (output.isTruncated());
-}
-template <>
-bool
-isOutputTruncated<OutputBuffer>(OutputBuffer&) {
-    return (false);
-}
-
-// Templated so we so can support OutputBuffer version of toWire().
-// We use the above helper templated functions for some renderer only methods.
-// We test two sets of cases: normal rendering case and case when truncation
-// is expected.  The latter is effectively for MessageRenderer only.
-// If len_limit == 0, we consider it the normal case; otherwise it's for
-// truncation.  prepended_name isn't used for the truncation case.
-template <typename OutputType>
-void
-checkToWireResult(OutputType& expected_output, OutputType& actual_output,
-                  const AbstractRRset& actual_rrset,
-                  const Name& prepended_name,
-                  ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
-                  bool dnssec_ok,
-                  size_t len_limit = 0,
-                  size_t expected_result = 0)
-{
-    expected_output.clear();
-    actual_output.clear();
-
-    if (len_limit == 0) {       // normal rendering
-        // Prepare "actual" rendered data.  We prepend a name to confirm the
-        // owner name should be compressed in both cases.
-        prepended_name.toWire(actual_output);
-        const size_t rdata_count = rrset ? rrset->getRdataCount() : 0;
-        const int expected_ret = (dnssec_ok && rrsig_rrset) ?
-            rdata_count + rrsig_rrset->getRdataCount() : rdata_count;
-        EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
-    } else {                    // truncation
-        setOutputLengthLimit(actual_output, len_limit);
-        EXPECT_EQ(expected_result, actual_rrset.toWire(actual_output));
-        EXPECT_TRUE(isOutputTruncated(actual_output)); // always true here
-    }
-
-    // Prepare "expected" data.
-    if (len_limit == 0) {       // normal rendering
-        prepended_name.toWire(expected_output);
-    } else {                    // truncation
-        setOutputLengthLimit(expected_output, len_limit);
-    }
-    if (rrset) {
-        rrset->toWire(expected_output);
-    }
-    if (!isOutputTruncated(expected_output) && dnssec_ok && rrsig_rrset) {
-        rrsig_rrset->toWire(expected_output);
-    }
-
-    // Compare the two.
-    matchWireData(expected_output.getData(), expected_output.getLength(),
-                  actual_output.getData(), actual_output.getLength());
-}
-
-TEST_F(TreeNodeRRsetTest, toWire) {
-    MessageRenderer expected_renderer, actual_renderer;
-    OutputBuffer expected_buffer(0), actual_buffer(0);
-
-    {
-        SCOPED_TRACE("with RRSIG, DNSSEC OK");
-        const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          www_name_, a_rrset_, a_rrsig_rrset_, true);
-        // Currently the buffer version throws
-        EXPECT_THROW(
-            checkToWireResult(expected_buffer, actual_buffer, rrset,
-                              www_name_, a_rrset_, a_rrsig_rrset_, true),
-            isc::Unexpected);
-    }
-
-    {
-        SCOPED_TRACE("with RRSIG, DNSSEC not OK");
-        const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          www_name_, a_rrset_, a_rrsig_rrset_, false);
-    }
-
-    {
-        SCOPED_TRACE("without RRSIG, DNSSEC OK");
-        const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_, true);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          origin_name_, ns_rrset_, ConstRRsetPtr(), true);
-    }
-
-    {
-        SCOPED_TRACE("without RRSIG, DNSSEC not OK");
-        const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_,
-                                  false);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          origin_name_, ns_rrset_, ConstRRsetPtr(), false);
-    }
-
-    {
-        // RDATA of DNAME DR shouldn't be compressed.  Prepending "example.org"
-        // will check that.
-        SCOPED_TRACE("uncompressed RDATA");
-        const TreeNodeRRset rrset(rrclass_, origin_node_, dname_rdataset_,
-                                  false);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          Name("example.org"), dname_rrset_, ConstRRsetPtr(),
-                          false);
-    }
-
-    {
-        SCOPED_TRACE("wildcard with RRSIG");
-        checkToWireResult(expected_renderer, actual_renderer,
-                          *createRRset(match_name_, rrclass_, wildcard_node_,
-                                       wildcard_rdataset_, true),
-                          origin_name_, wildmatch_rrset_,
-                          wildmatch_rrsig_rrset_, true);
-    }
-
-    {
-        SCOPED_TRACE("wildcard without RRSIG");
-        checkToWireResult(expected_renderer, actual_renderer,
-                          *createRRset(match_name_, rrclass_, wildcard_node_,
-                                       wildcard_rdataset_, false),
-                          origin_name_, wildmatch_rrset_,
-                          wildmatch_rrsig_rrset_, false);
-    }
-
-    {
-        // Very unusual case: the set only contains RRSIG (already rare)
-        // and it's requested to be dumped to wire (can only happen in
-        // ANY or type-RRSIG queries, which are rare also).  But can still
-        // happen.
-        SCOPED_TRACE("RRSIG only, DNSSEC OK");
-        const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
-                                  true);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,true);
-    }
-
-    {
-        // Similar to the previous case, but DNSSEC records aren't requested.
-        // In practice this case wouldn't happen, but API-wise possible, so
-        // we test it explicitly.
-        SCOPED_TRACE("RRSIG only, DNSSEC not OK");
-        const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
-                                  false);
-        checkToWireResult(expected_renderer, actual_renderer, rrset,
-                          www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,false);
-    }
-}
-
-TEST_F(TreeNodeRRsetTest, toWireTruncated) {
-    MessageRenderer expected_renderer, actual_renderer;
-    // dummy parameter to checkToWireResult (unused for the this test case)
-    const Name& name = Name::ROOT_NAME();
-
-    // Set the truncation limit to name len + 14 bytes of fixed data for A RR
-    // (type, class, TTL, rdlen, and 4-byte IPv4 address).  Then we can only
-    // render just one RR, without any garbage trailing data.
-    checkToWireResult(expected_renderer, actual_renderer,
-                      *createRRset(rrclass_, www_node_, a_rdataset_, true),
-                      name, a_rrset_, a_rrsig_rrset_, true,
-                      www_name_.getLength() + 14,
-                      1);   // 1 main RR, no RRSIG
-
-    // The first main RRs should fit in the renderer (the name will be
-    // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
-    checkToWireResult(expected_renderer, actual_renderer,
-                      *createRRset(rrclass_, www_node_, a_rdataset_, true),
-                      name, a_rrset_, a_rrsig_rrset_, true,
-                      www_name_.getLength() + 14 + 2 + 14,
-                      2);   // 2 main RR, no RRSIG
-
-    // This RRset has one main RR and two RRSIGs.  Rendering the second RRSIG
-    // causes truncation.
-    // First, compute the rendered length for the main RR and a single RRSIG.
-    // The length of the RRSIG should be the same if we "accidentally"
-    // rendered the RRSIG for the A RR (which only contains one RRSIG).
-    expected_renderer.clear();
-    aaaa_rrset_->toWire(expected_renderer);
-    a_rrsig_rrset_->toWire(expected_renderer);
-    const size_t limit_len = expected_renderer.getLength();
-    // Then perform the test
-    checkToWireResult(expected_renderer, actual_renderer,
-                      *createRRset(rrclass_, www_node_, aaaa_rdataset_, true),
-                      name, aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
-                      2);   // 1 main RR, 1 RRSIG
-
-    // RRSIG only case.  Render length limit being 1, so it won't fit,
-    // and will cause truncation.
-    checkToWireResult(expected_renderer, actual_renderer,
-                      *createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
-                                   true),
-                      name, ConstRRsetPtr(), txt_rrsig_rrset_, true, 1,
-                      0);   // no RR
-}
-
-void
-checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
-    for (vector<string>::const_iterator it = expected.begin();
-         it != expected.end();
-         ++it)
-    {
-        ASSERT_FALSE(rit->isLast());
-        EXPECT_EQ(*it, rit->getCurrent().toText());
-        rit->next();
-    }
-    // We should have reached the end of RDATA
-    EXPECT_TRUE(rit->isLast());
-
-    // move to the first RDATA again, and check the value.
-    rit->first();
-    if (!expected.empty()) {
-        EXPECT_EQ(expected[0], rit->getCurrent().toText());
-    } else {
-        EXPECT_TRUE(rit->isLast());
-    }
-}
-
-TEST_F(TreeNodeRRsetTest, getRdataIterator) {
-    // This RRset should have 2 A RDATAs
-    vector<string> expected;
-    expected.push_back("192.0.2.1");
-    expected.push_back("192.0.2.2");
-    checkRdataIterator(expected,
-                       TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
-                       getRdataIterator());
-
-    // The iterator shouldn't work different with or without RRSIG
-    checkRdataIterator(expected,
-                       TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
-                       getRdataIterator());
-
-    // This RRset should have 1 NS RDATA (containing name field)
-    expected.clear();
-    expected.push_back("ns.example.com.");
-    checkRdataIterator(expected,
-                       TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
-                                     false).getRdataIterator());
-
-    // RRSIG only.  Iterator will be empty and shouldn't cause any disruption.
-    expected.clear();
-    checkRdataIterator(expected,
-                       TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
-                                     true).getRdataIterator());
-}
-
-void
-checkToText(const AbstractRRset& actual_rrset,
-            ConstRRsetPtr expected_rrset, ConstRRsetPtr expected_sig_rrset)
-{
-    const string actual_text = actual_rrset.toText();
-    const string expected_text =
-        (expected_rrset ? expected_rrset->toText() : "") +
-        (expected_sig_rrset ? expected_sig_rrset->toText() : "");
-    EXPECT_EQ(expected_text, actual_text);
-}
-
-TEST_F(TreeNodeRRsetTest, toText) {
-    // Constructed with RRSIG, and it should be visible.
-    checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true),
-                a_rrset_, a_rrsig_rrset_);
-    // Constructed with RRSIG, and it should be invisible.
-    checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, false),
-                a_rrset_, ConstRRsetPtr());
-    // Constructed without RRSIG, and it would be visible (but of course won't)
-    checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
-                ns_rrset_, ConstRRsetPtr());
-    // Constructed without RRSIG, and it should be visible
-    checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
-                ns_rrset_, ConstRRsetPtr());
-    // Wildcard expanded name with RRSIG
-    checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
-                             wildcard_rdataset_, true),
-                wildmatch_rrset_, wildmatch_rrsig_rrset_);
-    // Wildcard expanded name without RRSIG
-    checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
-                             wildcard_rdataset_, false),
-                wildmatch_rrset_, ConstRRsetPtr());
-    // RRSIG case
-    checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
-                             true),
-                ConstRRsetPtr(), txt_rrsig_rrset_);
-    // Similar to the previous case, but completely empty.
-    checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
-                             false),
-                ConstRRsetPtr(), ConstRRsetPtr());
-}
-
-TEST_F(TreeNodeRRsetTest, isSameKind) {
-    const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
-
-    // Same name (node), same type (rdataset) => same kind
-    EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
-                                              a_rdataset_, true)));
-
-    // Same name (node), different type (rdataset) => not same kind
-    EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
-                                               aaaa_rdataset_, true)));
-
-    // Different name, different type => not same kind
-    EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
-                                               ns_rdataset_, true)));
-
-    // Different name, same type => not same kind.
-    // Note: this shouldn't happen in our in-memory data source implementation,
-    // but API doesn't prohibit it.
-    EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
-                                               a_rdataset_, true)));
-
-    // Wildcard and expanded RRset
-    const TreeNodeRRset wildcard_rrset(rrclass_, wildcard_node_,
-                                       wildcard_rdataset_, true);
-    const TreeNodeRRset match_rrset(match_name_, rrclass_, wildcard_node_,
-                                    wildcard_rdataset_, true);
-    EXPECT_FALSE(wildcard_rrset.isSameKind(match_rrset));
-    EXPECT_FALSE(match_rrset.isSameKind(wildcard_rrset));
-
-    // Both are wildcard expanded, and have different names
-    const TreeNodeRRset match2_rrset(Name("match2.example.com"), rrclass_,
-                                     wildcard_node_, wildcard_rdataset_, true);
-    EXPECT_FALSE(match_rrset.isSameKind(match2_rrset));
-    EXPECT_FALSE(match2_rrset.isSameKind(match_rrset));
-
-    // Pathological case.  "badwild" is constructed as if expanded due to
-    // a wildcard, but has the same owner name of the wildcard itself.
-    // Technically, they should be considered of the same kind, but this
-    // implementation considers they are not.  But this case shouldn't happen
-    // as long as the RRsets are only constructed inside the in-memory
-    // zone finder implementation.
-    const TreeNodeRRset badwild_rrset(wildcard_name_, rrclass_, wildcard_node_,
-                                      wildcard_rdataset_, true);
-    EXPECT_FALSE(wildcard_rrset.isSameKind(badwild_rrset));
-    EXPECT_EQ(wildcard_rrset.toText(), badwild_rrset.toText());
-
-    // Pathological case:  Same name, same type, but different class.
-    // This case should be impossible because if the RRsets share the same
-    // tree node, they must belong to the same RR class.  This case is
-    // a caller's bug, and the isSameKind() implementation returns the
-    // "wrong" (= true) answer.
-    EXPECT_TRUE(rrset.isSameKind(*createRRset(RRClass::CH(), www_node_,
-                                              a_rdataset_, true)));
-
-    // Same kind of different RRset class
-    EXPECT_TRUE(rrset.isSameKind(*a_rrset_));
-
-    // Different kind of different RRset class
-    EXPECT_FALSE(rrset.isSameKind(*aaaa_rrset_));
-}
-
-TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
-    // Note: buffer version of toWire() is checked in the toWire test.
-
-    TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
-
-    EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
-    EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
-    EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
-                 isc::Unexpected);
-    EXPECT_THROW(rrset.getRRsig(), isc::Unexpected);
-    RdataPtr sig_rdata = createRdata(
-        RRType::RRSIG(), rrclass_,
-        "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
-    EXPECT_THROW(rrset.addRRsig(sig_rdata), isc::Unexpected);
-    EXPECT_THROW(rrset.addRRsig(*a_rrsig_rrset_), isc::Unexpected);
-    EXPECT_THROW(rrset.addRRsig(a_rrsig_rrset_), isc::Unexpected);
-    EXPECT_THROW(rrset.addRRsig(RRsetPtr()), isc::Unexpected);
-    EXPECT_THROW(rrset.removeRRsig(), isc::Unexpected);
-}
-}
diff --git a/src/lib/datasrc/memory/tests/zone_data_unittest.cc b/src/lib/datasrc/memory/tests/zone_data_unittest.cc
deleted file mode 100644
index d15fe8b..0000000
--- a/src/lib/datasrc/memory/tests/zone_data_unittest.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "memory_segment_test.h"
-
-#include <dns/rdataclass.h>
-
-#include <exceptions/exceptions.h>
-
-#include <dns/name.h>
-#include <dns/labelsequence.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/memory/rdataset.h>
-#include <datasrc/memory/zone_data.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <new>                  // for bad_alloc
-#include <string>
-
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc::memory;
-using namespace isc::datasrc::memory::test;
-using namespace isc::testutils;
-
-namespace {
-
-// With this single fixture we'll test both NSEC3Data and ZoneData
-class ZoneDataTest : public ::testing::Test {
-protected:
-    ZoneDataTest() :
-        nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
-        param_rdata_nosalt_("1 1 10 -"),
-        param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
-        nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
-        nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
-        nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
-                               " TDK23RP6 SOA"),
-        zname_("example.com"),
-        zone_data_(ZoneData::create(mem_sgmt_, zname_)),
-        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
-        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
-        nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
-                                 "1 0 12 aabbccdd TDK23RP6 SOA"))
-    {}
-    void TearDown() {
-        if (nsec3_data_ != NULL) {
-            NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
-        }
-        if (zone_data_ != NULL) {
-            ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
-        }
-        // detect any memory leak in the test memory segment
-        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
-    }
-
-    MemorySegmentTest mem_sgmt_;
-    NSEC3Data* nsec3_data_;
-    const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
-        param_rdata_largesalt_;
-    const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
-        nsec3_rdata_largesalt_;
-    const Name zname_;
-    ZoneData* zone_data_;
-    const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
-    RdataEncoder encoder_;
-};
-
-// Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
-template <typename RdataType>
-void
-checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
-    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
-
-    // Internal tree should be created and empty.
-    EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
-
-    EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
-    EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
-    EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
-    EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
-    if (expect_rdata.getSalt().size() > 0) {
-        EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
-                            nsec3_data->getSaltData(),
-                            expect_rdata.getSalt().size()));
-    }
-
-    NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
-}
-
-void
-checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
-                  const RdataSet* expected_set)
-{
-    ZoneNode* node = NULL;
-    tree.find(name, &node);
-    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
-    EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
-}
-
-TEST_F(ZoneDataTest, createNSEC3Data) {
-    // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
-    // and of NSEC3), check if the resulting parameters match.
-    checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
-    checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
-    checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
-
-    // Same concepts of the tests, using NSEC3 RDATA.
-    checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
-    checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
-    checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
-}
-
-TEST_F(ZoneDataTest, addNSEC3) {
-    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
-
-    ZoneNode* node = NULL;
-    nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
-    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
-    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
-
-    RdataSet* rdataset_nsec3 =
-        RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
-    node->setData(rdataset_nsec3);
-
-    // Confirm we can find the added ones from the zone data.
-    checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
-                      RRType::NSEC3(), rdataset_nsec3);
-
-    // TearDown() will confirm there's no leak on destroy
-}
-
-TEST_F(ZoneDataTest, getOriginNode) {
-    EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
-}
-
-TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
-    // Note: below, we use our knowledge of how memory allocation happens
-    // within the NSEC3Data, the zone data and the underlying domain tree
-    // implementation.  We'll emulate rare situations where allocate() fails
-    // with an exception, and confirm it doesn't cause any harsh disruption
-    // or leak.
-
-    // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
-    // will fail due to bad_alloc.  It shouldn't cause memory leak
-    // (that would be caught in TearDown()).
-    mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
-
-    // allocate() will throw on the insertion of the origin node.
-    mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
-
-    // allocate() will throw on creating the zone data.
-    mem_sgmt_.setThrowCount(3);
-    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
-
-    // These incomplete create() attempts shouldn't cause memory leak
-    // (that would be caught in TearDown()).
-}
-
-TEST_F(ZoneDataTest, addRdataSets) {
-    // Insert a name to the zone, and add a couple the data (RdataSet) objects
-    // to the corresponding node.
-
-    ZoneNode* node = NULL;
-    zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
-    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
-    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
-
-    RdataSet* rdataset_a =
-        RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
-    node->setData(rdataset_a);
-
-    RdataSet* rdataset_aaaa =
-        RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
-    // make a linked list and replace the list head
-    rdataset_aaaa->next = rdataset_a;
-    node->setData(rdataset_aaaa);
-
-    // Confirm we can find the added ones from the zone data.
-    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
-                      RRType::A(), rdataset_a);
-    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
-                      RRType::AAAA(), rdataset_aaaa);
-    // There's no NS (or anything other than AAAA or A) RdataSet in the list
-    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
-                      RRType::NS(), NULL);
-
-    // TearDown() will confirm there's no leak on destroy
-}
-
-TEST_F(ZoneDataTest, getSetNSEC3Data) {
-    // Initially there's no NSEC3 data
-    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
-    // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
-    EXPECT_FALSE(zone_data_->isNSEC3Signed());
-
-    // Set a new one.  The set method should return NULL.  The get method
-    // should return the new one.
-    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
-    NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
-    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
-    EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
-    EXPECT_TRUE(zone_data_->isNSEC3Signed());
-
-    // Replace an existing one with a yet another one.
-    // We're responsible for destroying the old one.
-    NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
-    old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
-    EXPECT_EQ(nsec3_data, old_nsec3_data);
-    EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
-    EXPECT_TRUE(zone_data_->isNSEC3Signed());
-    NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
-
-    // Setting NULL clears any existing one.
-    old_nsec3_data = zone_data_->setNSEC3Data(NULL);
-    EXPECT_EQ(nsec3_data2, old_nsec3_data);
-    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
-    EXPECT_FALSE(zone_data_->isNSEC3Signed());
-
-    // Then set it again.  The zone data should destroy it on its own
-    // destruction.
-    zone_data_->setNSEC3Data(old_nsec3_data);
-}
-
-TEST_F(ZoneDataTest, isSigned) {
-    // By default it's considered unsigned
-    EXPECT_FALSE(zone_data_->isSigned());
-
-    // declare it's signed, the isSigned() says so too
-    zone_data_->setSigned(true);
-    EXPECT_TRUE(zone_data_->isSigned());
-
-    // change it to unsigned again
-    zone_data_->setSigned(false);
-    EXPECT_FALSE(zone_data_->isSigned());
-}
-}
diff --git a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc b/src/lib/datasrc/memory/tests/zone_finder_unittest.cc
deleted file mode 100644
index 94ebe5e..0000000
--- a/src/lib/datasrc/memory/tests/zone_finder_unittest.cc
+++ /dev/null
@@ -1,1481 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include "memory_segment_test.h"
-
-// NOTE: this faked_nsec3 inclusion (and all related code below)
-// was ported during #2109 for the convenience of implementing #2218
-// In #2218 the NSEC3 test code in this file is expected to be finalized.
-// In #2219 the original is expected to be removed, and this file should
-// probably be moved here (and any leftover code not handled in #2218 should
-// be cleaned up)
-#include "../../tests/faked_nsec3.h"
-
-#include <datasrc/memory/zone_finder.h>
-#include <datasrc/memory/rdata_serialization.h>
-#include <datasrc/data_source.h>
-#include <testutils/dnsmessage_test.h>
-
-#include <boost/foreach.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::testutils;
-using boost::shared_ptr;
-using namespace isc::datasrc::test;
-using namespace isc::datasrc::memory::test;
-using namespace isc::datasrc::memory;
-
-namespace {
-// Commonly used result codes (Who should write the prefix all the time)
-using result::SUCCESS;
-using result::EXIST;
-
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-// Also see NOTE at inclusion of "../../tests/faked_nsec3.h"
-class TestNSEC3HashCreator : public NSEC3HashCreator {
-    class TestNSEC3Hash : public NSEC3Hash {
-    private:
-        typedef map<Name, string> NSEC3HashMap;
-        typedef NSEC3HashMap::value_type NSEC3HashPair;
-        NSEC3HashMap map_;
-    public:
-        TestNSEC3Hash() {
-            // Build pre-defined hash
-            map_[Name("example.org")] = apex_hash;
-            map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("x.y.w.example.org")] =
-                "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
-            map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-            map_[Name("w.example.org")] = w_hash;
-            map_[Name("zzz.example.org")] = zzz_hash;
-            map_[Name("smallest.example.org")] =
-                "00000000000000000000000000000000";
-            map_[Name("largest.example.org")] =
-                "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
-        }
-        virtual string calculate(const Name& name) const {
-            const NSEC3HashMap::const_iterator found = map_.find(name);
-            if (found != map_.end()) {
-                return (found->second);
-            }
-            isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
-                      << name);
-        }
-        virtual bool match(const generic::NSEC3PARAM&) const {
-            return (true);
-        }
-        virtual bool match(const generic::NSEC3&) const {
-            return (true);
-        }
-    };
-
-public:
-    virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
-        return (new TestNSEC3Hash);
-    }
-    virtual NSEC3Hash* create(const generic::NSEC3&) const {
-        return (new TestNSEC3Hash);
-    }
-};
-
-
-/// \brief expensive rrset converter
-///
-/// converts any specialized rrset (which may not have implemented some
-/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
-/// checks.
-///
-/// Done very inefficiently through text representation, speed should not
-/// be a concern here.
-ConstRRsetPtr
-convertRRset(ConstRRsetPtr src) {
-    return (textToRRset(src->toText()));
-}
-
-/// \brief Test fixture for the InMemoryZoneFinder class
-class InMemoryZoneFinderTest : public ::testing::Test {
-    // A straightforward pair of textual RR(set) and a RRsetPtr variable
-    // to store the RRset.  Used to build test data below.
-    struct RRsetData {
-        const char* const text; // textual representation of an RRset
-        RRsetPtr* rrset;
-    };
-protected:
-    // The following sub tests are shared by multiple test cases, changing
-    // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
-    // expected_flags is set to either RESULT_NSEC_SIGNED or
-    // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
-    // find() is expected to set the corresponding flags.
-    // find_options should be set to FIND_DNSSEC for NSEC-signed case when
-    // NSEC is expected to be returned.
-    void findCheck(ZoneFinder::FindResultFlags expected_flags =
-                   ZoneFinder::RESULT_DEFAULT,
-                   ZoneFinder::FindOptions find_options =
-                   ZoneFinder::FIND_DEFAULT);
-    void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
-                        ZoneFinder::RESULT_DEFAULT);
-    void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
-                       ZoneFinder::RESULT_DEFAULT,
-                       ZoneFinder::FindOptions find_options =
-                       ZoneFinder::FIND_DEFAULT);
-    void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
-                               ZoneFinder::RESULT_DEFAULT,
-                               ZoneFinder::FindOptions find_options =
-                               ZoneFinder::FIND_DEFAULT);
-    void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
-                          ZoneFinder::RESULT_DEFAULT);
-    void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
-                            ZoneFinder::RESULT_DEFAULT);
-    void findNSECENTCheck(const Name& ent_name,
-                          ConstRRsetPtr expected_nsec,
-                          ZoneFinder::FindResultFlags expected_flags =
-                          ZoneFinder::RESULT_DEFAULT);
-
-public:
-    InMemoryZoneFinderTest() :
-        class_(RRClass::IN()),
-        origin_("example.org"),
-        zone_data_(ZoneData::create(mem_sgmt_, origin_)),
-        zone_finder_(*zone_data_, class_)
-    {
-        // Build test RRsets.  Below, we construct an RRset for
-        // each textual RR(s) of zone_data, and assign it to the corresponding
-        // rr_xxx.
-        // Note that this contains an out-of-zone RR, and due to the
-        // validation check of masterLoad() used below, we cannot add SOA.
-        const RRsetData zone_data[] = {
-            {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
-            {"example.org. 300 IN A 192.0.2.1", &rr_a_},
-            {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
-            {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
-            {"cname.example.org. 300 IN CNAME canonical.example.org",
-             &rr_cname_},
-            {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
-            {"dname.example.org. 300 IN DNAME target.example.org.",
-             &rr_dname_},
-            {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
-            {"dname.example.org. 300 IN NS ns.dname.example.org.",
-             &rr_dname_ns_},
-            {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
-            {"child.example.org. 300 IN NS ns.child.example.org.",
-             &rr_child_ns_},
-            {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF",
-             &rr_child_ds_},
-            {"ns.child.example.org. 300 IN A 192.0.2.153",
-             &rr_child_glue_},
-            {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
-             &rr_grandchild_ns_},
-            {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
-             &rr_grandchild_glue_},
-            {"dname.child.example.org. 300 IN DNAME example.com.",
-             &rr_child_dname_},
-            {"example.com. 300 IN A 192.0.2.10", &rr_out_},
-            {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
-            {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
-             &rr_cnamewild_},
-            {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
-            {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
-            {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
-             &rr_nested_emptywild_},
-            {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
-            {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
-             &rr_dnamewild_},
-            {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
-            {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
-            {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
-             &rr_not_wild_another_},
-            {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
-             "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
-             &rr_nsec3_},
-            {"example.org. 300 IN NSEC wild.*.foo.example.org. "
-             "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
-            // Together with the apex NSEC, these next NSECs make a complete
-            // chain in the case of the zone for the emptyNonterminal tests
-            // (We may want to clean up this generator code and/or masterLoad
-            // so that we can prepare conflicting datasets better)
-            {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
-             "A RRSIG NSEC", &rr_ent_nsec2_},
-            {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
-             &rr_ent_nsec3_},
-            {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
-             &rr_ent_nsec4_},
-            // And these are NSECs used in different tests
-            {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
-             &rr_ns_nsec_},
-            {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
-             &rr_wild_nsec_},
-            {NULL, NULL}
-        };
-
-        for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
-            *zone_data[i].rrset = textToRRset(zone_data[i].text);
-        }
-
-    }
-
-    ~InMemoryZoneFinderTest() {
-        // Make sure we reset the hash creator to the default
-        setNSEC3HashCreator(NULL);
-        ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
-    }
-
-    // NSEC3-specific call for 'loading' data
-    // This needs to be updated and checked when implementing #2118
-    void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
-        assert(rrset->getType() == RRType::NSEC3());
-
-        const Rdata* rdata = &rrset->getRdataIterator()->getCurrent();
-        const generic::NSEC3* nsec3_rdata =
-            dynamic_cast<const generic::NSEC3*>(rdata);
-        NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, *nsec3_rdata);
-        // in case we happen to be replacing, destroy old
-        NSEC3Data* old_data = zone_data_->setNSEC3Data(nsec3_data);
-        if (old_data != NULL) {
-            NSEC3Data::destroy(mem_sgmt_, old_data, rrset->getClass());
-        }
-        zone_data_->setSigned(true);
-    }
-
-    // simplified version of 'loading' data
-    void addZoneData(const ConstRRsetPtr rrset) {
-        ZoneNode* node = NULL;
-
-        if (rrset->getType() == RRType::NSEC3()) {
-            return (addZoneDataNSEC3(rrset));
-        } else if (rrset->getType() == RRType::NSEC()) {
-            zone_data_->setSigned(true);
-        }
-
-        zone_data_->insertName(mem_sgmt_, rrset->getName(), &node);
-
-        if (rrset->getType() == RRType::NS() &&
-            rrset->getName() != zone_data_->getOriginNode()->getName()) {
-            node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
-        } else if (rrset->getType() == RRType::DNAME()) {
-            node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
-        }
-
-        RdataSet* next_rds = node->getData();
-        RdataSet* rdataset =
-            RdataSet::create(mem_sgmt_, encoder_, rrset, rrset->getRRsig());
-        rdataset->next = next_rds;
-        node->setData(rdataset);
-
-        // find wildcard nodes in name (go through all of them in case there
-        // is a nonterminal one)
-        // Note that this method is pretty much equal to the 'real' loader;
-        // but less efficient
-        Name name(rrset->getName());
-        while (name.getLabelCount() > 1) {
-            if (name.isWildcard()) {
-                ZoneNode* wnode = NULL;
-                // add Wild node
-                zone_data_->insertName(mem_sgmt_, name.split(1), &wnode);
-                wnode->setFlag(ZoneData::WILDCARD_NODE);
-                // add wildcard name itself too
-                zone_data_->insertName(mem_sgmt_, name, &wnode);
-            }
-            name = name.split(1);
-        }
-    }
-
-    // Some data to test with
-    const RRClass class_;
-    const Name origin_;
-    // The zone finder to torture by tests
-    MemorySegmentTest mem_sgmt_;
-    memory::ZoneData* zone_data_;
-    memory::InMemoryZoneFinder zone_finder_;
-    isc::datasrc::memory::RdataEncoder encoder_;
-
-    // Placeholder for storing RRsets to be checked with rrsetsCheck()
-    vector<ConstRRsetPtr> actual_rrsets_;
-
-    /*
-     * Some RRsets to put inside the zone.
-     */
-    RRsetPtr
-        // Out of zone RRset
-        rr_out_,
-        // NS of example.org
-        rr_ns_,
-        // A of ns.example.org
-        rr_ns_a_,
-        // AAAA of ns.example.org
-        rr_ns_aaaa_,
-        // A of example.org
-        rr_a_;
-    RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
-    RRsetPtr rr_cname_a_; // for mixed CNAME + A case
-    RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
-    RRsetPtr rr_dname_a_; // for mixed DNAME + A case
-    RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
-    RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
-    RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
-    RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data)
-    RRsetPtr rr_child_glue_; // glue RR of the child domain
-    RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
-    RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
-    RRsetPtr rr_child_dname_; // A DNAME under NS
-    RRsetPtr rr_wild_;        // Wildcard record
-    RRsetPtr rr_cnamewild_;     // CNAME at a wildcard
-    RRsetPtr rr_emptywild_;
-    RRsetPtr rr_nested_emptywild_;
-    RRsetPtr rr_nswild_, rr_dnamewild_;
-    RRsetPtr rr_child_wild_;
-    RRsetPtr rr_under_wild_;
-    RRsetPtr rr_not_wild_;
-    RRsetPtr rr_not_wild_another_;
-    RRsetPtr rr_nsec3_;
-    RRsetPtr rr_nsec_;
-    RRsetPtr rr_ent_nsec2_;
-    RRsetPtr rr_ent_nsec3_;
-    RRsetPtr rr_ent_nsec4_;
-    RRsetPtr rr_ns_nsec_;
-    RRsetPtr rr_wild_nsec_;
-
-    // A faked NSEC3 hash calculator for convenience.
-    // Tests that need to use the faked hashed values should call
-    // setNSEC3HashCreator() with a pointer to this variable at the beginning
-    // of the test (at least before adding any NSEC3/NSEC3PARAM RR).
-    TestNSEC3HashCreator nsec3_hash_creator_;
-
-    /**
-     * \brief Test one find query to the zone finder.
-     *
-     * Asks a query to the zone finder and checks it does not throw and returns
-     * expected results. It returns nothing, it just signals failures
-     * to GTEST.
-     *
-     * \param name The name to ask for.
-     * \param rrtype The RRType to ask of.
-     * \param result The expected code of the result.
-     * \param check_answer Should a check against equality of the answer be
-     *     done?
-     * \param answer The expected rrset, if any should be returned.
-     * \param expected_flags The expected result flags returned via find().
-     *     These can be tested using isWildcard() etc.
-     * \param zone_finder Check different InMemoryZoneFinder object than
-     *     zone_finder_ (if NULL, uses zone_finder_)
-     * \param check_wild_answer Checks that the answer has the same RRs, type
-     *     class and TTL as the eqxpected answer and that the name corresponds
-     *     to the one searched. It is meant for checking answers for wildcard
-     *     queries.
-     */
-    void findTest(const Name& name, const RRType& rrtype,
-                  ZoneFinder::Result result,
-                  bool check_answer = true,
-                  const ConstRRsetPtr& answer = ConstRRsetPtr(),
-                  ZoneFinder::FindResultFlags expected_flags =
-                  ZoneFinder::RESULT_DEFAULT,
-                  memory::InMemoryZoneFinder* zone_finder = NULL,
-                  ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
-                  bool check_wild_answer = false)
-    {
-        SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText());
-
-        if (zone_finder == NULL) {
-            zone_finder = &zone_finder_;
-        }
-        const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
-            RRsetPtr(); // note we use the same type as of retval of getRRsig()
-        // The whole block is inside, because we need to check the result and
-        // we can't assign to FindResult
-        EXPECT_NO_THROW({
-                ZoneFinderContextPtr find_result(zone_finder->find(
-                                                     name, rrtype, options));
-                // Check it returns correct answers
-                EXPECT_EQ(result, find_result->code);
-                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-                          find_result->isWildcard());
-                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
-                          != 0, find_result->isNSECSigned());
-                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
-                          != 0, find_result->isNSEC3Signed());
-                if (check_answer) {
-                    if (!answer) {
-                        ASSERT_FALSE(find_result->rrset);
-                    } else {
-                        ASSERT_TRUE(find_result->rrset);
-                        ConstRRsetPtr result_rrset(
-                            convertRRset(find_result->rrset));
-                        rrsetCheck(answer, result_rrset);
-                        if (answer_sig) {
-                            ASSERT_TRUE(result_rrset->getRRsig());
-                            rrsetCheck(answer_sig, result_rrset->getRRsig());
-                        }
-                    }
-                } else if (check_wild_answer) {
-                    ASSERT_NE(ConstRRsetPtr(), answer) <<
-                        "Wrong test, don't check for wild names if you expect "
-                        "empty answer";
-                    ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
-                        "No answer found";
-                    // Build the expected answer using the given name and
-                    // other parameter of the base wildcard RRset.
-                    RRsetPtr wildanswer(new RRset(name, answer->getClass(),
-                                                  answer->getType(),
-                                                  answer->getTTL()));
-                    RdataIteratorPtr expectedIt(answer->getRdataIterator());
-                    for (; !expectedIt->isLast(); expectedIt->next()) {
-                        wildanswer->addRdata(expectedIt->getCurrent());
-                    }
-
-                    ConstRRsetPtr result_rrset(
-                        convertRRset(find_result->rrset));
-                    rrsetCheck(wildanswer, result_rrset);
-
-                    // Same for the RRSIG, if any.
-                    if (answer_sig) {
-                        ASSERT_TRUE(result_rrset->getRRsig());
-
-                        RRsetPtr wildsig(new RRset(name,
-                                                   answer_sig->getClass(),
-                                                   RRType::RRSIG(),
-                                                   answer_sig->getTTL()));
-                        RdataIteratorPtr expectedIt(
-                            answer_sig->getRdataIterator());
-                        for (; !expectedIt->isLast(); expectedIt->next()) {
-                            wildsig->addRdata(expectedIt->getCurrent());
-                        }
-                        rrsetCheck(wildsig, result_rrset->getRRsig());
-                    }
-                }
-            });
-    }
-    /**
-     * \brief Calls the findAll on the finder and checks the result.
-     */
-    void findAllTest(const Name& name, ZoneFinder::Result result,
-                     const vector<ConstRRsetPtr>& expected_rrsets,
-                     ZoneFinder::FindResultFlags expected_flags =
-                     ZoneFinder::RESULT_DEFAULT,
-                     memory::InMemoryZoneFinder* finder = NULL,
-                     const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
-                     ZoneFinder::FindOptions options =
-                     ZoneFinder::FIND_DEFAULT)
-    {
-        if (finder == NULL) {
-            finder = &zone_finder_;
-        }
-        std::vector<ConstRRsetPtr> target;
-        ZoneFinderContextPtr find_result(finder->findAll(name, target,
-                                                         options));
-        EXPECT_EQ(result, find_result->code);
-        if (!rrset_result) {
-            EXPECT_FALSE(find_result->rrset);
-        } else {
-            ASSERT_TRUE(find_result->rrset);
-            rrsetCheck(rrset_result, convertRRset(find_result->rrset));
-        }
-        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-                  find_result->isWildcard());
-        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
-                  != 0, find_result->isNSECSigned());
-        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
-                  != 0, find_result->isNSEC3Signed());
-        // Convert all rrsets to 'full' ones before checking
-        std::vector<ConstRRsetPtr> converted_rrsets;
-        BOOST_FOREACH(ConstRRsetPtr cur_rrset, target) {
-            converted_rrsets.push_back(convertRRset(cur_rrset));
-        }
-        rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
-                    converted_rrsets.begin(), converted_rrsets.end());
-    }
-};
-
-/**
- * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
- *
- * Takes the created zone finder and checks its properties they are the same
- * as passed parameters.
- */
-TEST_F(InMemoryZoneFinderTest, constructor) {
-    ASSERT_EQ(origin_, zone_finder_.getOrigin());
-}
-
-TEST_F(InMemoryZoneFinderTest, findCNAME) {
-    // install CNAME RR
-    addZoneData(rr_cname_);
-
-    // Find A RR of the same.  Should match the CNAME
-    findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
-             rr_cname_);
-
-    // Find the CNAME itself.  Should result in normal SUCCESS
-    findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
-             rr_cname_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
-    // There's nothing special when we find a CNAME under a zone cut
-    // (with FIND_GLUE_OK).  The behavior is different from BIND 9,
-    // so we test this case explicitly.
-    addZoneData(rr_child_ns_);
-    ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
-        "cname.child.example.org. 300 IN CNAME target.child.example.org.");
-    addZoneData(rr_cname_under_cut_);
-    findTest(Name("cname.child.example.org"), RRType::AAAA(),
-             ZoneFinder::CNAME, true, rr_cname_under_cut_,
-             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-// Search under a DNAME record. It should return the DNAME
-TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
-    EXPECT_NO_THROW(addZoneData(rr_dname_));
-    findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
-             true, rr_dname_);
-}
-
-// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
-// influences only the data below (see RFC 2672, section 3)
-TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
-    EXPECT_NO_THROW(addZoneData(rr_dname_));
-    EXPECT_NO_THROW(addZoneData(rr_dname_a_));
-
-    const Name dname_name(rr_dname_->getName());
-    findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
-    findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
-             rr_dname_);
-    findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
-}
-
-// Try searching something that is both under NS and DNAME, without and with
-// GLUE_OK mode (it should stop at the NS and DNAME respectively).
-TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
-    addZoneData(rr_child_ns_);
-    addZoneData(rr_child_dname_);
-
-    Name lowName("below.dname.child.example.org.");
-
-    findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
-    findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
-             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-// Test adding child zones and zone cut handling
-TEST_F(InMemoryZoneFinderTest, delegationNS) {
-    // add in-zone data
-    EXPECT_NO_THROW(addZoneData(rr_ns_));
-
-    // install a zone cut
-    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
-
-    // below the zone cut
-    findTest(Name("www.child.example.org"), RRType::A(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_);
-
-    // at the zone cut
-    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_);
-    findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_);
-
-    // finding NS for the apex (origin) node.  This must not be confused
-    // with delegation due to the existence of an NS RR.
-    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
-
-    // unusual case of "nested delegation": the highest cut should be used.
-    EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
-    findTest(Name("www.grand.child.example.org"), RRType::A(),
-             // note: !rr_grandchild_ns_
-             ZoneFinder::DELEGATION, true, rr_child_ns_);
-}
-
-TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
-    // Similar setup to the previous one, but with DS RR at the delegation
-    // point.
-    addZoneData(rr_ns_);
-    addZoneData(rr_child_ns_);
-    addZoneData(rr_child_ds_);
-
-    // Normal types of query should result in delegation, but DS query
-    // should be considered in-zone (but only exactly at the delegation point).
-    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_);
-    findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
-             true, rr_child_ds_);
-    findTest(Name("grand.child.example.org"), RRType::DS(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_);
-
-    // There's nothing special for DS query at the zone apex.  It would
-    // normally result in NXRRSET.
-    findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET,
-             true, ConstRRsetPtr());
-}
-
-TEST_F(InMemoryZoneFinderTest, findAny) {
-    EXPECT_NO_THROW(addZoneData(rr_a_));
-    EXPECT_NO_THROW(addZoneData(rr_ns_));
-    EXPECT_NO_THROW(addZoneData(rr_child_glue_));
-
-    vector<ConstRRsetPtr> expected_sets;
-
-    // origin
-    expected_sets.push_back(rr_a_);
-    expected_sets.push_back(rr_ns_);
-    findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
-
-    // out zone name
-    EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
-                             vector<ConstRRsetPtr>()),
-                 OutOfZone);
-
-    expected_sets.clear();
-    expected_sets.push_back(rr_child_glue_);
-    findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
-
-    // add zone cut
-    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
-
-    // zone cut
-    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
-                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
-                NULL, rr_child_ns_);
-
-    // glue for this zone cut
-    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
-                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
-                NULL, rr_child_ns_);
-}
-
-TEST_F(InMemoryZoneFinderTest, glue) {
-    // install zone data:
-    // a zone cut
-    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
-    // glue for this cut
-    EXPECT_NO_THROW(addZoneData(rr_child_glue_));
-    // a nested zone cut (unusual)
-    EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
-    // glue under the deeper zone cut
-    EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
-
-    // by default glue is hidden due to the zone cut
-    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
-             true, rr_child_ns_);
-
-
-    // If we do it in the "glue OK" mode, we should find the exact match.
-    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
-             rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
-             ZoneFinder::FIND_GLUE_OK);
-
-    // glue OK + NXRRSET case
-    findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
-             true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
-             ZoneFinder::FIND_GLUE_OK);
-
-    // glue OK + NXDOMAIN case
-    findTest(Name("www.child.example.org"), RRType::A(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_,
-             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-
-    // nested cut case.  The glue should be found.
-    findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
-             ZoneFinder::SUCCESS,
-             true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
-             ZoneFinder::FIND_GLUE_OK);
-
-    // A non-existent name in nested cut.  This should result in delegation
-    // at the highest zone cut.
-    findTest(Name("www.grand.child.example.org"), RRType::TXT(),
-             ZoneFinder::DELEGATION, true, rr_child_ns_,
-             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-}
-
-/**
- * \brief Test searching.
- *
- * Check it finds or does not find correctly and does not throw exceptions.
- */
-void
-InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
-                                  ZoneFinder::FindOptions find_options)
-{
-    // Fill some data inside
-    // Now put all the data we have there. It should throw nothing
-    EXPECT_NO_THROW(addZoneData(rr_ns_));
-    EXPECT_NO_THROW(addZoneData(rr_ns_a_));
-    EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
-    EXPECT_NO_THROW(addZoneData(rr_a_));
-    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
-        addZoneData(rr_nsec3_);
-        zone_data_->setSigned(true);
-    }
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_nsec_);
-        zone_data_->setSigned(true);
-    }
-
-    // These two should be successful
-    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
-    findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
-             rr_ns_a_);
-
-    // These domains don't exist. (and one is out of the zone).  In an
-    // NSEC-signed zone with DNSSEC records requested, it should return the
-    // covering NSEC for the query name (the actual NSEC in the test data may
-    // not really "cover" it, but for the purpose of this test it's okay).
-    ConstRRsetPtr expected_nsec; // by default it's NULL
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
-        (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
-        expected_nsec = rr_nsec_;
-    }
-
-    // There's no other name between this one and the origin, so when NSEC
-    // is to be returned it should be the origin NSEC.
-    findTest(Name("nothere.example.org"), RRType::A(),
-             ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
-             NULL, find_options);
-
-    // The previous name in the zone is "ns.example.org", but it doesn't
-    // have an NSEC.  It should be skipped and the origin NSEC will be
-    // returned as the "closest NSEC".
-    findTest(Name("nxdomain.example.org"), RRType::A(),
-             ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
-             NULL, find_options);
-    EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
-                 OutOfZone);
-
-    // These domain exist but don't have the provided RRType.  For the latter
-    // one we now add its NSEC (which was delayed so that it wouldn't break
-    // other cases above).
-    findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
-             expected_nsec, expected_flags, NULL, find_options);
-
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_ns_nsec_);
-        zone_data_->setSigned(true);
-        if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
-            expected_nsec = rr_ns_nsec_;
-        }
-    }
-    findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
-             expected_nsec, expected_flags, NULL, find_options);
-}
-
-TEST_F(InMemoryZoneFinderTest, find) {
-    findCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
-    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
-    // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
-    // anything (the NSEC3_SIGNED flag is always set, and no records are
-    // returned for negative cases regardless).
-    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
-    // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
-    findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Generalized test for Empty Nonterminal (ENT) cases with NSEC
-void
-InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
-    ConstRRsetPtr expected_nsec,
-    ZoneFinder::FindResultFlags expected_flags)
-{
-    addZoneData(rr_emptywild_);
-    addZoneData(rr_under_wild_);
-
-    // Sanity check: Should result in NXRRSET
-    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
-             ConstRRsetPtr(), expected_flags);
-    // Sanity check: No NSEC added yet
-    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
-             ConstRRsetPtr(), expected_flags,
-             NULL, ZoneFinder::FIND_DNSSEC);
-
-    // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
-    // there are no sigs)
-    addZoneData(rr_nsec_);
-    addZoneData(rr_ent_nsec2_);
-    addZoneData(rr_ent_nsec3_);
-    addZoneData(rr_ent_nsec4_);
-    zone_data_->setSigned(true);
-
-    // Should result in NXRRSET, and RESULT_NSEC_SIGNED
-    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
-             ConstRRsetPtr(),
-             expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
-
-    // And check for the NSEC if DNSSEC_OK
-    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
-             expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
-             NULL, ZoneFinder::FIND_DNSSEC);
-}
-
-TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
-    // Non-wildcard case
-    findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalWildcard) {
-    // Wildcard case, above actual wildcard
-    findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalAtWildcard) {
-    // Wildcard case, at actual wildcard
-    findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
-                     ZoneFinder::RESULT_WILDCARD);
-}
-
-TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
-    // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
-    findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
-}
-
-void
-InMemoryZoneFinderTest::emptyNodeCheck(
-    ZoneFinder::FindResultFlags expected_flags)
-{
-    /*
-     * The backend RBTree for this test should look like as follows:
-     *          example.org
-     *               |
-     *              baz (empty; easy case)
-     *            /  |  \
-     *          bar  |  x.foo ('foo' part is empty; a bit trickier)
-     *              bbb
-     *             /
-     *           aaa
-     */
-
-    // Construct the test zone
-    const char* const names[] = {
-        "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
-        "bbb.baz.example.org.", NULL};
-    for (int i = 0; names[i] != NULL; ++i) {
-        ConstRRsetPtr rrset = textToRRset(string(names[i]) +
-                                          " 300 IN A 192.0.2.1");
-        addZoneData(rrset);
-    }
-    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
-        addZoneData(rr_nsec3_);
-        zone_data_->setSigned(true);
-    }
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_nsec_);
-        zone_data_->setSigned(true);
-    }
-
-    // empty node matching, easy case: the node for 'baz' exists with
-    // no data.
-    findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
-             ConstRRsetPtr(), expected_flags);
-
-    // empty node matching, a trickier case: the node for 'foo' is part of
-    // "x.foo", which should be considered an empty node.
-    findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
-             ConstRRsetPtr(), expected_flags);
-
-    // "org" is contained in "example.org", but it shouldn't be treated as
-    // NXRRSET because it's out of zone.
-    // Note: basically we don't expect such a query to be performed (the common
-    // operation is to identify the best matching zone first then perform
-    // search it), but we shouldn't be confused even in the unexpected case.
-    EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNode) {
-    emptyNodeCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
-    emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
-    emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-/*
- * Test that puts a (simple) wildcard into the zone and checks we can
- * correctly find the data.
- */
-void
-InMemoryZoneFinderTest::wildcardCheck(
-    ZoneFinder::FindResultFlags expected_flags,
-    ZoneFinder::FindOptions find_options)
-{
-    /*
-     *            example.org.
-     *                 |
-     *             [cname]wild (not *.wild, should have wild mark)
-     *                 |
-     *                 *
-     */
-
-    // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
-    // add RRSIGs to the records.
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
-        (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
-        // Convenience shortcut.  The RDATA is not really validatable, but
-        // it doesn't matter for our tests.
-        const char* const rrsig_common = "5 3 3600 "
-            "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
-
-        find_options = find_options | ZoneFinder::FIND_DNSSEC;
-        rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
-                                       string(rrsig_common)));
-        rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
-                                            "RRSIG CNAME " +
-                                            string(rrsig_common)));
-    }
-    addZoneData(rr_wild_);
-    addZoneData(rr_cnamewild_);
-    // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
-    // (the content of the NSEC3 shouldn't matter)
-    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
-        addZoneData(rr_nsec3_);
-    }
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_nsec_);
-    }
-
-    // Search at the parent. The parent will not have the A, but it will
-    // be in the wildcard (so check the wildcard isn't matched at the parent)
-    {
-        SCOPED_TRACE("Search at parent");
-        if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-            findTest(Name("wild.example.org"), RRType::A(),
-                     ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags,
-                     NULL, find_options);
-        } else {
-            findTest(Name("wild.example.org"), RRType::A(),
-                     ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
-                     expected_flags, NULL, find_options);
-        }
-    }
-
-    // For the test setup of "NSEC-signed" zone, we might expect it will
-    // be returned with a negative result, either because wildcard match is
-    // disabled by the search option or because wildcard match is canceled
-    // per protocol.
-    ConstRRsetPtr expected_nsec; // by default it's NULL
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
-        (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
-        expected_nsec = rr_nsec_;
-    }
-    // Explicitly converting the following to const pointers; some compilers
-    // would complain about mixed use of const and non const in ?: below.
-    const ConstRRsetPtr rr_wild = rr_wild_;
-    const ConstRRsetPtr rr_cnamewild = rr_cnamewild_;
-
-    // Search the original name of wildcard
-    {
-        SCOPED_TRACE("Search directly at *");
-        findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
-                 true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
-                 find_options);
-    }
-
-    // Below some of the test cases will normally result in a wildcard match;
-    // if NO_WILDCARD is specified, it should result in NXDOMAIN instead,
-    // and, when available and requested, the covering NSEC will be returned.
-    // The following are shortcut parameters to unify these cases.
-    const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0);
-    const ZoneFinder::FindResultFlags wild_expected_flags =
-        wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) :
-        expected_flags;
-
-    // Search "created" name.
-    {
-        SCOPED_TRACE("Search at created child");
-        findTest(Name("a.wild.example.org"), RRType::A(),
-                 wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
-                 wild_ok ? rr_wild : expected_nsec,
-                 wild_expected_flags, NULL, find_options, wild_ok);
-    }
-
-    // Search name that has CNAME.
-    {
-        SCOPED_TRACE("Matching CNAME");
-        findTest(Name("a.cnamewild.example.org"), RRType::A(),
-                 wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false,
-                 wild_ok ? rr_cnamewild : expected_nsec,
-                 wild_expected_flags, NULL, find_options, wild_ok);
-    }
-
-    // Search another created name, this time little bit lower
-    {
-        SCOPED_TRACE("Search at created grand-child");
-        findTest(Name("a.b.wild.example.org"), RRType::A(),
-                 wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
-                 wild_ok ? rr_wild : expected_nsec,
-                 wild_expected_flags, NULL, find_options, wild_ok);
-    }
-
-    addZoneData(rr_under_wild_);
-    {
-        SCOPED_TRACE("Search under non-wildcard");
-        findTest(Name("bar.foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
-                 NULL, find_options);
-    }
-
-    // Wildcard match, but no data.  We add the additional NSEC at the wildcard
-    // at this point so that it wouldn't break other tests above.  Note also
-    // that in the NO_WILDCARD case the resulting NSEC is the same.  Ideally
-    // we could use a more tricky setup so we can distinguish these cases,
-    // but for this purpose it's not bad; what we'd like to test here is that
-    // wildcard substitution doesn't happen for either case, and the
-    // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
-    ConstRRsetPtr expected_wild_nsec; // by default it's NULL
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_wild_nsec_);
-        expected_wild_nsec = rr_wild_nsec_;
-    }
-    {
-        SCOPED_TRACE("Search at wildcard, no data");
-        findTest(Name("a.wild.example.org"), RRType::AAAA(),
-                 wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true,
-                 wild_ok ? expected_wild_nsec : expected_wild_nsec,
-                 wild_expected_flags, NULL, find_options);
-    }
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcard) {
-    // Normal case
-    wildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) {
-    // Wildcard is disabled.  In practice, this is used as part of query
-    // processing for an NSEC-signed zone, so we test that case specifically.
-    wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD);
-}
-
-TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
-    // Similar to the previous once, but check the behavior for a non signed
-    // zone just in case.
-    wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD);
-}
-
-/*
- * Test that we don't match a wildcard if we get under delegation.
- * By 4.3.3 of RFC1034:
- * "Wildcard RRs do not apply:
- *   - When the query is in another zone.  That is, delegation cancels
- *     the wildcard defaults."
- */
-TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
-    addZoneData(rr_child_wild_);
-    addZoneData(rr_child_ns_);
-
-    {
-        SCOPED_TRACE("Looking under delegation point");
-        findTest(Name("a.child.example.org"), RRType::A(),
-                 ZoneFinder::DELEGATION, true, rr_child_ns_);
-    }
-
-    {
-        SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
-        findTest(Name("a.child.example.org"), RRType::A(),
-                 ZoneFinder::DELEGATION, true, rr_child_ns_,
-                 ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
-    }
-}
-
-// Tests combination of wildcard and ANY.
-void
-InMemoryZoneFinderTest::anyWildcardCheck(
-    ZoneFinder::FindResultFlags expected_flags)
-{
-    addZoneData(rr_wild_);
-    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
-        addZoneData(rr_nsec3_);
-    }
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_nsec_);
-    }
-
-    vector<ConstRRsetPtr> expected_sets;
-
-    // First try directly the name (normal match)
-    {
-        SCOPED_TRACE("Asking directly for *");
-        expected_sets.push_back(rr_wild_);
-        findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
-                    expected_sets);
-    }
-
-    // Then a wildcard match
-    {
-        SCOPED_TRACE("Asking in the wild way");
-        expected_sets.clear();
-        RRsetPtr expected(new RRset(Name("a.wild.example.org"),
-                                    rr_wild_->getClass(), rr_wild_->getType(),
-                                    rr_wild_->getTTL()));
-        expected->addRdata(rr_wild_->getRdataIterator()->getCurrent());
-        expected_sets.push_back(expected);
-        findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
-                    expected_sets,
-                    ZoneFinder::RESULT_WILDCARD | expected_flags);
-    }
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcard) {
-    anyWildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
-    anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) {
-    anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Test there's nothing in the wildcard in the middle if we load
-// wild.*.foo.example.org.
-void
-InMemoryZoneFinderTest::emptyWildcardCheck(
-    ZoneFinder::FindResultFlags expected_flags)
-{
-    /*
-     *            example.org.
-     *                foo
-     *                 *
-     *               wild
-     */
-    addZoneData(rr_emptywild_);
-    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
-        addZoneData(rr_nsec3_);
-    }
-    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
-        addZoneData(rr_nsec_);
-    }
-
-    {
-        SCOPED_TRACE("Asking for the original record under wildcard");
-        findTest(Name("wild.*.foo.example.org"), RRType::A(),
-                 ZoneFinder::SUCCESS, true, rr_emptywild_);
-    }
-
-    {
-        SCOPED_TRACE("Asking for A record");
-        findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(),
-                 ZoneFinder::RESULT_WILDCARD | expected_flags);
-        findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(), expected_flags);
-        findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(), expected_flags);
-    }
-
-    {
-        SCOPED_TRACE("Asking for ANY record");
-        findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET,
-                    vector<ConstRRsetPtr>(), expected_flags);
-
-        findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET,
-                    vector<ConstRRsetPtr>(),
-                    ZoneFinder::RESULT_WILDCARD | expected_flags);
-    }
-
-    {
-        SCOPED_TRACE("Asking on the non-terminal");
-        findTest(Name("wild.bar.foo.example.org"), RRType::A(),
-                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
-                 ZoneFinder::RESULT_WILDCARD | expected_flags);
-    }
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
-    emptyWildcardCheck();
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
-    emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-}
-
-TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
-    emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-}
-
-// Same as emptyWildcard, but with multiple * in the path.
-TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
-    addZoneData(rr_nested_emptywild_);
-
-    {
-        SCOPED_TRACE("Asking for the original record under wildcards");
-        findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(),
-            ZoneFinder::SUCCESS, true, rr_nested_emptywild_);
-    }
-
-    {
-        SCOPED_TRACE("Matching wildcard against empty nonterminal");
-
-        const char* names[] = {
-            "baz.foo.*.bar.example.org",
-            "baz.foo.baz.bar.example.org",
-            "*.foo.baz.bar.example.org",
-            NULL
-        };
-
-        for (const char** name = names; *name != NULL; ++ name) {
-            SCOPED_TRACE(string("Node ") + *name);
-            findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true,
-                     ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD);
-        }
-    }
-
-    // Domains to test
-    const char* names[] = {
-        "*.foo.*.bar.example.org",
-        "foo.*.bar.example.org",
-        "*.bar.example.org",
-        "bar.example.org",
-        NULL
-    };
-
-    {
-        SCOPED_TRACE("Asking directly for A on parent nodes");
-
-        for (const char** name = names; *name != NULL; ++ name) {
-            SCOPED_TRACE(string("Node ") + *name);
-            findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
-        }
-    }
-
-    {
-        SCOPED_TRACE("Asking for ANY on parent nodes");
-
-        for (const char** name = names; *name != NULL; ++ name) {
-            SCOPED_TRACE(string("Node ") + *name);
-
-            findAllTest(Name(*name), ZoneFinder::NXRRSET,
-                        vector<ConstRRsetPtr>());
-        }
-    }
-}
-
-// We run this part twice from the below test, in two slightly different
-// situations
-void
-InMemoryZoneFinderTest::doCancelWildcardCheck(
-    ZoneFinder::FindResultFlags expected_flags,
-    ZoneFinder::FindOptions find_options)
-{
-    // These should be canceled
-    {
-        SCOPED_TRACE("Canceled under foo.wild.example.org");
-
-        // For an NSEC-signed zone with DNSSEC requested, the covering NSEC
-        // should be returned.  The expected NSEC is actually just the only
-        // NSEC in the test data, but in this context it doesn't matter;
-        // it's sufficient just to check any NSEC is returned (or not).
-        ConstRRsetPtr expected_nsec; // by default it's NULL
-        if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
-            (find_options & ZoneFinder::FIND_DNSSEC)) {
-            expected_nsec = rr_nsec_;
-        }
-
-        findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
-                 NULL, find_options);
-        findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
-                 NULL, find_options);
-    }
-
-    // This is existing, non-wildcard domain, shouldn't wildcard at all
-    {
-        SCOPED_TRACE("Existing domain under foo.wild.example.org");
-        findTest(Name("bar.foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::SUCCESS, true, rr_not_wild_);
-    }
-
-    // These should be caught by the wildcard
-    {
-        SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org");
-
-        const char* names[] = {
-            "aaa.bbb.wild.example.org",
-            "aaa.zzz.wild.example.org",
-            "zzz.wild.example.org",
-            NULL
-        };
-
-        for (const char** name = names; *name != NULL; ++ name) {
-            SCOPED_TRACE(string("Node ") + *name);
-
-            findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
-                     rr_wild_,
-                     ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                     ZoneFinder::FIND_DEFAULT, true);
-        }
-    }
-
-    // This shouldn't be wildcarded, it's an existing domain
-    {
-        SCOPED_TRACE("The foo.wild.example.org itself");
-        findTest(Name("foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags);
-    }
-}
-
-/*
- * This tests that if there's a name between the wildcard domain and the
- * searched one, it will not trigger wildcard, for example, if we have
- * *.wild.example.org and bar.foo.wild.example.org, then we know
- * foo.wild.example.org exists and is not wildcard. Therefore, search for
- * aaa.foo.wild.example.org should return NXDOMAIN.
- *
- * Tests few cases "around" the canceled wildcard match, to see something that
- * shouldn't be canceled isn't.
- */
-TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
-    addZoneData(rr_wild_);
-    addZoneData(rr_not_wild_);
-
-    {
-        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
-        doCancelWildcardCheck();
-    }
-
-    // Try putting another one under foo.wild....
-    // The result should be the same but it will be done in another way in the
-    // code, because the foo.wild.example.org will exist in the tree.
-    addZoneData(rr_not_wild_another_);
-    {
-        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
-        doCancelWildcardCheck();
-    }
-}
-
-// Same tests as cancelWildcard for NSEC3-signed zone
-TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
-    addZoneData(rr_wild_);
-    addZoneData(rr_not_wild_);
-    addZoneData(rr_nsec3_);
-
-    {
-        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
-        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-    }
-    addZoneData(rr_not_wild_another_);
-    {
-        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
-        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
-    }
-}
-
-// Same tests as cancelWildcard for NSEC-signed zone.  Check both cases with
-// or without FIND_DNSSEC option.  NSEC should be returned only when the option
-// is given.
-TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
-    addZoneData(rr_wild_);
-    addZoneData(rr_not_wild_);
-    addZoneData(rr_nsec_);
-
-    {
-        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
-        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
-                              ZoneFinder::FIND_DNSSEC);
-        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-    }
-    addZoneData(rr_not_wild_another_);
-    {
-        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
-        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
-                              ZoneFinder::FIND_DNSSEC);
-        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
-    }
-}
-
-
-// DISABLED: nsec3 will be re-added in #2118
-TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3) {
-    // Set up the faked hash calculator.
-    setNSEC3HashCreator(&nsec3_hash_creator_);
-
-    // Add a few NSEC3 records:
-    // apex (example.org.): hash=0P..
-    // ns1.example.org:     hash=2T..
-    // w.example.org:       hash=01..
-    // zzz.example.org:     hash=R5..
-    const string apex_nsec3_text = string(apex_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(apex_nsec3_text));
-    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(ns1_nsec3_text));
-    const string w_nsec3_text = string(w_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(w_nsec3_text));
-    const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(zzz_nsec3_text));
-
-    performNSEC3Test(zone_finder_);
-}
-
-// DISABLED: NSEC3 will be re-added in #2218
-TEST_F(InMemoryZoneFinderTest, DISABLED_findNSEC3ForBadZone) {
-    // Set up the faked hash calculator.
-    setNSEC3HashCreator(&nsec3_hash_creator_);
-
-    // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
-    // findNSEC3() should be rejected.
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
-                 DataSourceError);
-
-    // Only having NSEC3PARAM isn't enough
-    addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
-                            "1 0 12 aabbccdd"));
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
-                 DataSourceError);
-
-    // Unless NSEC3 for apex is added the result in the recursive mode
-    // is guaranteed.
-    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
-        string(nsec3_common);
-    addZoneData(textToRRset(ns1_nsec3_text));
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
-                 DataSourceError);
-}
-
-}
diff --git a/src/lib/datasrc/memory/tests/zone_table_unittest.cc b/src/lib/datasrc/memory/tests/zone_table_unittest.cc
deleted file mode 100644
index 359df49..0000000
--- a/src/lib/datasrc/memory/tests/zone_table_unittest.cc
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-
-#include <util/memory_segment_local.h>
-
-#include <dns/name.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/result.h>
-#include <datasrc/memory/zone_data.h>
-#include <datasrc/memory/zone_table.h>
-
-#include <gtest/gtest.h>
-
-#include <new>                  // for bad_alloc
-
-using namespace isc::dns;
-using namespace isc::datasrc;
-using namespace isc::datasrc::memory;
-
-namespace {
-// Memory segment specified for tests.  It normally behaves like a "local"
-// memory segment.  If "throw count" is set to non 0 via setThrowCount(),
-// it continues the normal behavior up to the specified number of calls to
-// allocate(), and throws an exception at the next call.
-class TestMemorySegment : public isc::util::MemorySegmentLocal {
-public:
-    TestMemorySegment() : throw_count_(0) {}
-    virtual void* allocate(size_t size) {
-        if (throw_count_ > 0) {
-            if (--throw_count_ == 0) {
-                throw std::bad_alloc();
-            }
-        }
-        return (isc::util::MemorySegmentLocal::allocate(size));
-    }
-    void setThrowCount(size_t count) { throw_count_ = count; }
-
-private:
-    size_t throw_count_;
-};
-
-class ZoneTableTest : public ::testing::Test {
-protected:
-    ZoneTableTest() : zclass_(RRClass::IN()),
-                      zname1(Name("example.com")),
-                      zname2(Name("example.net")),
-                      zname3(Name("example")),
-                      zone_table(ZoneTable::create(mem_sgmt_, zclass_))
-    {}
-    ~ZoneTableTest() {
-        if (zone_table != NULL) {
-            ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
-        }
-    }
-    void TearDown() {
-        ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
-        zone_table = NULL;
-        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
-    }
-    const RRClass zclass_;
-    const Name zname1, zname2, zname3;
-    TestMemorySegment mem_sgmt_;
-    ZoneTable* zone_table;
-};
-
-TEST_F(ZoneTableTest, create) {
-    // Test about creating a zone table.  Normal case covers through other
-    // tests.  We only check exception safety by letting the test memory
-    // segment throw.
-    mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
-    // This shouldn't cause memory leak (that would be caught in TearDown()).
-}
-
-TEST_F(ZoneTableTest, addZone) {
-    // Normal successful case.
-    const ZoneTable::AddResult result1 =
-        zone_table->addZone(mem_sgmt_, zclass_, zname1);
-    EXPECT_EQ(result::SUCCESS, result1.code);
-
-    // Duplicate add doesn't replace the existing data.
-    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
-                                                 zname1).code);
-    EXPECT_EQ(result1.zone_data,
-              zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
-    // names are compared in a case insensitive manner.
-    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
-                                                 Name("EXAMPLE.COM")).code);
-    // Add some more different ones.  Should just succeed.
-    EXPECT_EQ(result::SUCCESS,
-              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
-    EXPECT_EQ(result::SUCCESS,
-              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
-
-    // Have the memory segment throw an exception in extending the internal
-    // tree.  It still shouldn't cause memory leak (which would be detected
-    // in TearDown()).
-    mem_sgmt_.setThrowCount(2);
-    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
-                 std::bad_alloc);
-}
-
-TEST_F(ZoneTableTest, findZone) {
-    const ZoneTable::AddResult add_result1 =
-        zone_table->addZone(mem_sgmt_, zclass_, zname1);
-    EXPECT_EQ(result::SUCCESS, add_result1.code);
-    EXPECT_EQ(result::SUCCESS,
-              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
-    EXPECT_EQ(result::SUCCESS,
-              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
-
-    const ZoneTable::FindResult find_result1 =
-        zone_table->findZone(Name("example.com"));
-    EXPECT_EQ(result::SUCCESS, find_result1.code);
-    EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
-
-    EXPECT_EQ(result::NOTFOUND,
-              zone_table->findZone(Name("example.org")).code);
-    EXPECT_EQ(static_cast<ZoneData*>(NULL),
-              zone_table->findZone(Name("example.org")).zone_data);
-
-    // there's no exact match.  the result should be the longest match,
-    // and the code should be PARTIALMATCH.
-    EXPECT_EQ(result::PARTIALMATCH,
-              zone_table->findZone(Name("www.example.com")).code);
-    EXPECT_EQ(add_result1.zone_data,
-              zone_table->findZone(Name("www.example.com")).zone_data);
-
-    // make sure the partial match is indeed the longest match by adding
-    // a zone with a shorter origin and query again.
-    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
-                                                   Name("com")).code);
-    EXPECT_EQ(add_result1.zone_data,
-              zone_table->findZone(Name("www.example.com")).zone_data);
-}
-}
diff --git a/src/lib/datasrc/memory/treenode_rrset.cc b/src/lib/datasrc/memory/treenode_rrset.cc
index fb7cafc..f51b2f9 100644
--- a/src/lib/datasrc/memory/treenode_rrset.cc
+++ b/src/lib/datasrc/memory/treenode_rrset.cc
@@ -79,10 +79,6 @@ TreeNodeRRset::setTTL(const RRTTL&) {
 
 std::string
 TreeNodeRRset::toText() const {
-    // Create TTL from internal data
-    util::InputBuffer ttl_buffer(rdataset_->getTTLData(), sizeof(uint32_t));
-    const RRTTL ttl(ttl_buffer);
-
     // Dump the main RRset, if not empty
     std::string ret;
     RRsetPtr tmp_rrset;
@@ -92,7 +88,7 @@ TreeNodeRRset::toText() const {
     {
         if (!tmp_rrset) {
             tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_, getType(),
-                                           ttl));
+                                           getTTL()));
         }
         tmp_rrset->addRdata(rit->getCurrent());
     }
@@ -101,17 +97,7 @@ TreeNodeRRset::toText() const {
     }
 
     // Dump any RRSIGs
-    tmp_rrset.reset();
-    for (RdataIteratorPtr rit = getSigRdataIterator();
-         !rit->isLast();
-         rit->next())
-    {
-        if (!tmp_rrset) {
-            tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
-                                           RRType::RRSIG(), ttl));
-        }
-        tmp_rrset->addRdata(rit->getCurrent());
-    }
+    tmp_rrset = getRRsig();
     if (tmp_rrset) {
         ret += tmp_rrset->toText();
     }
@@ -292,7 +278,26 @@ TreeNodeRRset::getSigRdataIterator() const {
 
 RRsetPtr
 TreeNodeRRset::getRRsig() const {
-    isc_throw(Unexpected, "unexpected method called on TreeNodeRRset");
+    // Shortcut: if DNSSEC is disabled for the RRset, simply return NULL.
+    // The generic code below provides the same behavior, but with much more
+    // overhead.
+    if (!dnssec_ok_) {
+        return (RRsetPtr());
+    }
+
+    RRsetPtr tmp_rrset;
+    for (RdataIteratorPtr rit = getSigRdataIterator();
+         !rit->isLast();
+         rit->next())
+    {
+        if (!tmp_rrset) {
+            tmp_rrset = RRsetPtr(new RRset(getName(), rrclass_,
+                                           RRType::RRSIG(), getTTL()));
+        }
+        tmp_rrset->addRdata(rit->getCurrent());
+    }
+
+    return (tmp_rrset);
 }
 
 void
diff --git a/src/lib/datasrc/memory/treenode_rrset.h b/src/lib/datasrc/memory/treenode_rrset.h
index a5f48bf..460a704 100644
--- a/src/lib/datasrc/memory/treenode_rrset.h
+++ b/src/lib/datasrc/memory/treenode_rrset.h
@@ -190,8 +190,6 @@ public:
     virtual dns::RdataIteratorPtr getRdataIterator() const;
 
     /// \brief Specialized version of \c getRRsig() for \c TreeNodeRRset.
-    ///
-    /// It throws \c isc::Unexpected unconditionally.
     virtual dns::RRsetPtr getRRsig() const;
 
     virtual unsigned int getRRsigDataCount() const {
diff --git a/src/lib/datasrc/memory/zone_finder.cc b/src/lib/datasrc/memory/zone_finder.cc
index 75d3187..86dd874 100644
--- a/src/lib/datasrc/memory/zone_finder.cc
+++ b/src/lib/datasrc/memory/zone_finder.cc
@@ -15,6 +15,7 @@
 #include <datasrc/memory/zone_finder.h>
 #include <datasrc/memory/domaintree.h>
 #include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdata_serialization.h>
 
 #include <datasrc/zone.h>
 #include <datasrc/data_source.h>
@@ -22,9 +23,18 @@
 #include <dns/name.h>
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
+#include <dns/nsec3hash.h>
 
 #include <datasrc/logger.h>
 
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include <algorithm>
+#include <vector>
+
+#include <utility>
+
 using namespace isc::dns;
 using namespace isc::datasrc::memory;
 using namespace isc::datasrc;
@@ -33,7 +43,49 @@ namespace isc {
 namespace datasrc {
 namespace memory {
 
+namespace internal {
+
+// Specialized version of ZoneFinder::ResultContext, which  holds objects
+// related to find() results using internal representations of the in-memory
+// data source implementation.
+class ZoneFinderResultContext {
+public:
+    /// \brief Constructor
+    ///
+    /// The first three parameters correspond to those of
+    /// ZoneFinder::ResultContext.  If node is non NULL, it specifies the
+    /// found ZoneNode in the search.
+    ZoneFinderResultContext(ZoneFinder::Result code_param,
+                            TreeNodeRRsetPtr rrset_param,
+                            ZoneFinder::FindResultFlags flags_param,
+                            const ZoneData& zone_data_param,
+                            const ZoneNode* node, const RdataSet* rdset) :
+        code(code_param), rrset(rrset_param), flags(flags_param),
+        zone_data(&zone_data_param), found_node(node), found_rdset(rdset)
+    {}
+
+    const ZoneFinder::Result code;
+    const TreeNodeRRsetPtr rrset;
+    const ZoneFinder::FindResultFlags flags;
+    const ZoneData* const zone_data;
+    const ZoneNode* const found_node;
+    const RdataSet* const found_rdset;
+};
+}
+using internal::ZoneFinderResultContext;
+
 namespace {
+/// Conceptual RRset in the form of a pair of zone node and RdataSet.
+///
+/// In this implementation, the owner name of an RRset is derived from the
+/// corresponding zone node, and the rest of the attributes come from
+/// an RdataSet.  This shortcut type can be used when we want to refer to
+/// the conceptual RRset without knowing these details.
+///
+/// This is a read-only version of the pair (and at the moment we don't need
+/// a mutable version).
+typedef std::pair<const ZoneNode*, const RdataSet*> ConstNodeRRset;
+
 /// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
 /// the given RRClass
 ///
@@ -51,18 +103,21 @@ TreeNodeRRsetPtr
 createTreeNodeRRset(const ZoneNode* node,
                     const RdataSet* rdataset,
                     const RRClass& rrclass,
+                    ZoneFinder::FindOptions options,
                     const Name* realname = NULL)
 {
+    const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
     if (node != NULL && rdataset != NULL) {
         if (realname != NULL) {
-            return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node,
-                                                      rdataset, true));
+            return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
+                                                       node, rdataset,
+                                                       dnssec)));
         } else {
-            return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node,
-                                                      rdataset, true));
+            return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
+                                                       dnssec)));
         }
     } else {
-        return TreeNodeRRsetPtr();
+        return (TreeNodeRRsetPtr());
     }
 }
 
@@ -75,7 +130,7 @@ struct FindState {
     FindState(bool glue_ok) :
         zonecut_node_(NULL),
         dname_node_(NULL),
-        rrset_(NULL),
+        rdataset_(NULL),
         glue_ok_(glue_ok)
     {}
 
@@ -87,7 +142,7 @@ struct FindState {
     const ZoneNode* dname_node_;
 
     // Delegation RRset (NS or DNAME), if found.
-    const RdataSet* rrset_;
+    const RdataSet* rdataset_;
 
     // Whether to continue search below a delegation point.
     // Set at construction time.
@@ -95,7 +150,7 @@ struct FindState {
 };
 
 // A callback called from possible zone cut nodes and nodes with DNAME.
-// This will be passed from findNode() to \c RBTree::find().
+// This will be passed from findNode() to \c ZoneTree::find().
 bool cutCallback(const ZoneNode& node, FindState* state) {
     // We need to look for DNAME first, there's allowed case where
     // DNAME and NS coexist in the apex. DNAME is the one to notice,
@@ -107,7 +162,7 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
     if (found_dname != NULL) {
         LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
         state->dname_node_ = &node;
-        state->rrset_ = found_dname;
+        state->rdataset_ = found_dname;
         return (true);
     }
 
@@ -129,7 +184,7 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
         // It cannot happen for us (at least for now), so we don't do
         // that check.
         state->zonecut_node_ = &node;
-        state->rrset_ = found_ns;
+        state->rdataset_ = found_ns;
 
         // Unless glue is allowed the search stops here, so we return
         // false; otherwise return true to continue the search.
@@ -145,6 +200,27 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
     return (false);
 }
 
+/// Creates a NSEC3 ConstRRsetPtr for the given ZoneNode inside the
+/// NSEC3 tree, for the given RRClass.
+///
+/// It asserts that the node contains data (RdataSet) and is of type
+/// NSEC3.
+///
+/// \param node The ZoneNode inside the NSEC3 tree
+/// \param rrclass The RRClass as passed by the client
+ConstRRsetPtr
+createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
+     const RdataSet* rdataset = node->getData();
+     // Only NSEC3 ZoneNodes are allowed to be passed to this method. We
+     // assert that these have data, and also are of type NSEC3.
+     assert(rdataset != NULL);
+     assert(rdataset->type == RRType::NSEC3());
+
+    // Create the RRset.  Note the DNSSEC flag: NSEC3 implies DNSSEC.
+    return (createTreeNodeRRset(node, rdataset, rrclass,
+                                ZoneFinder::FIND_DNSSEC));
+}
+
 // convenience function to fill in the final details
 //
 // Set up ZoneFinderResultContext object as a return value of find(),
@@ -158,14 +234,16 @@ bool cutCallback(const ZoneNode& node, FindState* state) {
 // if wild is true, the RESULT_WILDCARD flag will be set.
 // If qname is not NULL, this is the query name, to be used in wildcard
 // substitution instead of the Node's name).
-isc::datasrc::memory::ZoneFinderResultContext
+ZoneFinderResultContext
 createFindResult(const RRClass& rrclass,
                  const ZoneData& zone_data,
                  ZoneFinder::Result code,
-                 const RdataSet* rrset,
                  const ZoneNode* node,
+                 const RdataSet* rdataset,
+                 ZoneFinder::FindOptions options,
                  bool wild = false,
-                 const Name* qname = NULL) {
+                 const Name* qname = NULL)
+{
     ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
     const Name* rename = NULL;
 
@@ -182,9 +260,10 @@ createFindResult(const RRClass& rrclass,
         }
     }
 
-    return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset,
-                                                              rrclass, rename),
-                                    flags, node));
+    return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdataset,
+                                                              rrclass, options,
+                                                              rename),
+                                    flags, zone_data, node, rdataset));
 }
 
 // A helper function for NSEC-signed zones.  It searches the zone for
@@ -198,7 +277,7 @@ createFindResult(const RRClass& rrclass,
 // it should always succeed.
 //
 // node_path must store valid search context (in practice, it's expected
-// to be set by findNode()); otherwise the underlying RBTree implementation
+// to be set by findNode()); otherwise the underlying ZoneTree implementation
 // throws.
 //
 // If the zone is not considered NSEC-signed or DNSSEC records were not
@@ -206,16 +285,15 @@ createFindResult(const RRClass& rrclass,
 // method doesn't bother to find NSEC, and simply returns NULL.  So, by
 // definition of "NSEC-signed", when it really tries to find an NSEC it
 // should succeed; there should be one at least at the zone origin.
-const RdataSet*
+ConstNodeRRset
 getClosestNSEC(const ZoneData& zone_data,
                ZoneChain& node_path,
-               const ZoneNode** nsec_node,
                ZoneFinder::FindOptions options)
 {
     if (!zone_data.isSigned() ||
         (options & ZoneFinder::FIND_DNSSEC) == 0 ||
         zone_data.isNSEC3Signed()) {
-        return (NULL);
+        return (ConstNodeRRset(NULL, NULL));
     }
 
     const ZoneNode* prev_node;
@@ -225,8 +303,7 @@ getClosestNSEC(const ZoneData& zone_data,
             const RdataSet* found =
                 RdataSet::find(prev_node->getData(), RRType::NSEC());
             if (found != NULL) {
-                *nsec_node = prev_node;
-                return (found);
+                return (ConstNodeRRset(prev_node, found));
             }
         }
     }
@@ -235,7 +312,7 @@ getClosestNSEC(const ZoneData& zone_data,
     assert(false);
     // Even though there is an assert here, strict compilers
     // will still need some return value.
-    return (NULL);
+    return (ConstNodeRRset(NULL, NULL));
 }
 
 // A helper function for the NXRRSET case in find().  If the zone is
@@ -272,16 +349,16 @@ public:
 
     FindNodeResult(ZoneFinder::Result code_param,
                    const ZoneNode* node_param,
-                   const RdataSet* rrset_param,
+                   const RdataSet* rdataset_param,
                    unsigned int flags_param = 0) :
         code(code_param),
         node(node_param),
-        rrset(rrset_param),
+        rdataset(rdataset_param),
         flags(flags_param)
     {}
     const ZoneFinder::Result code;
     const ZoneNode* node;
-    const RdataSet* rrset;
+    const RdataSet* rdataset;
     const unsigned int flags;
 };
 
@@ -341,23 +418,26 @@ public:
 // caught above.
 //
 // If none of the above succeeds, we conclude the name doesn't exist in
-// the zone, and throw an OutOfZone exception.
+// the zone, and throw an OutOfZone exception by default.  If the optional
+// out_of_zone_ok is true, it returns an NXDOMAIN result with NULL data so
+// the caller can take an action to it (technically it's not "NXDOMAIN",
+// but the caller is assumed not to rely on the difference.)
 FindNodeResult findNode(const ZoneData& zone_data,
-                        const Name& name,
+                        const LabelSequence& name_labels,
                         ZoneChain& node_path,
-                        ZoneFinder::FindOptions options)
+                        ZoneFinder::FindOptions options,
+                        bool out_of_zone_ok = false)
 {
     ZoneNode* node = NULL;
     FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
 
     const ZoneTree& tree(zone_data.getZoneTree());
-    ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
-                                        &node, node_path, cutCallback,
-                                        &state);
+    const ZoneTree::Result result = tree.find(name_labels, &node, node_path,
+                                              cutCallback, &state);
     const unsigned int zonecut_flag =
         (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
     if (result == ZoneTree::EXACTMATCH) {
-        return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
+        return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rdataset_,
                                zonecut_flag));
     } else if (result == ZoneTree::PARTIALMATCH) {
         assert(node != NULL);
@@ -365,26 +445,23 @@ FindNodeResult findNode(const ZoneData& zone_data,
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
                 arg(state.dname_node_->getName());
             return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_,
-                                   state.rrset_));
+                                   state.rdataset_));
         }
         if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
                 arg(state.zonecut_node_->getName());
             return (FindNodeResult(ZoneFinder::DELEGATION,
                                    state.zonecut_node_,
-                                   state.rrset_));
+                                   state.rdataset_));
         }
         if (node_path.getLastComparisonResult().getRelation() ==
             NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
             LOG_DEBUG(logger, DBG_TRACE_DATA,
-                      DATASRC_MEM_SUPER_STOP).arg(name);
-            const ZoneNode* nsec_node;
-            const RdataSet* nsec_rds = getClosestNSEC(zone_data,
-                                                      node_path,
-                                                      &nsec_node,
-                                                      options);
-            return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node,
-                                   nsec_rds));
+                      DATASRC_MEM_SUPER_STOP).arg(name_labels);
+            ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data, node_path,
+                                                       options);
+            return (FindNodeResult(ZoneFinder::NXRRSET, nsec_rrset.first,
+                                   nsec_rrset.second));
         }
         // Nothing really matched.
 
@@ -398,14 +475,12 @@ FindNodeResult findNode(const ZoneData& zone_data,
                 // baz.foo.wild.example. The common ancestor, foo.wild.example,
                 // should cancel wildcard.  Treat it as NXDOMAIN.
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
-                          DATASRC_MEM_WILDCARD_CANCEL).arg(name);
-                    const ZoneNode* nsec_node;
-                    const RdataSet* nsec_rds = getClosestNSEC(zone_data,
-                                                              node_path,
-                                                              &nsec_node,
-                                                              options);
-                    return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node,
-                                           nsec_rds));
+                          DATASRC_MEM_WILDCARD_CANCEL).arg(name_labels);
+                ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data,
+                                                           node_path,
+                                                           options);
+                return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_rrset.first,
+                                       nsec_rrset.second));
             }
             uint8_t ls_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
 
@@ -421,63 +496,217 @@ FindNodeResult findNode(const ZoneData& zone_data,
             // Clear the node_path so that we don't keep incorrect (NSEC)
             // context
             node_path.clear();
-            ZoneTree::Result result = tree.find(LabelSequence(wildcard_ls),
-                                                &node, node_path, cutCallback,
-                                                &state);
+            ZoneTree::Result result = tree.find(wildcard_ls, &node, node_path,
+                                                cutCallback, &state);
             // Otherwise, why would the domain_flag::WILD be there if
             // there was no wildcard under it?
             assert(result == ZoneTree::EXACTMATCH);
-            return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
+            return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rdataset_,
                         FindNodeResult::FIND_WILDCARD | zonecut_flag));
         }
 
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
-        const ZoneNode* nsec_node;
-        const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
-                                                  &nsec_node, options);
-        return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
+            arg(name_labels);
+        ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data, node_path,
+                                                   options);
+        return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_rrset.first,
+                               nsec_rrset.second));
     } else {
         // If the name is neither an exact or partial match, it is
-        // out of bailiwick, which is considered an error.
-        isc_throw(OutOfZone, name.toText() << " not in " <<
+        // out of bailiwick, which is considered an error, unless the caller
+        // is willing to accept it.
+        if (out_of_zone_ok) {
+            return (FindNodeResult(ZoneFinder::NXDOMAIN, NULL, NULL));
+        }
+        isc_throw(OutOfZone, name_labels << " not in " <<
                              zone_data.getOriginNode()->getName());
     }
 }
 
 } // end anonymous namespace
 
-// Specialization of the ZoneFinder::Context for the in-memory finder.
+
+/// \brief Specialization of the ZoneFinder::Context for the in-memory finder.
+///
+/// Note that we don't have a specific constructor for the findAll() case.
+/// For (successful) type ANY query, found_node points to the
+/// corresponding zone node, which is recorded within this specialized
+/// context.
 class InMemoryZoneFinder::Context : public ZoneFinder::Context {
 public:
-    /// \brief Constructor.
-    ///
-    /// Note that we don't have a specific constructor for the findAll() case.
-    /// For (successful) type ANY query, found_node points to the
-    /// corresponding RB node, which is recorded within this specialized
-    /// context.
-    Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
-            ZoneFinderResultContext result) :
-        ZoneFinder::Context(finder, options,
-                            ResultContext(result.code, result.rrset,
-                                          result.flags)),
-        rrset_(result.rrset), found_node_(result.found_node)
+    Context(InMemoryZoneFinder& finder, ZoneFinder::FindOptions options,
+            const RRClass& rrclass, const ZoneFinderResultContext& result) :
+        ZoneFinder::Context(options, ResultContext(result.code, result.rrset,
+                                                   result.flags)),
+        finder_(finder), // NOTE: when entire #2283 is done we won't need this
+        rrclass_(rrclass), zone_data_(result.zone_data),
+        found_node_(result.found_node),
+        found_rdset_(result.found_rdset)
     {}
 
+protected:
+    // When all tickets in #2283 are done this can simply return NULL.
+    virtual ZoneFinder* getFinder() { return (&finder_); }
+
+    // We don't use the default protected methods that rely on this method,
+    // so we can simply return NULL.
+    virtual const std::vector<isc::dns::ConstRRsetPtr>* getAllRRsets() const {
+        return (NULL);
+    }
+
+    virtual void getAdditionalImpl(const std::vector<RRType>& requested_types,
+                                   std::vector<ConstRRsetPtr>& result)
+    {
+        if (found_rdset_ != NULL) {
+            // Normal query with successful result.
+            getAdditionalForRdataset(found_rdset_, requested_types, result,
+                                     options_);
+        } else if (found_node_ != NULL) {
+            // Successful type ANY query result.  Call
+            // getAdditionalForRdataset for each RdataSet of the node.
+            for (const RdataSet* rdset = found_node_->getData();
+                 rdset != NULL;
+                 rdset = rdset->getNext())
+            {
+                getAdditionalForRdataset(rdset, requested_types, result,
+                                         options_);
+            }
+        }
+    }
+
 private:
-    const TreeNodeRRsetPtr rrset_;
+    // Main subroutine of getAdditionalImpl, iterate over Rdata fields
+    // find, create, and insert necessary additional RRsets.
+    void
+    getAdditionalForRdataset(const RdataSet* rdset,
+                             const std::vector<RRType>& requested_types,
+                             std::vector<ConstRRsetPtr>& result,
+                             ZoneFinder::FindOptions orig_options) const
+    {
+        ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+        if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+            options = options | ZoneFinder::FIND_DNSSEC;
+        }
+        if (rdset->type == RRType::NS()) {
+            options = options | ZoneFinder::FIND_GLUE_OK;
+        }
+
+        RdataReader(rrclass_, rdset->type, rdset->getDataBuf(),
+                    rdset->getRdataCount(), rdset->getSigRdataCount(),
+                    boost::bind(&Context::findAdditional, this,
+                                &requested_types, &result, options, _1, _2),
+                    &RdataReader::emptyDataAction).iterate();
+    }
+
+    // RdataReader callback for additional section processing.
+    void
+    findAdditional(const std::vector<RRType>* requested_types,
+                   std::vector<ConstRRsetPtr>* result,
+                   ZoneFinder::FindOptions options,
+                   const LabelSequence& name_labels,
+                   RdataNameAttributes attr) const;
+
+    // Subroutine for findAdditional() to unify the normal and wildcard match
+    // cases.
+    void
+    findAdditionalHelper(const std::vector<RRType>* requested_types,
+                         std::vector<ConstRRsetPtr>* result,
+                         const ZoneNode* node,
+                         ZoneFinder::FindOptions options,
+                         const Name* real_name) const
+    {
+        const std::vector<RRType>::const_iterator type_beg =
+            requested_types->begin();
+        const std::vector<RRType>::const_iterator type_end =
+            requested_types->end();
+        for (const RdataSet* rdset = node->getData();
+             rdset != NULL;
+             rdset = rdset->getNext())
+        {
+            // Checking all types for all RdataSets could be suboptimal.
+            // This can be a bit more optimized, but unless we have many
+            // requested types the effect is probably marginal.  For now we
+            // keep it simple.
+            if (std::find(type_beg, type_end, rdset->type) != type_end) {
+                result->push_back(createTreeNodeRRset(node, rdset, rrclass_,
+                                                      options, real_name));
+            }
+        }
+    }
+
+private:
+    InMemoryZoneFinder& finder_;
+    const RRClass rrclass_;
+    const ZoneData* const zone_data_;
     const ZoneNode* const found_node_;
+    const RdataSet* const found_rdset_;
 };
 
+void
+InMemoryZoneFinder::Context::findAdditional(
+    const std::vector<RRType>* requested_types,
+    std::vector<ConstRRsetPtr>* result,
+    ZoneFinder::FindOptions options,
+    const LabelSequence& name_labels,
+    RdataNameAttributes attr) const
+{
+    // Ignore name data that don't need additional processing.
+    if ((attr & NAMEATTR_ADDITIONAL) == 0) {
+        return;
+    }
+
+    // Find the zone node for the additional name.  By passing true as the
+    // last parameter of findNode() we ignore out-of-zone names.
+    ZoneChain node_path;
+    const FindNodeResult node_result =
+        findNode(*zone_data_, name_labels, node_path, options, true);
+    // we only need non-empty exact match
+    if (node_result.code != SUCCESS) {
+        return;
+    }
+
+    // Ignore data at a zone cut (due to subdomain delegation) unless glue is
+    // allowed.  Checking the node callback flag is a cheap way to detect
+    // zone cuts, but it includes DNAME delegation, in which case we should
+    // keep finding the additional records regardless of the 'GLUE_OK' flag.
+    // The last two conditions limit the case to delegation NS, i.e, the node
+    // has an NS and it's not the zone origin.
+    const ZoneNode* node = node_result.node;
+    if ((options & ZoneFinder::FIND_GLUE_OK) == 0 &&
+        node->getFlag(ZoneNode::FLAG_CALLBACK) &&
+        node != zone_data_->getOriginNode() &&
+        RdataSet::find(node->getData(), RRType::NS()) != NULL) {
+        return;
+    }
+
+    // Examine RdataSets of the node, and create and insert requested types
+    // of RRsets as we find them.
+    if ((node_result.flags & FindNodeResult::FIND_WILDCARD) == 0) {
+        // normal case
+        findAdditionalHelper(requested_types, result, node, options, NULL);
+    } else {
+        // if the additional name is subject to wildcard substitution, we need
+        // to create a name object for the "real" (after substitution) name.
+        // This is expensive, but in the additional processing this should be
+        // very rare cases and acceptable.
+        size_t data_len;
+        const uint8_t* data;
+        data = name_labels.getData(&data_len);
+        util::InputBuffer buffer(data, data_len);
+        const Name real_name(buffer);
+        findAdditionalHelper(requested_types, result, node, options,
+                             &real_name);
+    }
+}
+
 boost::shared_ptr<ZoneFinder::Context>
 InMemoryZoneFinder::find(const isc::dns::Name& name,
                 const isc::dns::RRType& type,
                 const FindOptions options)
 {
-    return ZoneFinderContextPtr(new Context(*this, options,
-                                            find_internal(name,
-                                                          type,
-                                                          NULL,
-                                                          options)));
+    return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
+                                             findInternal(name, type,
+                                                          NULL, options))));
 }
 
 boost::shared_ptr<ZoneFinder::Context>
@@ -485,27 +714,28 @@ InMemoryZoneFinder::findAll(const isc::dns::Name& name,
         std::vector<isc::dns::ConstRRsetPtr>& target,
         const FindOptions options)
 {
-    return ZoneFinderContextPtr(new Context(*this, options,
-                                            find_internal(name,
+    return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
+                                             findInternal(name,
                                                           RRType::ANY(),
                                                           &target,
-                                                          options)));
+                                                          options))));
 }
 
 ZoneFinderResultContext
-InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
-                                  const isc::dns::RRType& type,
-                                  std::vector<ConstRRsetPtr>* target,
-                                  const FindOptions options)
+InMemoryZoneFinder::findInternal(const isc::dns::Name& name,
+                                 const isc::dns::RRType& type,
+                                 std::vector<ConstRRsetPtr>* target,
+                                 const FindOptions options)
 {
     // Get the node.  All other cases than an exact match are handled
     // in findNode().  We simply construct a result structure and return.
     ZoneChain node_path;
     const FindNodeResult node_result =
-        findNode(zone_data_, name, node_path, options);
+        findNode(zone_data_, LabelSequence(name), node_path, options);
     if (node_result.code != SUCCESS) {
         return (createFindResult(rrclass_, zone_data_, node_result.code,
-                                 node_result.rrset, node_result.node));
+                                 node_result.node, node_result.rdataset,
+                                 options));
     }
 
     const ZoneNode* node = node_result.node;
@@ -520,29 +750,30 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
     if (node->isEmpty()) {
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
             arg(name);
-        const ZoneNode* nsec_node;
-        const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
-                                                  &nsec_node, options);
+        ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data_, node_path,
+                                                   options);
         return (createFindResult(rrclass_, zone_data_, NXRRSET,
-                                 nsec_rds,
-                                 nsec_node,
-                                 wild));
+                                 nsec_rrset.first, nsec_rrset.second,
+                                 options, wild));
     }
 
     const RdataSet* found;
 
     // If the node callback is enabled, this may be a zone cut.  If it
     // has a NS RR, we should return a delegation, but not in the apex.
-    // There is one exception: the case for DS query, which should always
-    // be considered in-zone lookup.
+    // There are two exceptions:
+    // - the case for DS query, which should always be considered in-zone
+    //   lookup.
+    // - when we are looking for glue records (FIND_GLUE_OK)
     if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
-            node != zone_data_.getOriginNode() && type != RRType::DS()) {
+        (options & FIND_GLUE_OK) == 0 &&
+        node != zone_data_.getOriginNode() && type != RRType::DS()) {
         found = RdataSet::find(node->getData(), RRType::NS());
         if (found != NULL) {
             LOG_DEBUG(logger, DBG_TRACE_DATA,
                       DATASRC_MEM_EXACT_DELEGATION).arg(name);
             return (createFindResult(rrclass_, zone_data_, DELEGATION,
-                                     found, node, wild, &name));
+                                     node, found, options, wild, &name));
         }
     }
 
@@ -552,47 +783,181 @@ InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
         const RdataSet* cur_rds = node->getData();
         while (cur_rds != NULL) {
             target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
-                                                  &name));
+                                                  options, &name));
             cur_rds = cur_rds->getNext();
         }
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
             arg(name);
-        return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
-                                 wild, &name));
+        return (createFindResult(rrclass_, zone_data_, SUCCESS, node, NULL,
+                                 options, wild, &name));
     }
 
-    const RdataSet* currds = node->getData();
-    while (currds != NULL) {
-        currds = currds->getNext();
-    }
     found = RdataSet::find(node->getData(), type);
     if (found != NULL) {
         // Good, it is here
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
             arg(type);
-        return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
-                                 wild, &name));
+        return (createFindResult(rrclass_, zone_data_, SUCCESS, node, found,
+                                 options, wild, &name));
     } else {
         // Next, try CNAME.
         found = RdataSet::find(node->getData(), RRType::CNAME());
         if (found != NULL) {
 
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
-            return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
-                                     wild, &name));
+            return (createFindResult(rrclass_, zone_data_, CNAME, node, found,
+                                     options, wild, &name));
         }
     }
     // No exact match or CNAME.  Get NSEC if necessary and return NXRRSET.
-    return (createFindResult(rrclass_, zone_data_, NXRRSET,
+    return (createFindResult(rrclass_, zone_data_, NXRRSET, node,
                              getNSECForNXRRSET(zone_data_, options, node),
-                             node, wild, &name));
+                             options, wild, &name));
 }
 
 isc::datasrc::ZoneFinder::FindNSEC3Result
 InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
-    (void)name;
-    (void)recursive;
-    isc_throw(isc::NotImplemented, "not completed yet! please implement me");
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
+        arg(recursive ? "recursive" : "non-recursive");
+
+    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+    const LabelSequence origin_ls(zone_data_.getOriginNode()->
+                                  getAbsoluteLabels(labels_buf));
+    const LabelSequence name_ls(name);
+
+    if (!zone_data_.isNSEC3Signed()) {
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt for non NSEC3 signed zone: " <<
+                  origin_ls << "/" << getClass());
+    }
+
+    const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
+    // This would be a programming mistake, as ZoneData::isNSEC3Signed()
+    // should check this.
+    assert(nsec3_data != NULL);
+
+    const ZoneTree& tree = nsec3_data->getNSEC3Tree();
+    if (tree.getNodeCount() == 0) {
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
+                  origin_ls << "/" << getClass());
+    }
+
+    const NameComparisonResult cmp_result = name_ls.compare(origin_ls);
+    if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+        cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+        isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
+                  << name_ls << ", zone: " << origin_ls << "/"
+                  << getClass());
+    }
+
+    // Convenient shortcuts
+    const unsigned int olabels = origin_ls.getLabelCount();
+    const unsigned int qlabels = name.getLabelCount();
+    // placeholder of the next closer proof
+    const ZoneNode* covering_node(NULL);
+
+    // Now we'll first look up the origin node and initialize orig_chain
+    // with it.
+    ZoneChain orig_chain;
+    const ZoneNode* node(NULL);
+    ZoneTree::Result result =
+         tree.find<void*>(origin_ls, &node, orig_chain, NULL, NULL);
+    if (result != ZoneTree::EXACTMATCH) {
+        // If the origin node doesn't exist, simply fail.
+        isc_throw(DataSourceError,
+                  "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
+                  origin_ls << "/" << getClass());
+    }
+
+    const boost::scoped_ptr<NSEC3Hash> hash
+        (NSEC3Hash::create(nsec3_data->hashalg,
+                           nsec3_data->iterations,
+                           nsec3_data->getSaltData(),
+                           nsec3_data->getSaltLen()));
+
+    // Examine all names from the query name to the origin name, stripping
+    // the deepest label one by one, until we find a name that has a matching
+    // NSEC3 hash.
+    for (unsigned int labels = qlabels; labels >= olabels; --labels) {
+        const Name& hname = (labels == qlabels ?
+                             name : name.split(qlabels - labels, labels));
+        const std::string hlabel = hash->calculate(hname);
+
+        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
+            arg(name).arg(labels).arg(hlabel);
+
+        node = NULL;
+        ZoneChain chain(orig_chain);
+
+        // Now, make a label sequence relative to the origin.
+        const Name hlabel_name(hlabel);
+        LabelSequence hlabel_ls(hlabel_name);
+        // Remove trailing '.' making it relative
+        hlabel_ls.stripRight(1);
+
+        // Find hlabel relative to the orig_chain.
+        result = tree.find<void*>(hlabel_ls, &node, chain, NULL, NULL);
+        if (result == ZoneTree::EXACTMATCH) {
+            // We found an exact match.
+            ConstRRsetPtr closest = createNSEC3RRset(node, getClass());
+            ConstRRsetPtr next;
+            if (covering_node != NULL) {
+                next = createNSEC3RRset(covering_node, getClass());
+            }
+
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
+                arg(*closest);
+
+            return (FindNSEC3Result(true, labels, closest, next));
+        } else {
+            while ((covering_node = tree.previousNode(chain)) != NULL &&
+                   covering_node->isEmpty()) {
+                ;
+            }
+            if (covering_node == NULL) {
+                covering_node = tree.largestNode();
+            }
+
+            if (!recursive) {   // in non recursive mode, we are done.
+                ConstRRsetPtr closest;
+                if (covering_node != NULL) {
+                    closest = createNSEC3RRset(covering_node, getClass());
+
+                    LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                              DATASRC_MEM_FINDNSEC3_COVER).
+                        arg(name).arg(*closest);
+                }
+
+                return (FindNSEC3Result(false, labels,
+                                        closest, ConstRRsetPtr()));
+            }
+        }
+    }
+
+    isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
+              "a broken NSEC3 zone: " << getOrigin() << "/"
+              << getClass());
+}
+
+Name
+InMemoryZoneFinder::getOrigin() const {
+    // In future we may allow adding out-of-zone names in the zone tree.
+    // For example, to hold out-of-zone NS names so we can establish a
+    // shortcut link to them as an optimization.  If and when that happens
+    // the origin node may not have an absolute label (consider the zone
+    // is example.org and we add ns.noexample.org).  Even in such cases,
+    // DomainTreeNode::getAbsoluteLabels() returns the correct absolute
+    // label sequence.
+    uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+    const LabelSequence name_labels =
+         zone_data_.getOriginNode()->getAbsoluteLabels(labels_buf);
+    size_t data_len;
+    const uint8_t* data = name_labels.getData(&data_len);
+
+    util::InputBuffer buffer(data, data_len);
+    return (Name(buffer));
 }
 
 } // namespace memory
diff --git a/src/lib/datasrc/memory/zone_finder.h b/src/lib/datasrc/memory/zone_finder.h
index 8f2c687..c95b5bc 100644
--- a/src/lib/datasrc/memory/zone_finder.h
+++ b/src/lib/datasrc/memory/zone_finder.h
@@ -23,30 +23,15 @@
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
 
+#include <string>
+
 namespace isc {
 namespace datasrc {
 namespace memory {
-
-class ZoneFinderResultContext {
-public:
-    /// \brief Constructor
-    ///
-    /// The first three parameters correspond to those of
-    /// ZoneFinder::ResultContext.  If node is non NULL, it specifies the
-    /// found RBNode in the search.
-    ZoneFinderResultContext(ZoneFinder::Result code_param,
-                            TreeNodeRRsetPtr rrset_param,
-                            ZoneFinder::FindResultFlags flags_param,
-                            const ZoneNode* node) :
-        code(code_param), rrset(rrset_param), flags(flags_param),
-        found_node(node)
-    {}
-
-    const ZoneFinder::Result code;
-    const TreeNodeRRsetPtr rrset;
-    const ZoneFinder::FindResultFlags flags;
-    const ZoneNode* const found_node;
-};
+namespace internal {
+// intermediate result context, only used in the zone finder implementation.
+class ZoneFinderResultContext;
+}
 
 /// A derived zone finder class intended to be used with the memory data
 /// source, using ZoneData for its contents.
@@ -92,16 +77,13 @@ public:
     findNSEC3(const isc::dns::Name& name, bool recursive);
 
     /// \brief Returns the origin of the zone.
-    virtual isc::dns::Name getOrigin() const {
-        return zone_data_.getOriginNode()->getName();
-    }
+    virtual isc::dns::Name getOrigin() const;
 
     /// \brief Returns the RR class of the zone.
     virtual isc::dns::RRClass getClass() const {
-        return rrclass_;
+        return (rrclass_);
     }
 
-
 private:
     /// \brief In-memory version of finder context.
     ///
@@ -110,7 +92,7 @@ private:
     class Context;
 
     /// Actual implementation for both find() and findAll()
-    ZoneFinderResultContext find_internal(
+    internal::ZoneFinderResultContext findInternal(
         const isc::dns::Name& name,
         const isc::dns::RRType& type,
         std::vector<isc::dns::ConstRRsetPtr>* target,
@@ -118,7 +100,7 @@ private:
         FIND_DEFAULT);
 
     const ZoneData& zone_data_;
-    const isc::dns::RRClass& rrclass_;
+    const isc::dns::RRClass rrclass_;
 };
 
 } // namespace memory
diff --git a/src/lib/datasrc/memory/zone_table_segment.cc b/src/lib/datasrc/memory/zone_table_segment.cc
new file mode 100644
index 0000000..7a80e3c
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment.cc
@@ -0,0 +1,38 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <datasrc/memory/zone_table_segment_local.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+ZoneTableSegment*
+ZoneTableSegment::create(const isc::data::Element&) {
+    /// FIXME: For now, we always return ZoneTableSegmentLocal. This
+    /// should be updated eventually to parse the passed Element
+    /// argument and construct a corresponding ZoneTableSegment
+    /// implementation.
+    return (new ZoneTableSegmentLocal);
+}
+
+void
+ZoneTableSegment::destroy(ZoneTableSegment *segment) {
+    delete segment;
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/memory/zone_table_segment.h b/src/lib/datasrc/memory/zone_table_segment.h
new file mode 100644
index 0000000..7fd1310
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment.h
@@ -0,0 +1,110 @@
+// 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 __ZONE_TABLE_SEGMENT_H__
+#define __ZONE_TABLE_SEGMENT_H__
+
+#include <datasrc/memory/zone_table.h>
+#include <cc/data.h>
+#include <util/memory_segment.h>
+
+#include <boost/interprocess/offset_ptr.hpp>
+
+#include <stdlib.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Memory-management independent entry point that contains a
+/// pointer to a zone table in memory.
+///
+/// An instance of this type lives inside a ZoneTableSegment
+/// implementation. It contains an offset pointer to the zone table (a
+/// map from domain names to zone locators) in memory.
+struct ZoneTableHeader {
+public:
+    /// \brief Returns a pointer to the underlying zone table.
+    ZoneTable* getTable() {
+        return (table.get());
+    }
+
+    /// \brief const version of \c getTable().
+    const ZoneTable* getTable() const {
+        return (table.get());
+    }
+
+private:
+    boost::interprocess::offset_ptr<ZoneTable> table;
+};
+
+/// \brief Manages a ZoneTableHeader, an entry point into a table of
+/// zones
+///
+/// This class specifies an interface for derived implementations which
+/// return a pointer to an object of type ZoneTableHeader, an entry
+/// point into a table of zones regardless of the underlying memory
+/// management implementation. Derived classes would implement the
+/// interface for specific memory-implementation behavior.
+class ZoneTableSegment {
+protected:
+    /// \brief Protected constructor
+    ///
+    /// An instance implementing this interface is expected to be
+    /// created by the factory method (\c create()), so this constructor
+    /// is protected.
+    ZoneTableSegment()
+    {}
+public:
+    /// \brief Destructor
+    virtual ~ZoneTableSegment() {}
+
+    /// \brief Return the ZoneTableHeader for the zone table segment.
+    virtual ZoneTableHeader& getHeader() = 0;
+
+    /// \brief const version of \c getHeader().
+    virtual const ZoneTableHeader& getHeader() const = 0;
+
+    /// \brief Return the MemorySegment for the zone table segment.
+    virtual isc::util::MemorySegment& getMemorySegment() = 0;
+
+    /// \brief Create an instance depending on the memory segment model
+    ///
+    /// This is a factory method to create a derived ZoneTableSegment
+    /// object based on the \c config passed. The method returns a
+    /// dynamically-allocated object. The caller is responsible for
+    /// destroying it with \c ZoneTableSegment::destroy().
+    ///
+    /// FIXME: For now, we always return ZoneTableSegmentLocal
+    /// regardless of the passed \c config.
+    ///
+    /// \param config The configuration based on which a derived object
+    ///               is returned.
+    /// \return Returns a ZoneTableSegment object
+    static ZoneTableSegment* create(const isc::data::Element& config);
+
+    /// \brief Destroy a ZoneTableSegment
+    ///
+    /// This method destroys the passed ZoneTableSegment. It must be
+    /// passed a segment previously created by \c ZoneTableSegment::create().
+    ///
+    /// \param segment The segment to destroy.
+    static void destroy(ZoneTableSegment* segment);
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // __ZONE_TABLE_SEGMENT_H__
diff --git a/src/lib/datasrc/memory/zone_table_segment_local.cc b/src/lib/datasrc/memory/zone_table_segment_local.cc
new file mode 100644
index 0000000..589c9af
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment_local.cc
@@ -0,0 +1,43 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment_local.h>
+
+using namespace isc::util;
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+// After more methods' definitions are added here, it would be a good
+// idea to move getHeader() and getMemorySegment() definitions to the
+// header file.
+ZoneTableHeader&
+ZoneTableSegmentLocal::getHeader() {
+     return (header_);
+}
+
+const ZoneTableHeader&
+ZoneTableSegmentLocal::getHeader() const {
+     return (header_);
+}
+
+MemorySegment&
+ZoneTableSegmentLocal::getMemorySegment() {
+     return (mem_sgmt_);
+}
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
diff --git a/src/lib/datasrc/memory/zone_table_segment_local.h b/src/lib/datasrc/memory/zone_table_segment_local.h
new file mode 100644
index 0000000..de776a9
--- /dev/null
+++ b/src/lib/datasrc/memory/zone_table_segment_local.h
@@ -0,0 +1,66 @@
+// 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 __ZONE_TABLE_SEGMENT_LOCAL_H__
+#define __ZONE_TABLE_SEGMENT_LOCAL_H__
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <util/memory_segment_local.h>
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+
+/// \brief Local implementation of ZoneTableSegment class
+///
+/// This class specifies a concrete implementation for a
+/// MemorySegmentLocal based ZoneTableSegment. Please see the
+/// ZoneTableSegment class documentation for usage.
+class ZoneTableSegmentLocal : public ZoneTableSegment {
+    // This is so that ZoneTableSegmentLocal can be instantiated from
+    // ZoneTableSegment::create().
+    friend class ZoneTableSegment;
+protected:
+    /// \brief Protected constructor
+    ///
+    /// Instances are expected to be created by the factory method
+    /// (\c ZoneTableSegment::create()), so this constructor is
+    /// protected.
+    ZoneTableSegmentLocal()
+    {}
+public:
+    /// \brief Destructor
+    virtual ~ZoneTableSegmentLocal() {}
+
+    /// \brief Return the ZoneTableHeader for the local zone table
+    /// segment implementation.
+    virtual ZoneTableHeader& getHeader();
+
+    /// \brief const version of \c getHeader().
+    virtual const ZoneTableHeader& getHeader() const;
+
+    /// \brief Return the MemorySegment for the local zone table segment
+    /// implementation (a MemorySegmentLocal instance).
+    virtual isc::util::MemorySegment& getMemorySegment();
+
+private:
+    ZoneTableHeader header_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+};
+
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // __ZONE_TABLE_SEGMENT_LOCAL_H__
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index e38a487..53cf077 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -792,13 +792,19 @@ public:
     /// context.
     Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
             const RBNodeResultContext& result) :
-        ZoneFinder::Context(finder, options,
+        ZoneFinder::Context(options,
                             ResultContext(result.code, result.rrset,
                                           result.flags)),
-        rrset_(result.rrset), found_node_(result.found_node)
+        finder_(finder), rrset_(result.rrset), found_node_(result.found_node)
     {}
 
 protected:
+    virtual ZoneFinder* getFinder() { return (&finder_); }
+
+    virtual const std::vector<isc::dns::ConstRRsetPtr>* getAllRRsets() const {
+        return (NULL);
+    }
+
     virtual void getAdditionalImpl(const vector<RRType>& requested_types,
                                    vector<ConstRRsetPtr>& result)
     {
@@ -866,6 +872,7 @@ private:
         }
     }
 
+    ZoneFinder& finder_;
     const ConstRBNodeRRsetPtr rrset_;
     const DomainNode* const found_node_;
 };
diff --git a/src/lib/datasrc/query.cc b/src/lib/datasrc/query.cc
deleted file mode 100644
index a8d675a..0000000
--- a/src/lib/datasrc/query.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/buffer.h>
-#include <dns/name.h>
-#include <dns/rrset.h>
-#include <dns/message.h>
-
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-
-using namespace isc::dns;
-
-namespace isc {
-namespace datasrc {
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
-                     const isc::dns::RRType& t,
-                     const isc::dns::Message::Section sect) :
-    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
-    op(AUTH_QUERY), state(GETANSWER), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, 
-                     const isc::dns::RRType& t,
-                     const isc::dns::Message::Section sect,
-                     const Op o) :
-    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
-    state(GETANSWER), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
-                     const isc::dns::RRType& t,
-                     const isc::dns::Message::Section sect,
-                     const State st) :
-    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect),
-    op(AUTH_QUERY), state(st), flags(0)
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
-                     const isc::dns::RRType& t,
-                     const isc::dns::Message::Section sect,
-                     const Op o, const State st) :
-    q(qry), qname(n), qclass(qry.qclass()), qtype(t), section(sect), op(o),
-    state(st), flags(0) 
-{}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, 
-                     const isc::dns::RRType& t, const Op o) :
-    q(qry), qname(n), qclass(qry.qclass()), qtype(t),
-    section(Message::SECTION_ANSWER), op(o), state(GETANSWER), flags(0)
-{
-    if (op != SIMPLE_QUERY) {
-        isc_throw(Unexpected, "invalid constructor for this task operation");
-    }
-}
-
-// A referral query doesn't need to specify section, state, or type.
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n, const Op o) :
-    q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
-    section(Message::SECTION_ANSWER), op(o), state(GETANSWER), flags(0)
-{
-    if (op != REF_QUERY) {
-        isc_throw(Unexpected, "invalid constructor for this task operation");
-    }
-}
-
-QueryTask::QueryTask(const Query& qry, const isc::dns::Name& n,
-                     const isc::dns::Message::Section sect, const Op o,
-                     const State st) :
-        q(qry), qname(n), qclass(qry.qclass()), qtype(RRType::ANY()),
-        section(sect), op(o), state(st), flags(0)
-{
-    if (op != GLUE_QUERY && op != NOGLUE_QUERY) {
-        isc_throw(Unexpected, "invalid constructor for this task operation");
-    }
-}
-
-QueryTask::~QueryTask() {}
-
-Query::Query(Message& m, HotCache& c, bool dnssec) :
-    status_(PENDING), qname_(NULL), qclass_(NULL), qtype_(NULL),
-    cache_(&c), message_(&m), want_additional_(true), want_dnssec_(dnssec)
-{
-    // Check message formatting
-    if (message_->getRRCount(Message::SECTION_QUESTION) != 1) {
-        isc_throw(Unexpected, "malformed message: too many questions");
-    }
-
-    // Populate the query task queue with the initial question
-    QuestionPtr question = *message_->beginQuestion();
-    qname_ = &question->getName();
-    qclass_ = &question->getClass();
-    qtype_ = &question->getType();
-    restarts_ = 0;
-
-    querytasks_.push(QueryTaskPtr(new QueryTask(*this, *qname_, *qtype_,
-                                                Message::SECTION_ANSWER)));
-}
-
-Query::~Query() {}
-
-}
-}
diff --git a/src/lib/datasrc/query.h b/src/lib/datasrc/query.h
deleted file mode 100644
index 43b62cc..0000000
--- a/src/lib/datasrc/query.h
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright (C) 2010  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 __QUERY_H
-#define __QUERY_H
-
-#include <boost/shared_ptr.hpp>
-
-#include <datasrc/cache.h>
-#include <datasrc/data_source.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rrtype.h>
-#include <dns/rrclass.h>
-
-#include <queue>
-
-namespace isc {
-namespace datasrc {
-
-class Query;
-typedef boost::shared_ptr<Query> QueryPtr;
-
-// An individual task to be carried out by the query logic
-class QueryTask {
-private:
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    QueryTask(const QueryTask& source);
-    QueryTask& operator=(const QueryTask& source);
-public:
-    // XXX: Members are currently public, but should probably be
-    // moved to private and wrapped in get() functions later.
-
-    // The \c Query that this \c QueryTask was created to service.
-    const Query& q;
-
-    // The standard query tuple: qname/qclass/qtype.
-    // Note that qtype is ignored in the GLUE_QUERY/NOGLUE_QUERY case.
-    const isc::dns::Name qname;
-    const isc::dns::RRClass qclass;
-    const isc::dns::RRType qtype;
-
-    // The section of the reply into which the data should be
-    // written after it has been fetched from the data source.
-    const isc::dns::Message::Section section;
-
-    // The op field indicates the operation to be carried out by
-    // this query task:
-    //
-    // - SIMPLE_QUERY: look for a match for qname/qclass/qtype
-    //   in local data (regardless of whether it is above or below
-    //   a zone cut).
-    //
-    // - AUTH_QUERY: look for a match for qname/qclass/qtype, or
-    //   for qname/qclass/CNAME, or for a referral.
-    //
-    // - GLUE_QUERY: look for matches with qname/qclass/A
-    //   OR qname/class/AAAA in local data, regardless of
-    //   authority, for use in glue.  (This can be implemented
-    //   as two successive SIMPLE_QUERY tasks, but might be
-    //   optimized by the concrete data source implementation
-    //   by turning it into a single database lookup.)
-    //
-    // - NOGLUE_QUERY: same as GLUE_QUERY except that answers
-    //   are rejected if they are below a zone cut.
-    //
-    // - REF_QUERY: look for matches for qname/qclass/NS,
-    //   qname/qclass/DS, and qname/qclass/DNAME.  Used
-    //   to search for a zone cut.
-
-    const enum Op {
-        SIMPLE_QUERY,
-        AUTH_QUERY,
-        GLUE_QUERY,
-        NOGLUE_QUERY,
-        REF_QUERY
-    } op;
-
-    // The state field indicates the state of the query; it controls
-    // the next step after processing each query task.
-    //
-    // - GETANSWER: We are looking for the answer to a primary query.
-    //   (The qname of the task should exactly match the qname of the
-    //   query.)  If we have no match, the query has failed.
-    //
-    // - GETADDITIONAL: We are filling in additional data, either
-    //   as a result of finding NS or MX records via a GETANSWER
-    //   query task, or as a result of finding NS records when
-    //   getting authority-section data.
-    //
-    // - FOLLOWCNAME: We are looking for the target of a CNAME RR that
-    //   was found via a previous GETANSWER query task.  If we have no
-    //   match, the query is still successful.
-    //
-    // (NOTE: It is only necessary to set a task state when pushing
-    // tasks onto the query task queue, which in turn is only necessary
-    // when it's uncertain which data source will be authoritative for the
-    // data.  That's why there is no GETAUTHORITY task state; when
-    // processing an answer, either positive or negative, the authoritative
-    // data source will already have been discovered, and can be queried
-    // directly.)
-
-    enum State {
-        GETANSWER,
-        GETADDITIONAL,
-        FOLLOWCNAME
-    } state;
-
-    // Response flags to indicate conditions encountered while
-    // processing this task.
-    uint32_t flags;
-
-    // Constructors
-    QueryTask(const Query& q, const isc::dns::Name& n,
-              const isc::dns::RRType& t,
-              const isc::dns::Message::Section sect);
-    QueryTask(const Query& q, const isc::dns::Name& n,
-              const isc::dns::RRType& t,
-              const isc::dns::Message::Section sect, Op o);
-    QueryTask(const Query& q, const isc::dns::Name& n,
-              const isc::dns::RRType& t,
-              const isc::dns::Message::Section sect,
-              const State st);
-    QueryTask(const Query& q, const isc::dns::Name& n,
-              const isc::dns::RRType& t,
-              const isc::dns::Message::Section sect,
-              Op o, State st);
-
-    // These are special constructors for particular query task types,
-    // to simplify the code.
-    //
-    // A simple query doesn't need to specify section or state.
-    QueryTask(const Query& q, const isc::dns::Name& n,
-              const isc::dns::RRType& t, Op o);
-    // A referral query doesn't need to specify section, state, or type.
-    QueryTask(const Query& q, const isc::dns::Name& n, Op o);
-    // A glue (or noglue) query doesn't need to specify type.
-    QueryTask(const Query& q, const isc::dns::Name& n,
-              const isc::dns::Message::Section sect, Op o, State st);
-
-    ~QueryTask();
-};
-
-typedef boost::shared_ptr<QueryTask> QueryTaskPtr;
-typedef std::queue<QueryTaskPtr> QueryTaskQueue;
-
-// Data Source query
-class Query {
-public:
-    // The state of a query: pending or answered.
-    enum Status {
-        PENDING,
-        ANSWERED
-    };
-
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-private:
-    Query(const Query& source);
-    Query& operator=(const Query& source);
-public:
-    // Query constructor
-    Query(isc::dns::Message& m, HotCache& c, bool dnssec);
-    /// \brief The destructor.
-    virtual ~Query();
-    //@}
-
-    // wantAdditional() == true indicates that additional-section data
-    // should be looked up while processing this query.  false indicates
-    // that we're only interested in answer-section data
-    bool wantAdditional() { return (want_additional_); }
-    void setWantAdditional(bool d) { want_additional_ = d; }
-
-    // wantDnssec() == true indicates that DNSSEC data should be retrieved
-    // from the data source when this query is being processed
-    bool wantDnssec() const { return (want_dnssec_); }
-    void setWantDnssec(bool d) { want_dnssec_ = d; }
-
-    const isc::dns::Name& qname() const { return (*qname_); }
-    const isc::dns::RRClass& qclass() const { return (*qclass_); }
-    const isc::dns::RRType& qtype() const { return (*qtype_); }
-
-    // Note: these can't be constant member functions because they expose
-    // writable 'handles' of internal member variables.  It's questionable
-    // whether we need these accessors in the first place because the
-    // corresponding members are public (which itself is not a good practice
-    // but it's a different topic), but at the moment we keep them.
-    // We should definitely revisit the design later.
-    isc::dns::Message& message() { return (*message_); }
-    QueryTaskQueue& tasks() { return (querytasks_); }
-
-    Status status() const { return (status_); }
-    void setStatus(Status s) { status_ = s; }
-
-    // Limit CNAME chains to 16 per query, to avoid loops
-    inline bool tooMany() {
-        if (++restarts_ > MAX_RESTARTS) {
-            return (true);
-        }
-        return (false);
-    }
-
-    void setDatasrc(DataSrc* ds) { datasrc_ = ds; }
-    DataSrc* datasrc() const { return (datasrc_); }
-
-    // \brief The query cache.  This is a static member of class \c Query;
-    // the same cache will be used by all instances.
-    HotCache& getCache() const { return (*cache_); }
-
-private:
-    Status status_;
-
-    const isc::dns::Name* qname_;
-    const isc::dns::RRClass* qclass_;
-    const isc::dns::RRType* qtype_;
-
-    HotCache* cache_;
-    DataSrc* datasrc_;
-
-    isc::dns::Message* message_;
-    QueryTaskQueue querytasks_;
-
-    bool want_additional_;
-    bool want_dnssec_;
-
-    static const int MAX_RESTARTS = 16;
-    int restarts_;
-};
-
-}
-}
-
-
-#endif
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index 5d651a6..78bab76 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -87,8 +87,13 @@ const char* const text_statements[NUM_STATEMENTS] = {
     "SELECT id FROM zones WHERE name=?1 AND rdclass = ?2", // ZONE
     "SELECT rdtype, ttl, sigtype, rdata FROM records "     // ANY
         "WHERE zone_id=?1 AND name=?2",
-    "SELECT rdtype, ttl, sigtype, rdata " // ANY_SUB
+
+    // ANY_SUB:
+    // This query returns records in the specified zone for the domain
+    // matching the passed name, and its sub-domains.
+    "SELECT rdtype, ttl, sigtype, rdata "
         "FROM records WHERE zone_id=?1 AND rname LIKE ?2",
+
     "BEGIN",                    // BEGIN
     "COMMIT",                   // COMMIT
     "ROLLBACK",                 // ROLLBACK
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
deleted file mode 100644
index 0482474..0000000
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ /dev/null
@@ -1,919 +0,0 @@
-// Copyright (C) 2010  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 <string>
-#include <sstream>
-#include <utility>
-
-#include <sqlite3.h>
-
-#include <datasrc/sqlite3_datasrc.h>
-#include <datasrc/logger.h>
-#include <exceptions/exceptions.h>
-#include <dns/rrttl.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-
-namespace {
-// Expected schema.  The major version must match else there is an error.  If
-// the minor version of the database is less than this, a warning is output.
-//
-// It is assumed that a program written to run on m.n of the database will run
-// with a database version m.p, where p is any number.  However, if p < n,
-// we assume that the database structure was upgraded for some reason, and that
-// some advantage may result if the database is upgraded. Conversely, if p > n,
-// The database is at a later version than the program was written for and the
-// program may not be taking advantage of features (possibly performance
-// improvements) added to the database.
-const int SQLITE_SCHEMA_MAJOR_VERSION = 2;
-const int SQLITE_SCHEMA_MINOR_VERSION = 0;
-}
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace isc {
-namespace datasrc {
-
-struct Sqlite3Parameters {
-    Sqlite3Parameters() :  db_(NULL), major_version_(-1), minor_version_(-1),
-        q_zone_(NULL), q_record_(NULL), q_addrs_(NULL), q_referral_(NULL),
-        q_any_(NULL), q_count_(NULL), q_previous_(NULL), q_nsec3_(NULL),
-        q_prevnsec3_(NULL)
-    {}
-    sqlite3* db_;
-    int major_version_;
-    int minor_version_;
-    sqlite3_stmt* q_zone_;
-    sqlite3_stmt* q_record_;
-    sqlite3_stmt* q_addrs_;
-    sqlite3_stmt* q_referral_;
-    sqlite3_stmt* q_any_;
-    sqlite3_stmt* q_count_;
-    sqlite3_stmt* q_previous_;
-    sqlite3_stmt* q_nsec3_;
-    sqlite3_stmt* q_prevnsec3_;
-};
-
-namespace {
-const char* const SCHEMA_LIST[] = {
-    "CREATE TABLE schema_version (version INTEGER NOT NULL, "
-        "minor INTEGER NOT NULL DEFAULT 0)",
-    "INSERT INTO schema_version VALUES (2, 1)",
-    "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
-    "name TEXT NOT NULL COLLATE NOCASE, "
-    "rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN', "
-    "dnssec BOOLEAN NOT NULL DEFAULT 0)",
-    "CREATE INDEX zones_byname ON zones (name)",
-    "CREATE TABLE records (id INTEGER PRIMARY KEY, "
-    "zone_id INTEGER NOT NULL, name TEXT NOT NULL COLLATE NOCASE, "
-    "rname TEXT NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
-    "rdtype TEXT NOT NULL COLLATE NOCASE, sigtype TEXT COLLATE NOCASE, "
-    "rdata TEXT NOT NULL)",
-    "CREATE INDEX records_byname ON records (name)",
-    "CREATE INDEX records_byrname ON records (rname)",
-    "CREATE INDEX records_bytype_and_rname ON records (rdtype, rname)",
-    "CREATE INDEX records_byrname_and_rdtype ON records (rname, rdtype)",
-    "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
-    "hash TEXT NOT NULL COLLATE NOCASE, "
-    "owner TEXT NOT NULL COLLATE NOCASE, "
-    "ttl INTEGER NOT NULL, rdtype TEXT NOT NULL COLLATE NOCASE, "
-    "rdata TEXT NOT NULL)",
-    "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
-    "CREATE INDEX nsec3_byhash_and_rdtype ON nsec3 (hash, rdtype)",
-    "CREATE TABLE diffs (id INTEGER PRIMARY KEY, "
-        "zone_id INTEGER NOT NULL, "
-        "version INTEGER NOT NULL, "
-        "operation INTEGER NOT NULL, "
-        "name TEXT NOT NULL COLLATE NOCASE, "
-        "rrtype TEXT NOT NULL COLLATE NOCASE, "
-        "ttl INTEGER NOT NULL, "
-        "rdata TEXT NOT NULL)",
-    NULL
-};
-
-const char* const q_version_str = "SELECT version FROM schema_version";
-const char* const q_minor_str = "SELECT minor FROM schema_version";
-
-const char* const q_zone_str = "SELECT id FROM zones WHERE name=?1";
-
-const char* const q_record_str = "SELECT rdtype, ttl, sigtype, rdata "
-    "FROM records WHERE zone_id=?1 AND name=?2 AND "
-    "((rdtype=?3 OR sigtype=?3) OR "
-    "(rdtype='CNAME' OR sigtype='CNAME') OR "
-    "(rdtype='NS' OR sigtype='NS'))";
-
-const char* const q_addrs_str = "SELECT rdtype, ttl, sigtype, rdata "
-    "FROM records WHERE zone_id=?1 AND name=?2 AND "
-    "(rdtype='A' OR sigtype='A' OR rdtype='AAAA' OR sigtype='AAAA')";
-
-const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
-    "records WHERE zone_id=?1 AND name=?2 AND"
-    "(rdtype='NS' OR sigtype='NS' OR rdtype='DS' OR sigtype='DS' OR "
-    "rdtype='DNAME' OR sigtype='DNAME')";
-
-const char* const q_any_str = "SELECT rdtype, ttl, sigtype, rdata "
-    "FROM records WHERE zone_id=?1 AND name=?2";
-
-// Note: the wildcard symbol '%' is expected to be added to the text
-// for the placeholder for LIKE given via sqlite3_bind_text().  We don't
-// use the expression such as (?2 || '%') because it would disable the use
-// of indices and could result in terrible performance.
-const char* const q_count_str = "SELECT COUNT(*) FROM records "
-    "WHERE zone_id=?1 AND rname LIKE ?2;";
-
-const char* const q_previous_str = "SELECT name FROM records "
-    "WHERE rname < ?2 AND zone_id=?1 AND rdtype = 'NSEC' "
-    "ORDER BY rname DESC LIMIT 1";
-
-const char* const q_nsec3_str = "SELECT rdtype, ttl, rdata FROM nsec3 "
-    "WHERE zone_id = ?1 AND hash = $2";
-
-const char* const q_prevnsec3_str = "SELECT hash FROM nsec3 "
-    "WHERE zone_id = ?1 AND hash <= $2 ORDER BY hash DESC LIMIT 1";
-
-}
-
-//
-//  Find the exact zone match.  Return -1 if not found, or the zone's
-//  ID if found.  This will always be >= 0 if found.
-//
-int
-Sqlite3DataSrc::hasExactZone(const char* const name) const {
-    int rc;
-
-    sqlite3_reset(dbparameters->q_zone_);
-    rc = sqlite3_bind_text(dbparameters->q_zone_, 1, name, -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind " << name <<
-                  " to SQL statement (zone)");
-    }
-
-    rc = sqlite3_step(dbparameters->q_zone_);
-    const int i = (rc == SQLITE_ROW) ?
-        sqlite3_column_int(dbparameters->q_zone_, 0) : -1; 
-    sqlite3_reset(dbparameters->q_zone_);
-    return (i);
-}
-
-namespace {
-int
-importSqlite3Rows(sqlite3_stmt* query, const Name& qname, const RRClass& qclass,
-                  const RRType& qtype, const bool nsec3_tree,
-                  RRsetList& result_sets, uint32_t& flags)
-{
-    int rows = 0;
-    int rc = sqlite3_step(query);
-    const bool qtype_is_any = (qtype == RRType::ANY());
-
-    while (rc == SQLITE_ROW) {
-        const char* type = (const char*)sqlite3_column_text(query, 0);
-        int ttl = sqlite3_column_int(query, 1);
-        const char* sigtype = NULL;
-        const char* rdata;
-
-        if (nsec3_tree) {
-            rdata = (const char*)sqlite3_column_text(query, 2);
-            if (RRType(type) == RRType::RRSIG()) {
-                sigtype = "NSEC3";
-            }
-        } else {
-            sigtype = (const char*)sqlite3_column_text(query, 2);
-            rdata = (const char*)sqlite3_column_text(query, 3);
-        }
-
-        const RRType base_rrtype(sigtype != NULL ? sigtype : type);
-
-        // found an NS; we need to inform the caller that this might be a
-        // referral, but we do not return the NS RRset to the caller
-        // unless asked for it.
-        if (base_rrtype == RRType::NS()) {
-            flags |= DataSrc::REFERRAL;
-            if (!qtype_is_any && qtype != RRType::NS()) {
-                rc = sqlite3_step(query);
-                continue;
-            }
-        }
-
-        ++rows;
-
-        // Looking for something else but found CNAME
-        if (base_rrtype == RRType::CNAME() && qtype != RRType::CNAME()) {
-            if (qtype == RRType::NSEC()) {
-                // NSEC query, just skip the CNAME
-                rc = sqlite3_step(query);
-                continue;
-            } else if (!qtype_is_any) {
-                // include the CNAME, but don't flag it for chasing if
-                // this is an ANY query
-                flags |= DataSrc::CNAME_FOUND;
-            }
-        }
-
-        RRsetPtr rrset = result_sets.findRRset(base_rrtype, qclass);
-        if (rrset == NULL) {
-            rrset = RRsetPtr(new RRset(qname, qclass, base_rrtype, RRTTL(ttl)));
-            result_sets.addRRset(rrset);
-        }
-
-        if (sigtype == NULL && base_rrtype == rrset->getType()) {
-            rrset->addRdata(createRdata(rrset->getType(), qclass, rdata));
-            if (ttl > rrset->getTTL().getValue()) {
-                rrset->setTTL(RRTTL(ttl));
-            }
-        } else if (sigtype != NULL && base_rrtype == rrset->getType()) {
-            RdataPtr rrsig = createRdata(RRType::RRSIG(), qclass, rdata);
-            if (rrset->getRRsig()) {
-                rrset->getRRsig()->addRdata(rrsig);
-            } else {
-                RRsetPtr sigs = RRsetPtr(new RRset(qname, qclass,
-                                                   RRType::RRSIG(),
-                                                   RRTTL(ttl)));
-                sigs->addRdata(rrsig);
-                rrset->addRRsig(sigs);
-            }
-
-            if (ttl > rrset->getRRsig()->getTTL().getValue()) {
-                rrset->getRRsig()->setTTL(RRTTL(ttl));
-            }
-        }
-
-        rc = sqlite3_step(query);
-    }
-
-    return (rows);
-}
-}
-
-int
-Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
-                            RRsetList& target, const Name* zonename,
-                            const Mode mode, uint32_t& flags) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_SQLITE_FINDREC).arg(name).
-        arg(rdtype);
-    flags = 0;
-    int zone_id = (zonename == NULL) ? findClosest(name, NULL) :
-        findClosest(*zonename, NULL);
-    if (zone_id < 0) {
-        flags = NO_SUCH_ZONE;
-        return (0);
-    }
-
-    sqlite3_stmt* query;
-    switch (mode) {
-    case ADDRESS:
-        query = dbparameters->q_addrs_;
-        break;
-    case DELEGATION:
-        query = dbparameters->q_referral_;
-        break;
-    default:
-        if (rdtype == RRType::ANY()) {
-            query = dbparameters->q_any_;
-        } else {
-            query = dbparameters->q_record_;
-        }
-        break;
-    }
-
-    sqlite3_reset(query);
-    sqlite3_clear_bindings(query);
-
-    int rc;
-    rc = sqlite3_bind_int(query, 1, zone_id);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
-                  " to SQL statement (query)");
-    }
-    const string name_text = name.toText();
-    rc = sqlite3_bind_text(query, 2, name_text.c_str(), -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind name " << name_text <<
-                  " to SQL statement (query)");
-    }
-
-    const string rdtype_text = rdtype.toText();
-    if (query == dbparameters->q_record_) {
-        rc = sqlite3_bind_text(query, 3, rdtype_text.c_str(), -1,
-                               SQLITE_STATIC);
-        if (rc != SQLITE_OK) {
-            isc_throw(Sqlite3Error, "Could not bind RR type " <<
-                      rdtype.toText() << " to SQL statement (query)");
-        }
-    }
-
-    const int rows = importSqlite3Rows(query, name, getClass(), rdtype, false,
-                                       target, flags);
-    sqlite3_reset(query);
-    if (rows > 0) {
-        return (rows);
-    }
-
-    //
-    // No rows were found.  We need to find out whether there are
-    // any RRs with that name to determine whether this is NXDOMAIN or
-    // NXRRSET
-    //
-    sqlite3_reset(dbparameters->q_count_);
-    sqlite3_clear_bindings(dbparameters->q_count_);
-
-    rc = sqlite3_bind_int(dbparameters->q_count_, 1, zone_id);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
-                  " to SQL statement (qcount)");
-    }
-
-    const string revname_text = name.reverse().toText() + "%";
-    rc = sqlite3_bind_text(dbparameters->q_count_, 2,
-                           revname_text.c_str(),
-                           -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind name " << name.reverse() <<
-                  " to SQL statement (qcount)");
-    }
-
-    rc = sqlite3_step(dbparameters->q_count_);
-    if (rc == SQLITE_ROW) {
-        if (sqlite3_column_int(dbparameters->q_count_, 0) != 0) {
-            flags |= TYPE_NOT_FOUND;
-            sqlite3_reset(dbparameters->q_count_);
-            return (0);
-        }
-    }
-
-    flags |= NAME_NOT_FOUND;
-    sqlite3_reset(dbparameters->q_count_);
-    return (0);
-}
-
-//
-//  Search for the closest enclosing zone.  Will return -1 if not found,
-//  >= 0 if found.  If position is not NULL, it will be filled in with the
-//  longest match found.
-//
-int
-Sqlite3DataSrc::findClosest(const Name& name, unsigned int* position) const {
-    const unsigned int nlabels = name.getLabelCount();
-    for (unsigned int i = 0; i < nlabels; ++i) {
-        const Name matchname(name.split(i));
-        const int rc = hasExactZone(matchname.toText().c_str());
-        if (rc >= 0) {
-            if (position != NULL) {
-                *position = i;
-            }
-            return (rc);
-        }
-    }
-
-    return (-1);
-}
-
-void
-Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE).
-        arg(match.getName());
-    if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
-        return;
-    }
-
-    unsigned int position;
-    if (findClosest(match.getName(), &position) == -1) {
-        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE_NOT_FOUND)
-                  .arg(match.getName());
-        return;
-    }
-
-    match.update(*this, match.getName().split(position));
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findPreviousName(const Name& qname,
-                                 Name& target,
-                                 const Name* zonename) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_PREVIOUS).arg(qname);
-    const int zone_id = (zonename == NULL) ?
-        findClosest(qname, NULL) : findClosest(*zonename, NULL);
-    if (zone_id < 0) {
-        LOG_ERROR(logger, DATASRC_SQLITE_PREVIOUS_NO_ZONE).arg(qname.toText());
-        return (ERROR);
-    }
-    
-    sqlite3_reset(dbparameters->q_previous_);
-    sqlite3_clear_bindings(dbparameters->q_previous_);
-
-    int rc = sqlite3_bind_int(dbparameters->q_previous_, 1, zone_id);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
-                  " to SQL statement (qprevious)");        
-    }
-    const string revname_text = qname.reverse().toText();
-    rc = sqlite3_bind_text(dbparameters->q_previous_, 2,
-                           revname_text.c_str(), -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind name " << qname <<
-                  " to SQL statement (qprevious)");
-    }
-  
-    rc = sqlite3_step(dbparameters->q_previous_);
-    if (rc != SQLITE_ROW) {
-        sqlite3_reset(dbparameters->q_previous_);
-        return (ERROR);
-    }
-
-    // XXX: bad cast.  we should revisit this.
-    target = Name((const char*)sqlite3_column_text(dbparameters->q_previous_,
-                                                   0));
-    sqlite3_reset(dbparameters->q_previous_);
-    return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findCoveringNSEC3(const Name& zonename,
-                                  string& hashstr,
-                                  RRsetList& target) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND_NSEC3).
-        arg(zonename).arg(hashstr);
-    const int zone_id = findClosest(zonename, NULL);
-    if (zone_id < 0) {
-        LOG_ERROR(logger, DATASRC_SQLITE_FIND_NSEC3_NO_ZONE).arg(zonename);
-        return (ERROR);
-    }
-
-    sqlite3_reset(dbparameters->q_prevnsec3_);
-    sqlite3_clear_bindings(dbparameters->q_prevnsec3_);
-
-    int rc = sqlite3_bind_int(dbparameters->q_prevnsec3_, 1, zone_id);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
-                  " to SQL statement (previous NSEC3)");        
-    }
-
-    rc = sqlite3_bind_text(dbparameters->q_prevnsec3_, 2, hashstr.c_str(),
-                           -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind hash " << hashstr <<
-                  " to SQL statement (previous NSEC3)");
-    }
-
-    rc = sqlite3_step(dbparameters->q_prevnsec3_);
-    const char* hash;
-    if (rc == SQLITE_ROW) {
-        hash = (const char*) sqlite3_column_text(dbparameters->q_prevnsec3_, 0);
-    } else {
-        // We need to find the final NSEC3 in the chain.
-        // A valid NSEC3 hash is in base32, which contains no
-        // letters higher than V, so a search for the previous 
-        // NSEC3 from "w" will always find it.
-        sqlite3_reset(dbparameters->q_prevnsec3_);
-        rc = sqlite3_bind_text(dbparameters->q_prevnsec3_, 2, "w", -1,
-                               SQLITE_STATIC);
-        if (rc != SQLITE_OK) {
-            isc_throw(Sqlite3Error, "Could not bind \"w\""
-                      " to SQL statement (previous NSEC3)");
-        }
-
-        rc = sqlite3_step(dbparameters->q_prevnsec3_);
-        if (rc != SQLITE_ROW) {
-            return (ERROR);
-        }
-
-        hash = (const char*) sqlite3_column_text(dbparameters->q_prevnsec3_, 0);
-    }
-
-    sqlite3_reset(dbparameters->q_nsec3_);
-    sqlite3_clear_bindings(dbparameters->q_nsec3_);
-
-    rc = sqlite3_bind_int(dbparameters->q_nsec3_, 1, zone_id);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind zone ID " << zone_id <<
-                  " to SQL statement (NSEC3)");        
-    }
-
-    rc = sqlite3_bind_text(dbparameters->q_nsec3_, 2, hash, -1, SQLITE_STATIC);
-    if (rc != SQLITE_OK) {
-        isc_throw(Sqlite3Error, "Could not bind hash " << hash <<
-                  " to SQL statement (NSEC3)");
-    }
-
-    DataSrc::Result result = SUCCESS;
-    uint32_t flags = 0;
-    if (importSqlite3Rows(dbparameters->q_nsec3_,
-                          Name(hash).concatenate(zonename),
-                          getClass(), RRType::NSEC3(), true, target,
-                          flags) == 0 || flags != 0) {
-        result = ERROR;
-    }
-    hashstr = string(hash);
-    sqlite3_reset(dbparameters->q_nsec3_);
-    return (result);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findRRset(const Name& qname,
-                          const RRClass& qclass,
-                          const RRType& qtype,
-                          RRsetList& target,
-                          uint32_t& flags,
-                          const Name* zonename) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FIND).arg(qname).
-        arg(qtype);
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_SQLITE_FIND_BAD_CLASS).arg(getClass()).
-            arg(qclass);
-        return (ERROR);
-    }
-    findRecords(qname, qtype, target, zonename, NORMAL, flags);
-    return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findExactRRset(const Name& qname,
-                               const RRClass& qclass,
-                               const RRType& qtype,
-                               RRsetList& target,
-                               uint32_t& flags,
-                               const Name* zonename) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDEXACT).arg(qname).
-        arg(qtype);
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_SQLITE_FINDEXACT_BAD_CLASS).arg(getClass()).
-            arg(qclass);
-        return (ERROR);
-    }
-    findRecords(qname, qtype, target, zonename, NORMAL, flags);
-
-    // Ignore referrals in this case
-    flags &= ~REFERRAL;
-
-    // CNAMEs don't count in this case
-    if (flags & CNAME_FOUND) {
-        flags &= ~CNAME_FOUND;
-        flags |= TYPE_NOT_FOUND;
-    }
-
-    return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findAddrs(const Name& qname,
-                          const RRClass& qclass,
-                          RRsetList& target,
-                          uint32_t& flags,
-                          const Name* zonename) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDADDRS).arg(qname);
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_SQLITE_FINDADDRS_BAD_CLASS).arg(getClass()).
-            arg(qclass);
-        return (ERROR);
-    }
-    findRecords(qname, RRType::ANY(), target, zonename, ADDRESS, flags);
-    return (SUCCESS);
-}
-
-DataSrc::Result
-Sqlite3DataSrc::findReferral(const Name& qname,
-                             const RRClass& qclass,
-                             RRsetList& target,
-                             uint32_t& flags,
-                             const Name* zonename) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_FINDREF).arg(qname);
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_SQLITE_FINDREF_BAD_CLASS).arg(getClass()).
-            arg(qclass);
-        return (ERROR);
-    }
-    findRecords(qname, RRType::ANY(), target, zonename, DELEGATION, flags);
-    return (SUCCESS);
-}
-
-Sqlite3DataSrc::Sqlite3DataSrc() :
-    dbparameters(new Sqlite3Parameters)
-{
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CREATE);
-}
-
-Sqlite3DataSrc::~Sqlite3DataSrc() {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_DESTROY);
-    if (dbparameters->db_ != NULL) {
-        close();
-    }
-    delete dbparameters;
-}
-
-DataSrc::Result
-Sqlite3DataSrc::init(isc::data::ConstElementPtr config) {
-    if (config && config->contains("database_file")) {
-        open(config->get("database_file")->stringValue());
-    } else {
-        isc_throw(DataSourceError, "No SQLite database file specified");
-    }
-    return (SUCCESS);
-}
-
-namespace {
-// This is a helper class to initialize a Sqlite3 DB safely.  An object of
-// this class encapsulates all temporary resources that are necessary for
-// the initialization, and release them in the destructor.  Once everything
-// is properly initialized, the move() method moves the allocated resources
-// to the main object in an exception free manner.  This way, the main code
-// for the initialization can be exception safe, and can provide the strong
-// exception guarantee.
-class Sqlite3Initializer {
-public:
-    ~Sqlite3Initializer() {
-        if (params_.q_zone_ != NULL) {
-            sqlite3_finalize(params_.q_zone_);
-        }
-        if (params_.q_record_ != NULL) {
-            sqlite3_finalize(params_.q_record_);
-        }
-        if (params_.q_addrs_ != NULL) {
-            sqlite3_finalize(params_.q_addrs_);
-        }
-        if (params_.q_referral_ != NULL) {
-            sqlite3_finalize(params_.q_referral_);
-        }
-        if (params_.q_any_ != NULL) {
-            sqlite3_finalize(params_.q_any_);
-        }
-        if (params_.q_count_ != NULL) {
-            sqlite3_finalize(params_.q_count_);
-        }
-        if (params_.q_previous_ != NULL) {
-            sqlite3_finalize(params_.q_previous_);
-        }
-        if (params_.q_nsec3_ != NULL) {
-            sqlite3_finalize(params_.q_nsec3_);
-        }
-        if (params_.q_prevnsec3_ != NULL) {
-            sqlite3_finalize(params_.q_prevnsec3_);
-        }
-        if (params_.db_ != NULL) {
-            sqlite3_close(params_.db_);
-        }
-    }
-    void move(Sqlite3Parameters* dst) {
-        *dst = params_;
-        params_ = Sqlite3Parameters(); // clear everything
-    }
-    Sqlite3Parameters params_;
-};
-
-sqlite3_stmt*
-prepare(sqlite3* const db, const char* const statement) {
-    sqlite3_stmt* prepared = NULL;
-    if (sqlite3_prepare_v2(db, statement, -1, &prepared, NULL) != SQLITE_OK) { 
-        isc_throw(Sqlite3Error, "Could not prepare SQLite statement: " <<
-                  statement);
-    }
-    return (prepared);
-}
-
-// small function to sleep for 0.1 seconds, needed when waiting for
-// exclusive database locks (which should only occur on startup, and only
-// when the database has not been created yet)
-void do_sleep() {
-    struct timespec req;
-    req.tv_sec = 0;
-    req.tv_nsec = 100000000;
-    nanosleep(&req, NULL);
-}
-
-// returns the schema version element if the schema version table exists
-// returns -1 if it does not
-int check_schema_version_element(sqlite3* db, const char* const version_query) {
-    sqlite3_stmt* prepared = NULL;
-    // At this point in time, the database might be exclusively locked, in
-    // which case even prepare() will return BUSY, so we may need to try a
-    // few times
-    for (size_t i = 0; i < 50; ++i) {
-        int rc = sqlite3_prepare_v2(db, version_query, -1, &prepared, NULL);
-        if (rc == SQLITE_ERROR) {
-            // this is the error that is returned when the table does not
-            // exist
-            return (-1);
-        } else if (rc == SQLITE_OK) {
-            break;
-        } else if (rc != SQLITE_BUSY || i == 50) {
-            isc_throw(Sqlite3Error, "Unable to prepare version query: "
-                        << rc << " " << sqlite3_errmsg(db));
-        }
-        do_sleep();
-    }
-    if (sqlite3_step(prepared) != SQLITE_ROW) {
-        isc_throw(Sqlite3Error,
-                    "Unable to query version: " << sqlite3_errmsg(db));
-    }
-    int version = sqlite3_column_int(prepared, 0);
-    sqlite3_finalize(prepared);
-    return (version);
-}
-
-// Returns the schema major and minor version numbers in a pair.
-// Returns (-1, -1) if the table does not exist, (1, 0) for a V1
-// database, and (n, m) for any other.
-pair<int, int> check_schema_version(sqlite3* db) {
-    int major = check_schema_version_element(db, q_version_str);
-    if (major == -1) {
-        return (make_pair(-1, -1));
-    } else if (major == 1) {
-        return (make_pair(1, 0));
-    } else {
-        int minor = check_schema_version_element(db, q_minor_str);
-        return (make_pair(major, minor));
-    }
-}
-
-// A helper class used in create_database() below so we manage the one shot
-// transaction safely.
-class ScopedTransaction {
-public:
-    ScopedTransaction(sqlite3* db) : db_(NULL) {
-        // try for 5 secs (50*0.1)
-        for (size_t i = 0; i < 50; ++i) {
-            const int rc = sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION",
-                                        NULL, NULL, NULL);
-            if (rc == SQLITE_OK) {
-                break;
-            } else if (rc != SQLITE_BUSY || i == 50) {
-                isc_throw(Sqlite3Error, "Unable to acquire exclusive lock "
-                          "for database creation: " << sqlite3_errmsg(db));
-            }
-            do_sleep();
-        }
-        // Hold the DB pointer once we have successfully acquired the lock.
-        db_ = db;
-    }
-    ~ScopedTransaction() {
-        if (db_ != NULL) {
-            // Note: even rollback could fail in theory, but in that case
-            // we cannot do much for safe recovery anyway.  We could at least
-            // log the event, but for now don't even bother to do that, with
-            // the expectation that we'll soon stop creating the schema in this
-            // module.
-            sqlite3_exec(db_, "ROLLBACK", NULL, NULL, NULL);
-        }
-    }
-    void commit() {
-        if (sqlite3_exec(db_, "COMMIT TRANSACTION", NULL, NULL, NULL) !=
-            SQLITE_OK) {
-            isc_throw(Sqlite3Error, "Unable to commit newly created database "
-                      "schema: " << sqlite3_errmsg(db_));
-        }
-        db_ = NULL;
-    }
-
-private:
-    sqlite3* db_;
-};
-
-// return db version
-pair<int, int> create_database(sqlite3* db) {
-    logger.info(DATASRC_SQLITE_SETUP_OLD_API);
-
-    // try to get an exclusive lock. Once that is obtained, do the version
-    // check *again*, just in case this process was racing another
-    ScopedTransaction transaction(db);
-    pair<int, int> schema_version = check_schema_version(db);
-    if (schema_version.first == -1) {
-        for (int i = 0; SCHEMA_LIST[i] != NULL; ++i) {
-            if (sqlite3_exec(db, SCHEMA_LIST[i], NULL, NULL, NULL) !=
-                SQLITE_OK) {
-                isc_throw(Sqlite3Error,
-                        "Failed to set up schema " << SCHEMA_LIST[i]);
-            }
-        }
-        transaction.commit();
-
-        // Return the version. We query again to ensure that the only point
-        // in which the current schema version is defined is in the
-        // CREATE statements.
-        schema_version = check_schema_version(db);
-    }
-    return (schema_version);
-}
-
-void
-checkAndSetupSchema(Sqlite3Initializer* initializer) {
-    sqlite3* const db = initializer->params_.db_;
-
-    // Note: we use the same SCHEMA_xxx_VERSION log IDs here and in
-    // sqlite3_accessor.cc, which is against our policy of ID uniqueness.
-    // The assumption is that this file will soon be deprecated, and we don't
-    // bother to define separate IDs for the short period.
-    pair<int, int> schema_version = check_schema_version(db);
-    if (schema_version.first == -1) {
-        schema_version = create_database(db);
-    } else if (schema_version.first != SQLITE_SCHEMA_MAJOR_VERSION) {
-        LOG_ERROR(logger, DATASRC_SQLITE_INCOMPATIBLE_VERSION)
-            .arg(schema_version.first).arg(schema_version.second)
-            .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
-        isc_throw(IncompatibleDbVersion, "Incompatible database version");
-    } else if (schema_version.second < SQLITE_SCHEMA_MINOR_VERSION) {
-        LOG_WARN(logger, DATASRC_SQLITE_COMPATIBLE_VERSION)
-            .arg(schema_version.first).arg(schema_version.second)
-            .arg(SQLITE_SCHEMA_MAJOR_VERSION).arg(SQLITE_SCHEMA_MINOR_VERSION);
-    }
-
-    initializer->params_.major_version_ = schema_version.first;
-    initializer->params_.minor_version_ = schema_version.second;
-    initializer->params_.q_zone_ = prepare(db, q_zone_str);
-    initializer->params_.q_record_ = prepare(db, q_record_str);
-    initializer->params_.q_addrs_ = prepare(db, q_addrs_str);
-    initializer->params_.q_referral_ = prepare(db, q_referral_str);
-    initializer->params_.q_any_ = prepare(db, q_any_str);
-    initializer->params_.q_count_ = prepare(db, q_count_str);
-    initializer->params_.q_previous_ = prepare(db, q_previous_str);
-    initializer->params_.q_nsec3_ = prepare(db, q_nsec3_str);
-    initializer->params_.q_prevnsec3_ = prepare(db, q_prevnsec3_str);
-}
-}
-
-//
-//  Open the database.
-//
-void
-Sqlite3DataSrc::open(const string& name) {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_OPEN).arg(name);
-    if (dbparameters->db_ != NULL) {
-        isc_throw(DataSourceError, "Duplicate SQLite open with " << name);
-    }
-
-    Sqlite3Initializer initializer;
-
-    if (sqlite3_open(name.c_str(), &initializer.params_.db_) != 0) {
-        isc_throw(Sqlite3Error, "Cannot open SQLite database file: " << name);
-    }
-
-    checkAndSetupSchema(&initializer);
-    initializer.move(dbparameters);
-}
-
-//
-//  Close the database.
-//
-DataSrc::Result
-Sqlite3DataSrc::close(void) {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_SQLITE_CLOSE);
-    if (dbparameters->db_ == NULL) {
-        isc_throw(DataSourceError,
-                  "SQLite data source is being closed before open");
-    }
-
-    // XXX: sqlite3_finalize() could fail.  What should we do in that case?
-    sqlite3_finalize(dbparameters->q_zone_);
-    dbparameters->q_zone_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_record_);
-    dbparameters->q_record_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_addrs_);
-    dbparameters->q_addrs_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_referral_);
-    dbparameters->q_referral_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_any_);
-    dbparameters->q_any_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_count_);
-    dbparameters->q_count_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_previous_);
-    dbparameters->q_previous_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_prevnsec3_);
-    dbparameters->q_prevnsec3_ = NULL;
-
-    sqlite3_finalize(dbparameters->q_nsec3_);
-    dbparameters->q_nsec3_ = NULL;
-
-    sqlite3_close(dbparameters->db_);
-    dbparameters->db_ = NULL;
-
-    return (SUCCESS);
-}
-
-}
-}
diff --git a/src/lib/datasrc/sqlite3_datasrc.h b/src/lib/datasrc/sqlite3_datasrc.h
deleted file mode 100644
index 8ee042f..0000000
--- a/src/lib/datasrc/sqlite3_datasrc.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2010  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 __DATA_SOURCE_SQLITE3_H
-#define __DATA_SOURCE_SQLITE3_H
-
-#include <string>
-
-#include <exceptions/exceptions.h>
-
-#include <datasrc/data_source.h>
-
-namespace isc {
-
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-class RRsetList;
-}
-
-namespace datasrc {
-
-class Query;
-struct Sqlite3Parameters;
-
-class Sqlite3Error : public Exception {
-public:
-    Sqlite3Error(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-class IncompatibleDbVersion : public Exception {
-public:
-    IncompatibleDbVersion(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
-};
-
-class Sqlite3DataSrc : public DataSrc {
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-private:
-    Sqlite3DataSrc(const Sqlite3DataSrc& source);
-    Sqlite3DataSrc& operator=(const Sqlite3DataSrc& source);
-public:
-    Sqlite3DataSrc();
-    ~Sqlite3DataSrc();
-    //@}
-
-    void findClosestEnclosure(DataSrcMatch& match) const;
-
-    Result findRRset(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     const isc::dns::RRType& qtype,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findExactRRset(const isc::dns::Name& qname,
-                          const isc::dns::RRClass& qclass,
-                          const isc::dns::RRType& qtype,
-                          isc::dns::RRsetList& target,
-                          uint32_t& flags,
-                          const isc::dns::Name* zonename) const;
-
-    Result findAddrs(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findReferral(const isc::dns::Name& qname,
-                        const isc::dns::RRClass& qclass,
-                        isc::dns::RRsetList& target,
-                        uint32_t& flags,
-                        const isc::dns::Name* zonename) const;
-
-    DataSrc::Result findPreviousName(const isc::dns::Name& qname,
-                                     isc::dns::Name& target,
-                                     const isc::dns::Name* zonename) const;
-
-    Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                             std::string& hash,
-                             isc::dns::RRsetList& target) const;
-
-    Result init() { return (init(isc::data::ElementPtr())); }
-    Result init(const isc::data::ConstElementPtr config);
-    Result close();
-
-private:
-    enum Mode {
-        NORMAL,
-        ADDRESS,
-        DELEGATION
-    };
-
-    void open(const std::string& name);
-    int hasExactZone(const char *name) const;
-    int findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
-                    isc::dns::RRsetList& target, const isc::dns::Name* zonename,
-                    const Mode mode, uint32_t& flags) const;
-    int findClosest(const isc::dns::Name& name, unsigned int* position) const;
-
-private:
-    Sqlite3Parameters* dbparameters;
-};
-
-}
-}
-
-#endif // __DATA_SOURCE_SQLITE3_H
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
deleted file mode 100644
index 77d7a1d..0000000
--- a/src/lib/datasrc/static_datasrc.cc
+++ /dev/null
@@ -1,275 +0,0 @@
-// Copyright (C) 2010  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 <config.h>
-
-#include <cassert>
-
-#include <dns/name.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <datasrc/data_source.h>
-#include <datasrc/static_datasrc.h>
-#include <datasrc/logger.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace isc {
-namespace datasrc {
-
-// This class stores the "static" data for the built-in static data source.
-// Since it's static, it could be literally static, i.e, defined as static
-// objects.  But to avoid the static initialization order fiasco, which would
-// be unlikely to happen for this class in practice but is still possible,
-// we took a safer approach.  A downside of this approach is that redundant
-// copies of exactly the same content of these objects can be created.
-// In practice, however, there's normally at most one StaticDataSrc object,
-// so this should be acceptable (if this turns out to be a real concern we
-// might consider making this class a singleton).
-// We use the "pimpl" idiom for this class.  Operations for this class is
-// not expected to be performance sensitive, so the overhead due to the pimpl
-// should be okay, and it's more beneficial to hide details and minimize
-// inter module dependencies in header files.
-struct StaticDataSrcImpl {
-public:
-    StaticDataSrcImpl();
-    const Name authors_name;
-    const Name version_name;
-    // XXX: unfortunately these can't be ConstRRsetPtr because they'll be
-    // passed to RRsetList::addRRset(), which expect non const RRsetPtr.
-    // We should revisit this design later.
-    RRsetPtr authors;
-    RRsetPtr authors_ns;
-    RRsetPtr authors_soa;
-    RRsetPtr version;
-    RRsetPtr version_ns;
-    RRsetPtr version_soa;
-};
-
-StaticDataSrcImpl::StaticDataSrcImpl() :
-    authors_name("authors.bind"), version_name("version.bind")
-{
-    authors = RRsetPtr(new RRset(authors_name, RRClass::CH(),
-                                 RRType::TXT(), RRTTL(0)));
-    authors->addRdata(generic::TXT("Chen Zhengzhang")); // Jerry
-    authors->addRdata(generic::TXT("Dmitriy Volodin"));
-    authors->addRdata(generic::TXT("Evan Hunt"));
-    authors->addRdata(generic::TXT("Haidong Wang")); // Ocean
-    authors->addRdata(generic::TXT("Haikuo Zhang"));
-    authors->addRdata(generic::TXT("Han Feng"));
-    authors->addRdata(generic::TXT("Jelte Jansen"));
-    authors->addRdata(generic::TXT("Jeremy C. Reed")); 
-    authors->addRdata(generic::TXT("Xie Jiagui")); // Kevin Tes
-    authors->addRdata(generic::TXT("Jin Jian"));
-    authors->addRdata(generic::TXT("JINMEI Tatuya"));
-    authors->addRdata(generic::TXT("Kazunori Fujiwara"));
-    authors->addRdata(generic::TXT("Michael Graff"));
-    authors->addRdata(generic::TXT("Michal Vaner"));
-    authors->addRdata(generic::TXT("Mukund Sivaraman"));
-    authors->addRdata(generic::TXT("Naoki Kambe"));
-    authors->addRdata(generic::TXT("Shane Kerr"));
-    authors->addRdata(generic::TXT("Shen Tingting"));
-    authors->addRdata(generic::TXT("Stephen Morris"));
-    authors->addRdata(generic::TXT("Yoshitaka Aharen"));
-    authors->addRdata(generic::TXT("Zhang Likun"));
-
-    authors_ns = RRsetPtr(new RRset(authors_name, RRClass::CH(),
-                                    RRType::NS(), RRTTL(0)));
-    authors_ns->addRdata(generic::NS(authors_name));
-
-    authors_soa = RRsetPtr(new RRset(authors_name, RRClass::CH(),
-                                     RRType::SOA(), RRTTL(0)));
-    authors_soa->addRdata(generic::SOA(
-                              "authors.bind. hostmaster.authors.bind. "
-                              "0 28800 7200 604800 86400"));
-
-    version = RRsetPtr(new RRset(version_name, RRClass::CH(),
-                                 RRType::TXT(), RRTTL(0)));
-    version->addRdata(generic::TXT(PACKAGE_STRING));
-
-    version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
-                                    RRType::NS(), RRTTL(0)));
-    version_ns->addRdata(generic::NS(version_name));
-
-    version_soa = RRsetPtr(new RRset(version_name, RRClass::CH(),
-                                     RRType::SOA(), RRTTL(0)));
-    version_soa->addRdata(generic::SOA(
-                              "version.bind. hostmaster.version.bind. "
-                               "0 28800 7200 604800 86400"));
-}
-
-StaticDataSrc::StaticDataSrc() {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_STATIC_CREATE);
-    setClass(RRClass::CH());
-    impl_ = new StaticDataSrcImpl;
-}
-
-StaticDataSrc::~StaticDataSrc() {
-    delete impl_;
-}
-
-namespace {
-bool
-isSubdomain(const Name& qname, const Name& zone) {
-    const NameComparisonResult::NameRelation cmp =
-        qname.compare(zone).getRelation();
-    return (cmp == NameComparisonResult::EQUAL ||
-            cmp == NameComparisonResult::SUBDOMAIN);
-}
-}
-
-void
-StaticDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
-    const Name& qname = match.getName();
-
-    if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
-        return;
-    }
-
-    if (isSubdomain(qname, impl_->version_name)) {
-        match.update(*this, impl_->version_name);
-        return;
-    }
-
-    if (isSubdomain(qname, impl_->authors_name)) {
-        match.update(*this, impl_->authors_name);
-        return;
-    }
-}
-
-DataSrc::Result
-StaticDataSrc::findRRset(const Name& qname,
-                         const RRClass& qclass, const RRType& qtype,
-                         RRsetList& target, uint32_t& flags,
-                         const Name* const zonename) const
-{
-    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_STATIC_FIND).arg(qname).
-        arg(qtype);
-    flags = 0;
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_STATIC_CLASS_NOT_CH);
-        return (ERROR);
-    }
-
-    // Identify the appropriate zone.
-    bool is_versionname = false, is_authorsname = false;
-    if (zonename != NULL) {
-        if (*zonename == impl_->version_name &&
-            isSubdomain(qname, impl_->version_name)) {
-            is_versionname = true;
-        } else if (*zonename == impl_->authors_name &&
-                   isSubdomain(qname, impl_->authors_name)) {
-            is_authorsname = true;
-        } else {
-            flags = NO_SUCH_ZONE;
-            return (SUCCESS);
-        }
-    } else {
-        if (isSubdomain(qname, impl_->version_name)) {
-            is_versionname = true;
-        } else if (isSubdomain(qname, impl_->authors_name)) {
-            is_authorsname = true;
-        } else {
-            flags = NO_SUCH_ZONE;
-            return (SUCCESS);
-        }
-    }
-
-    const bool any = (qtype == RRType::ANY());
-
-    if (is_versionname) {
-        if (qname == impl_->version_name) {
-            if (qtype == RRType::TXT() || any) {
-                target.addRRset(impl_->version);
-            }
-            if (qtype == RRType::NS() || any) {
-                target.addRRset(impl_->version_ns);
-            }
-            if (qtype == RRType::SOA() || any) {
-                target.addRRset(impl_->version_soa);
-            }
-            if (target.size() == 0) {
-                flags = TYPE_NOT_FOUND;
-            }
-        } else {
-            flags = NAME_NOT_FOUND;
-        }
-    } else {
-        assert(is_authorsname);
-        if (qname == impl_->authors_name) {
-            if (qtype == RRType::TXT() || any) {
-                target.addRRset(impl_->authors);
-            }
-            if (qtype == RRType::NS() || any) {
-                target.addRRset(impl_->authors_ns);
-            }
-            if (qtype == RRType::SOA() || any) {
-                target.addRRset(impl_->authors_soa);
-            }
-            if (target.size() == 0 ) {
-                flags = TYPE_NOT_FOUND;
-            }
-        } else {
-            flags = NAME_NOT_FOUND;
-        }
-    }
-
-    return (SUCCESS);
-}
-
-DataSrc::Result
-StaticDataSrc::findExactRRset(const Name& qname,
-                              const RRClass& qclass, const RRType& qtype,
-                              RRsetList& target, uint32_t& flags,
-                              const Name* zonename) const
-{
-    return (findRRset(qname, qclass, qtype, target, flags, zonename));
-}
-
-DataSrc::Result
-StaticDataSrc::findPreviousName(const Name&, Name&, const Name*) const {
-    return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-StaticDataSrc::findCoveringNSEC3(const Name&, string&, RRsetList&) const {
-   return (NOT_IMPLEMENTED);
-}
-
-DataSrc::Result
-StaticDataSrc::init() {
-    return (SUCCESS);
-}
-
-// Static data source is "configuration less", so the \c config parameter
-// is intentionally ignored.
-DataSrc::Result
-StaticDataSrc::init(isc::data::ConstElementPtr) {
-    return (init());
-}
-
-DataSrc::Result
-StaticDataSrc::close() {
-    return (SUCCESS);
-}
-
-}
-}
diff --git a/src/lib/datasrc/static_datasrc.h b/src/lib/datasrc/static_datasrc.h
deleted file mode 100644
index 4d212fe..0000000
--- a/src/lib/datasrc/static_datasrc.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (C) 2009  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.
-
-//
-// Sample Datasource implementation; this datasource only returns
-// static content for the queries
-// CH TXT version.bind
-// and
-// CH TXT authors.bind
-//
-
-#ifndef __STATIC_DATA_SOURCE_H
-#define __STATIC_DATA_SOURCE_H
-
-#include <datasrc/data_source.h>
-
-namespace isc {
-
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-class RRType;
-class RRsetList;
-}
-
-namespace datasrc {
-
-struct StaticDataSrcImpl;
-
-class StaticDataSrc : public DataSrc {
-private:
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-    StaticDataSrc(const StaticDataSrc& source);
-    StaticDataSrc& operator=(const StaticDataSrc& source);
-public:
-    StaticDataSrc();
-    ~StaticDataSrc();
-    //@}
-
-    void findClosestEnclosure(DataSrcMatch& match) const;
-
-    Result findRRset(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     const isc::dns::RRType& qtype,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findExactRRset(const isc::dns::Name& qname,
-                          const isc::dns::RRClass& qclass,
-                          const isc::dns::RRType& qtype,
-                          isc::dns::RRsetList& target,
-                          uint32_t& flags,
-                          const isc::dns::Name* zonename) const;
-
-    Result findPreviousName(const isc::dns::Name& qname,
-                            isc::dns::Name& target,
-                            const isc::dns::Name* zonename) const;
-
-    Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                             std::string& hash,
-                             isc::dns::RRsetList& target) const;
-
-    Result init();
-    Result init(isc::data::ConstElementPtr config);
-    Result close();
-private:
-    StaticDataSrcImpl* impl_;
-};
-
-}
-}
-
-#endif
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index b73a64a..d2049f1 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = testdata
+SUBDIRS = . memory testdata
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
@@ -47,15 +47,6 @@ common_ldadd += $(GTEST_LDADD) $(SQLITE_LIBS)
 # The general tests
 run_unittests_SOURCES = $(common_sources)
 
-# Commented out by ticket #2165. If you re-enable these, please modify
-# EXTRA_DIST at the bottom of this file.
-#run_unittests_SOURCES += datasrc_unittest.cc
-#run_unittests_SOURCES += static_unittest.cc
-#run_unittests_SOURCES += query_unittest.cc
-#run_unittests_SOURCES += cache_unittest.cc
-#run_unittests_SOURCES += sqlite3_unittest.cc
-#run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
-
 run_unittests_SOURCES += test_client.h test_client.cc
 run_unittests_SOURCES += rbtree_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
@@ -124,11 +115,3 @@ EXTRA_DIST += testdata/new_minor_schema.sqlite3
 EXTRA_DIST += testdata/newschema.sqlite3
 EXTRA_DIST += testdata/oldschema.sqlite3
 EXTRA_DIST += testdata/static.zone
-
-# Added by ticket #2165
-EXTRA_DIST += datasrc_unittest.cc
-EXTRA_DIST += static_unittest.cc
-EXTRA_DIST += query_unittest.cc
-EXTRA_DIST += cache_unittest.cc
-EXTRA_DIST += sqlite3_unittest.cc
-EXTRA_DIST += test_datasrc.h test_datasrc.cc
diff --git a/src/lib/datasrc/tests/cache_unittest.cc b/src/lib/datasrc/tests/cache_unittest.cc
deleted file mode 100644
index 1325f64..0000000
--- a/src/lib/datasrc/tests/cache_unittest.cc
+++ /dev/null
@@ -1,340 +0,0 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <stdexcept>
-
-#include <dns/name.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-#include <dns/rrset.h>
-
-#include <datasrc/cache.h>
-#include <datasrc/data_source.h>
-
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-
-namespace {
-class CacheTest : public ::testing::Test {
-protected:
-    CacheTest() : test_name("test.example.com"),
-                  test_nsname("ns.example.com"),
-                  test_ch("example.com")
-    {
-        RRsetPtr a(new RRset(test_name, RRClass::IN(), RRType::A(),
-                             RRTTL(3600)));
-        a->addRdata(in::A("192.0.2.1"));
-        a->addRdata(in::A("192.0.2.2"));
-
-        RRsetPtr b(new RRset(test_nsname, RRClass::IN(), RRType::NS(),
-                             RRTTL(86400)));
-        RRsetPtr c(new RRset(test_ch, RRClass::CH(), RRType::TXT(),
-                             RRTTL(0)));
-        c->addRdata(generic::TXT("Text record"));
-
-        cache.setSlots(5);
-
-        cache.addPositive(a, 1, 30);
-        cache.addPositive(b, 2, 30);
-        cache.addPositive(c, 4, 30);
-    }
-
-    Name test_name;
-    Name test_nsname;
-    Name test_ch;
-
-    HotCache cache;
-};
-
-class TestRRset : public RRset {
-public:
-    TestRRset(const Name& name, int& counter) :
-        RRset(name, RRClass::IN(), RRType::A(), RRTTL(3600)),
-        counter_(counter)
-    {
-        ++counter_;
-    }
-    ~TestRRset() {
-        --counter_;
-    }
-    int& counter_;
-};
-
-// make sure any remaining cache entries are purged on destruction of the
-// cache.
-TEST_F(CacheTest, cleanup) {
-    HotCache* local_cache(new HotCache);
-    int num_rrsets = 0;
-
-    local_cache->addPositive(RRsetPtr(new TestRRset(Name("example.com"),
-                                                    num_rrsets)), 0, 10);
-    local_cache->addPositive(RRsetPtr(new TestRRset(Name("example.org"),
-                                                    num_rrsets)), 0, 10);
-
-    EXPECT_EQ(2, num_rrsets);
-    delete local_cache;
-    EXPECT_EQ(0, num_rrsets);
-}
-
-TEST_F(CacheTest, slots) {
-    EXPECT_EQ(5, cache.getSlots());
-    EXPECT_EQ(3, cache.getCount());
-}
-
-TEST_F(CacheTest, insert) {
-    RRsetPtr aaaa(new RRset(Name("foo"), RRClass::IN(), RRType::AAAA(),
-                            RRTTL(0)));
-    aaaa->addRdata(in::AAAA("2001:db8:3:bb::5"));
-    cache.addPositive(aaaa, 0, 4);
-
-    EXPECT_EQ(4, cache.getCount());
-
-    RRsetPtr r;
-    uint32_t f;
-    bool hit = cache.retrieve(Name("foo"), RRClass::IN(),
-                                RRType::AAAA(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(aaaa, r);
-}
-
-TEST_F(CacheTest, retrieveOK) {
-    bool hit;
-    RRsetPtr r;
-    uint32_t f;
-
-    // Test repeatedly to ensure that all records remain accessible
-    // even after being promoted to the top of the cache
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_name, r->getName());
-    EXPECT_EQ(RRClass::IN(), r->getClass());
-    EXPECT_EQ(RRType::A(), r->getType());
-
-    hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_nsname, r->getName());
-    EXPECT_EQ(RRClass::IN(), r->getClass());
-    EXPECT_EQ(RRType::NS(), r->getType());
-
-    hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_ch, r->getName());
-    EXPECT_EQ(RRClass::CH(), r->getClass());
-    EXPECT_EQ(RRType::TXT(), r->getType());
-    
-    hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_nsname, r->getName());
-    EXPECT_EQ(RRClass::IN(), r->getClass());
-    EXPECT_EQ(RRType::NS(), r->getType());
-
-    hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_ch, r->getName());
-    EXPECT_EQ(RRClass::CH(), r->getClass());
-    EXPECT_EQ(RRType::TXT(), r->getType());
-
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_name, r->getName());
-    EXPECT_EQ(RRClass::IN(), r->getClass());
-    EXPECT_EQ(RRType::A(), r->getType());
-
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(test_name, r->getName());
-    EXPECT_EQ(RRClass::IN(), r->getClass());
-    EXPECT_EQ(RRType::A(), r->getType());
-};
-
-TEST_F(CacheTest, flags) {
-    bool hit;
-    RRsetPtr r;
-    uint32_t f;
-
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_TRUE(r);
-    EXPECT_EQ(DataSrc::REFERRAL, f);
-
-    hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_TRUE(r);
-    EXPECT_EQ(DataSrc::CNAME_FOUND, f);
-
-    hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_TRUE(r);
-    EXPECT_EQ(DataSrc::NAME_NOT_FOUND, f);
-}
-
-TEST_F(CacheTest, retrieveFail) {
-    bool hit;
-    RRsetPtr r;
-    uint32_t f;
-
-    hit = cache.retrieve(Name("fake"), RRClass::IN(), RRType::A(), r, f);
-    EXPECT_FALSE(hit);
-
-    hit = cache.retrieve(test_name, RRClass::CH(), RRType::A(), r, f);
-    EXPECT_FALSE(hit);
-
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::DNSKEY(), r, f);
-    EXPECT_FALSE(hit);
-}
-
-TEST_F(CacheTest, expire) {
-    // Insert "foo" with a duration of 1 seconds; sleep 2.  The
-    // record should not be returned from the cache even though it's
-    // at the top of the cache.
-    RRsetPtr aaaa(new RRset(Name("foo"), RRClass::IN(), RRType::AAAA(),
-                            RRTTL(0)));
-    aaaa->addRdata(in::AAAA("2001:db8:3:bb::5"));
-    cache.addPositive(aaaa, 0, 1);
-
-    sleep(2);
-
-    RRsetPtr r;
-    uint32_t f;
-    bool hit = cache.retrieve(Name("foo"), RRClass::IN(), RRType::AAAA(), r, f);
-    EXPECT_FALSE(hit);
-};
-
-TEST_F(CacheTest, LRU) {
-    // Retrieve a record, cache four new records; with five slots
-    // in the LRU queue this should remove all the previous records
-    // except the last one retreived.
-    bool hit;
-    RRsetPtr r;
-    uint32_t f;
-
-    hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_EQ(3, cache.getCount());
-
-    RRsetPtr one(new RRset(Name("one"), RRClass::IN(), RRType::TXT(),
-                           RRTTL(0)));
-    one->addRdata(generic::TXT("one"));
-    cache.addPositive(one, 0, 30);
-    EXPECT_EQ(4, cache.getCount());
-
-    RRsetPtr two(new RRset(Name("two"), RRClass::IN(), RRType::TXT(),
-                           RRTTL(0)));
-    two->addRdata(generic::TXT("two"));
-    cache.addPositive(two, 0, 30);
-    EXPECT_EQ(5, cache.getCount());
-
-    RRsetPtr three(new RRset(Name("three"), RRClass::IN(), RRType::TXT(),
-                             RRTTL(0)));
-    three->addRdata(generic::TXT("three"));
-    cache.addPositive(three, 0, 30);
-    EXPECT_EQ(5, cache.getCount());
-
-    RRsetPtr four(new RRset(Name("four"), RRClass::IN(), RRType::TXT(),
-                            RRTTL(0)));
-    four->addRdata(generic::TXT("four"));
-    cache.addPositive(four, 0, 30);
-    EXPECT_EQ(5, cache.getCount());
-
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_FALSE(hit);
-
-    hit = cache.retrieve(test_nsname, RRClass::IN(), RRType::NS(), r, f);
-    EXPECT_TRUE(hit);
-
-    hit = cache.retrieve(test_ch, RRClass::CH(), RRType::TXT(), r, f);
-    EXPECT_FALSE(hit);
-}
-
-TEST_F(CacheTest, ncache) {
-    Name missing("missing.example.com");
-    cache.addNegative(missing, RRClass::IN(), RRType::DNSKEY(), 8, 30);
-
-    RRsetPtr r;
-    uint32_t f;
-    bool hit = cache.retrieve(missing, RRClass::IN(), RRType::DNSKEY(), r, f);
-
-    EXPECT_TRUE(hit);
-    EXPECT_FALSE(r);
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, f);
-}
-
-TEST_F(CacheTest, overwrite) {
-    EXPECT_EQ(3, cache.getCount());
-
-    RRsetPtr a(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(300)));
-    a->addRdata(in::A("192.0.2.100"));
-
-    EXPECT_NO_THROW(cache.addPositive(a, 16, 30));
-    EXPECT_EQ(3, cache.getCount());
-
-    RRsetPtr r;
-    uint32_t f;
-    bool hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_TRUE(r);
-    EXPECT_EQ(16, f);
-
-    EXPECT_NO_THROW(cache.addNegative(test_name, RRClass::IN(), RRType::A(), 1, 30));
-    EXPECT_EQ(3, cache.getCount());
-
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-    EXPECT_FALSE(r);
-    EXPECT_EQ(DataSrc::REFERRAL, f);
-}
-
-TEST_F(CacheTest, reduceSlots) {
-    EXPECT_EQ(3, cache.getCount());
-    cache.setSlots(2);
-    EXPECT_EQ(2, cache.getCount());
-    cache.setSlots(1);
-    EXPECT_EQ(1, cache.getCount());
-    cache.setSlots(0);
-    EXPECT_EQ(1, cache.getCount());
-}
-
-TEST_F(CacheTest, setEnabled) {
-    cache.setEnabled(false);
-    EXPECT_FALSE(cache.getEnabled());
-    cache.setEnabled(true);
-    EXPECT_TRUE(cache.getEnabled());
-}
-
-TEST_F(CacheTest, disabled) {
-    bool hit;
-    RRsetPtr r;
-    uint32_t f;
-
-    cache.setEnabled(false);
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_FALSE(hit);
-
-    cache.setEnabled(true);
-    hit = cache.retrieve(test_name, RRClass::IN(), RRType::A(), r, f);
-    EXPECT_TRUE(hit);
-
-    EXPECT_EQ(test_name, r->getName());
-    EXPECT_EQ(RRClass::IN(), r->getClass());
-    EXPECT_EQ(RRType::A(), r->getType());
-}
-
-}
diff --git a/src/lib/datasrc/tests/client_list_unittest.cc b/src/lib/datasrc/tests/client_list_unittest.cc
index 3ca906b..d995d5c 100644
--- a/src/lib/datasrc/tests/client_list_unittest.cc
+++ b/src/lib/datasrc/tests/client_list_unittest.cc
@@ -12,11 +12,14 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <util/memory_segment_local.h>
+
 #include <datasrc/client_list.h>
 #include <datasrc/client.h>
 #include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/memory/memory_client.h>
+#include <datasrc/memory/zone_finder.h>
 
 #include <dns/rrclass.h>
 #include <dns/rrttl.h>
@@ -28,6 +31,8 @@
 #include <fstream>
 
 using namespace isc::datasrc;
+using isc::datasrc::memory::InMemoryClient;
+using isc::datasrc::memory::InMemoryZoneFinder;
 using namespace isc::data;
 using namespace isc::dns;
 using namespace boost;
@@ -67,35 +72,47 @@ public:
     };
     class Iterator : public ZoneIterator {
     public:
-        Iterator(const Name& origin) :
+        Iterator(const Name& origin, bool include_ns) :
             origin_(origin),
-            finished_(false),
-            soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(), RRTTL(3600)))
+            soa_(new RRset(origin_, RRClass::IN(), RRType::SOA(),
+                           RRTTL(3600)))
         {
             // The RData here is bogus, but it is not used to anything. There
             // just needs to be some.
             soa_->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
                                                Name::ROOT_NAME(),
                                                0, 0, 0, 0, 0));
+            rrsets_.push_back(soa_);
+
+            if (include_ns) {
+                ns_.reset(new RRset(origin_, RRClass::IN(), RRType::NS(),
+                                    RRTTL(3600)));
+                ns_->addRdata(rdata::generic::NS(Name::ROOT_NAME()));
+                rrsets_.push_back(ns_);
+            }
+            rrsets_.push_back(ConstRRsetPtr());
+
+            it_ = rrsets_.begin();
         }
         virtual isc::dns::ConstRRsetPtr getNextRRset() {
-            if (finished_) {
-                return (ConstRRsetPtr());
-            } else {
-                finished_ = true;
-                return (soa_);
-            }
+            ConstRRsetPtr result = *it_;
+            ++it_;
+            return (result);
         }
         virtual isc::dns::ConstRRsetPtr getSOA() const {
             return (soa_);
         }
     private:
         const Name origin_;
-        bool finished_;
-        const isc::dns::RRsetPtr soa_;
+        const RRsetPtr soa_;
+        RRsetPtr ns_;
+        std::vector<ConstRRsetPtr> rrsets_;
+        std::vector<ConstRRsetPtr>::const_iterator it_;
     };
     // Constructor from a list of zones.
-    MockDataSourceClient(const char* zone_names[]) {
+    MockDataSourceClient(const char* zone_names[]) :
+        have_ns_(true), use_baditerator_(true)
+    {
         for (const char** zone(zone_names); *zone; ++zone) {
             zones.insert(Name(*zone));
         }
@@ -105,7 +122,8 @@ public:
     MockDataSourceClient(const string& type,
                          const ConstElementPtr& configuration) :
         type_(type),
-        configuration_(configuration)
+        configuration_(configuration),
+        have_ns_(true), use_baditerator_(true)
     {
         EXPECT_NE("MasterFiles", type) << "MasterFiles is a special case "
             "and it never should be created as a data source client";
@@ -146,23 +164,27 @@ public:
         isc_throw(isc::NotImplemented, "Not implemented");
     }
     virtual ZoneIteratorPtr getIterator(const Name& name, bool) const {
-        if (name == Name("noiter.org")) {
+        if (use_baditerator_ && name == Name("noiter.org")) {
             isc_throw(isc::NotImplemented, "Asked not to be implemented");
-        } else if (name == Name("null.org")) {
+        } else if (use_baditerator_ && name == Name("null.org")) {
             return (ZoneIteratorPtr());
         } else {
             FindResult result(findZone(name));
             if (result.code == isc::datasrc::result::SUCCESS) {
-                return (ZoneIteratorPtr(new Iterator(name)));
+                return (ZoneIteratorPtr(new Iterator(name, have_ns_)));
             } else {
                 isc_throw(DataSourceError, "No such zone");
             }
         }
     }
+    void disableNS() { have_ns_ = false; }
+    void disableBadIterator() { use_baditerator_ = false; }
     const string type_;
     const ConstElementPtr configuration_;
 private:
     set<Name> zones;
+    bool have_ns_; // control the iterator behavior wrt whether to include NS
+    bool use_baditerator_; // whether to use bogus zone iterators for tests
 };
 
 
@@ -220,8 +242,9 @@ const size_t ds_count = (sizeof(ds_zones) / sizeof(*ds_zones));
 class ListTest : public ::testing::Test {
 public:
     ListTest() :
+        rrclass_(RRClass::IN()),
         // The empty list corresponds to a list with no elements inside
-        list_(new TestedList(RRClass::IN())),
+        list_(new TestedList(rrclass_)),
         config_elem_(Element::fromJSON("["
             "{"
             "   \"type\": \"test_type\","
@@ -238,28 +261,35 @@ public:
             shared_ptr<MockDataSourceClient>
                 ds(new MockDataSourceClient(ds_zones[i]));
             ds_.push_back(ds);
-            ds_info_.push_back(ConfigurableClientList::DataSourceInfo(ds.get(),
-                DataSourceClientContainerPtr(), false));
+            ds_info_.push_back(ConfigurableClientList::DataSourceInfo(
+                                   ds.get(), DataSourceClientContainerPtr(),
+                                   false, rrclass_, mem_sgmt_));
         }
     }
-    void prepareCache(size_t index, const Name& zone, bool prefill = false) {
-        const shared_ptr<InMemoryClient> cache(new InMemoryClient());
-        const shared_ptr<InMemoryZoneFinder>
-            finder(new InMemoryZoneFinder(RRClass::IN(), zone));
-        if (prefill) {
-            RRsetPtr soa(new RRset(zone, RRClass::IN(), RRType::SOA(),
-                                   RRTTL(3600)));
-            // The RData here is bogus, but it is not used to anything. There
-            // just needs to be some.
-            soa->addRdata(rdata::generic::SOA(Name::ROOT_NAME(),
-                                              Name::ROOT_NAME(),
-                                              0, 0, 0, 0, 0));
-            finder->add(soa);
-        }
-        // If we don't do prefill, we leave the zone empty. This way,
-        // we can check when it was reloaded.
-        cache->addZone(finder);
-        list_->getDataSources()[index].cache_ = cache;
+
+    // Install a "fake" cached zone using a temporary underlying data source
+    // client.
+    void prepareCache(size_t index, const Name& zone) {
+        // Prepare the temporary data source client
+        const char* zones[2];
+        const std::string zonename_txt = zone.toText();
+        zones[0] = zonename_txt.c_str();
+        zones[1] = NULL;
+        MockDataSourceClient mock_client(zones);
+        // Disable some default features of the mock to distinguish the
+        // temporary case from normal case.
+        mock_client.disableNS();
+        mock_client.disableBadIterator();
+
+        // Create cache from the temporary data source, and push it to the
+        // client list.
+        const shared_ptr<InMemoryClient> cache(new InMemoryClient(mem_sgmt_,
+                                                                  rrclass_));
+        cache->load(zone, *mock_client.getIterator(zone, false));
+
+        ConfigurableClientList::DataSourceInfo& dsrc_info =
+                list_->getDataSources()[index];
+        dsrc_info.cache_ = cache;
     }
     // Check the positive result is as we expect it.
     void positiveResult(const ClientList::FindResult& result,
@@ -331,6 +361,8 @@ public:
         EXPECT_EQ(cache, list_->getDataSources()[index].cache_ !=
                   shared_ptr<InMemoryClient>());
     }
+    const RRClass rrclass_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
     shared_ptr<TestedList> list_;
     const ClientList::FindResult negative_result_;
     vector<shared_ptr<MockDataSourceClient> > ds_;
@@ -815,39 +847,38 @@ TEST_F(ListTest, BadMasterFile) {
 // Test we can reload a zone
 TEST_F(ListTest, reloadSuccess) {
     list_->configure(config_elem_zones_, true);
-    Name name("example.org");
+    const Name name("example.org");
     prepareCache(0, name);
-    // Not there yet. It would be NXDOMAIN, but it is in apex and
-    // it returns NXRRSET instead.
+    // The cache currently contains a tweaked version of zone, which doesn't
+    // have apex NS.  So the lookup should result in NXRRSET.
     EXPECT_EQ(ZoneFinder::NXRRSET,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
-    // Now reload. It should be there now.
+              list_->find(name).finder_->find(name, RRType::NS())->code);
+    // Now reload the full zone. It should be there now.
     EXPECT_EQ(ConfigurableClientList::ZONE_RELOADED, list_->reload(name));
     EXPECT_EQ(ZoneFinder::SUCCESS,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
+              list_->find(name).finder_->find(name, RRType::NS())->code);
 }
 
 // The cache is not enabled. The load should be rejected.
 TEST_F(ListTest, reloadNotEnabled) {
     list_->configure(config_elem_zones_, false);
-    Name name("example.org");
+    const Name name("example.org");
     // We put the cache in even when not enabled. This won't confuse the thing.
     prepareCache(0, name);
-    // Not there yet. It would be NXDOMAIN, but it is in apex and
-    // it returns NXRRSET instead.
+    // See the reloadSuccess test.  This should result in NXRRSET.
     EXPECT_EQ(ZoneFinder::NXRRSET,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
+              list_->find(name).finder_->find(name, RRType::NS())->code);
     // Now reload. It should reject it.
     EXPECT_EQ(ConfigurableClientList::CACHE_DISABLED, list_->reload(name));
     // Nothing changed here
     EXPECT_EQ(ZoneFinder::NXRRSET,
-              list_->find(name).finder_->find(name, RRType::SOA())->code);
+              list_->find(name).finder_->find(name, RRType::NS())->code);
 }
 
 // Test several cases when the zone does not exist
 TEST_F(ListTest, reloadNoSuchZone) {
     list_->configure(config_elem_zones_, true);
-    Name name("example.org");
+    const Name name("example.org");
     // We put the cache in even when not enabled. This won't confuse the
     // reload method, as that one looks at the real state of things, not
     // at the configuration.
@@ -867,27 +898,27 @@ TEST_F(ListTest, reloadNoSuchZone) {
               list_->find(Name("example.cz")).dsrc_client_);
     EXPECT_EQ(static_cast<isc::datasrc::DataSourceClient*>(NULL),
               list_->find(Name("sub.example.com"), true).dsrc_client_);
-    // Not reloaded
+    // Not reloaded, so NS shouldn't be visible yet.
     EXPECT_EQ(ZoneFinder::NXRRSET,
               list_->find(Name("example.com")).finder_->
-              find(Name("example.com"), RRType::SOA())->code);
+              find(Name("example.com"), RRType::NS())->code);
 }
 
 // Check we gracefuly throw an exception when a zone disappeared in
 // the underlying data source when we want to reload it
 TEST_F(ListTest, reloadZoneGone) {
     list_->configure(config_elem_, true);
-    Name name("example.org");
+    const Name name("example.org");
     // We put in a cache for non-existant zone. This emulates being loaded
     // and then the zone disappearing. We prefill the cache, so we can check
     // it.
-    prepareCache(0, name, true);
-    // The zone contains something
+    prepareCache(0, name);
+    // The (cached) zone contains zone's SOA
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
     // The zone is not there, so abort the reload.
     EXPECT_THROW(list_->reload(name), DataSourceError);
-    // The zone is not hurt.
+    // The (cached) zone is not hurt.
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
 }
@@ -895,8 +926,8 @@ TEST_F(ListTest, reloadZoneGone) {
 // The underlying data source throws. Check we don't modify the state.
 TEST_F(ListTest, reloadZoneThrow) {
     list_->configure(config_elem_zones_, true);
-    Name name("noiter.org");
-    prepareCache(0, name, true);
+    const Name name("noiter.org");
+    prepareCache(0, name);
     // The zone contains stuff now
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
@@ -909,8 +940,8 @@ TEST_F(ListTest, reloadZoneThrow) {
 
 TEST_F(ListTest, reloadNullIterator) {
     list_->configure(config_elem_zones_, true);
-    Name name("null.org");
-    prepareCache(0, name, true);
+    const Name name("null.org");
+    prepareCache(0, name);
     // The zone contains stuff now
     EXPECT_EQ(ZoneFinder::SUCCESS,
               list_->find(name).finder_->find(name, RRType::SOA())->code);
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
deleted file mode 100644
index 36bed1d..0000000
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ /dev/null
@@ -1,1209 +0,0 @@
-// Copyright (C) 2010  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 <stdint.h>
-
-#include <iostream>
-#include <vector>
-#include <string>
-
-#include <gtest/gtest.h>
-
-#include <util/buffer.h>
-
-#include <dns/message.h>
-#include <dns/messagerenderer.h>
-#include <dns/question.h>
-#include <dns/opcode.h>
-#include <dns/rcode.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrttl.h>
-#include <dns/rrtype.h>
-
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-#include <datasrc/sqlite3_datasrc.h>
-#include <datasrc/static_datasrc.h>
-
-#include <testutils/dnsmessage_test.h>
-#include <dns/tests/unittest_util.h>
-#include <datasrc/tests/test_datasrc.h>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::util;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::data;
-using namespace isc::testutils;
-
-namespace {
-ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/example.org.sqlite3\"}");
-
-class DataSrcTest : public ::testing::Test {
-protected:
-    DataSrcTest() : msg(Message::PARSE),
-                    opcodeval(Opcode::QUERY().getCode()), qid(0)
-    {
-        DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc); 
-        sql3_source->init(SQLITE_DBFILE_EXAMPLE);
-        DataSrcPtr test_source = DataSrcPtr(new TestDataSrc);
-        test_source->init();
-        DataSrcPtr static_source = DataSrcPtr(new StaticDataSrc);
-        meta_source.addDataSrc(test_source);
-        meta_source.addDataSrc(sql3_source);
-        meta_source.addDataSrc(static_source);
-    }
-    void QueryCommon(const RRClass& qclass);
-    void createAndProcessQuery(const Name& qname, const RRClass& qclass,
-                               const RRType& qtype, bool need_dnssec);
-
-    HotCache cache;
-    MetaDataSrc meta_source;
-    MessageRenderer renderer;
-    Message msg;
-    const uint16_t opcodeval;
-    qid_t qid;
-};
-
-void
-performQuery(DataSrc& data_source, HotCache& cache, Message& message,
-             bool need_dnssec = true)
-{
-    message.setHeaderFlag(Message::HEADERFLAG_AA);
-    message.setRcode(Rcode::NOERROR());
-    Query q(message, cache, need_dnssec);
-    data_source.doQuery(q);
-}
-
-void
-DataSrcTest::createAndProcessQuery(const Name& qname, const RRClass& qclass,
-                                   const RRType& qtype,
-                                   bool need_dnssec = true)
-{
-    msg.makeResponse();
-    msg.setOpcode(Opcode::QUERY());
-    msg.addQuestion(Question(qname, qclass, qtype));
-    msg.setHeaderFlag(Message::HEADERFLAG_RD);
-    qid = msg.getQid();
-    performQuery(meta_source, cache, msg, need_dnssec);
-}
-
-void
-DataSrcTest::QueryCommon(const RRClass& qclass) {
-    createAndProcessQuery(Name("www.example.com"), qclass, RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("www.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    // XXX: also check ANSWER RRSIG
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, Query) {
-    QueryCommon(RRClass::IN());
-}
-
-// Query class doesn't match any of the data source classes.  The result
-// should be the same as "NxZone".
-TEST_F(DataSrcTest, QueryClassMismatch) {
-    createAndProcessQuery(Name("www.example.com"), RRClass::CH(), RRType::A());
-    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
-                1, 0, 0, 0);
-}
-
-// Query class of any should match the first data source.
-TEST_F(DataSrcTest, QueryClassAny) {
-    QueryCommon(RRClass::ANY());
-}
-
-TEST_F(DataSrcTest, queryClassAnyNegative) {
-    // There was a bug where Class ANY query triggered a crash due to NULL
-    // pointer dereference.  This test checks that condition.
-
-    // NXDOMAIN case
-    createAndProcessQuery(Name("notexistent.example.com"), RRClass::ANY(),
-                          RRType::A());
-    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
-
-    // NXRRSET case
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("www.example.com"), RRClass::ANY(),
-                          RRType::TXT());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
-}
-
-TEST_F(DataSrcTest, queryClassAnyDNAME) {
-    // Class ANY query that would match a DNAME.  Everything including the
-    // synthesized CNAME should be the same as the response to class IN query.
-    createAndProcessQuery(Name("www.dname.example.com"), RRClass::ANY(),
-                          RRType::A(), false);
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 3, 3);
-    rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n"
-                "www.dname.example.com. 3600 IN CNAME www.sql1.example.com.\n"
-                "www.sql1.example.com. 3600 IN A 192.0.2.2\n",
-                msg.beginSection(Message::SECTION_ANSWER),
-                msg.endSection(Message::SECTION_ANSWER));
-
-    // Also check the case of explicit DNAME query.
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("dname.example.com"), RRClass::ANY(),
-                          RRType::DNAME(), false);
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 3, 3);
-    rrsetsCheck("dname.example.com. 3600 IN DNAME sql1.example.com.\n",
-                msg.beginSection(Message::SECTION_ANSWER),
-                msg.endSection(Message::SECTION_ANSWER));
-}
-
-TEST_F(DataSrcTest, queryClassAnyCNAME) {
-    // Similar test for CNAME
-    createAndProcessQuery(Name("foo.example.com"), RRClass::ANY(),
-                          RRType::A(), false);
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 1, 0, 0);
-    rrsetsCheck("foo.example.com. 3600 IN CNAME cnametest.example.net.\n",
-                msg.beginSection(Message::SECTION_ANSWER),
-                msg.endSection(Message::SECTION_ANSWER));
-}
-
-TEST_F(DataSrcTest, NSQuery) {
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::NS());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-// Make sure two successive queries have the same result
-TEST_F(DataSrcTest, DuplicateQuery) {
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::NS());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::NS());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 0, 6);
-
-    rit = msg.beginSection(Message::SECTION_ANSWER);
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DNSKEYQuery) {
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::DNSKEY());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-// Repeat the previous query to check that cache is working correctly.
-// We query for a record at a zone cut to ensure the REFERRAL flag doesn't
-// cause incorrect behavior.
-TEST_F(DataSrcTest, DNSKEYDuplicateQuery) {
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::DNSKEY());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::DNSKEY());
-    rit = msg.beginSection(Message::SECTION_ANSWER);
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::DNSKEY(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, NxRRset) {
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::PTR());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 4, 0);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-}
-
-TEST_F(DataSrcTest, Nxdomain) {
-    createAndProcessQuery(Name("glork.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-    // XXX: check for other authority section answers
-}
-
-TEST_F(DataSrcTest, NxdomainAfterSOAQuery) {
-    // There was a bug where once SOA RR is stored in the hot spot cache
-    // subsequent negative search fails due to "missing SOA".  This test
-    // checks that situation.
-
-    // First, run the scenario with disabling the cache.
-    cache.setEnabled(false);
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::SOA());
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
-                          RRType::A());
-    {
-        SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
-        headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
-                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
-    }
-
-    // Then enable the cache and perform the same queries.  This should
-    // produce the same result.
-    cache.setEnabled(true);
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("example.com"), RRClass::IN(),
-                          RRType::SOA());
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("notexistent.example.com"), RRClass::IN(),
-                        RRType::A());
-    {
-        SCOPED_TRACE("NXDOMAIN after SOA, without hot spot cache");
-        headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
-                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 6, 0);
-    }
-}
-
-TEST_F(DataSrcTest, NxZone) {
-    createAndProcessQuery(Name("spork.example"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
-
-    EXPECT_EQ(Rcode::REFUSED(), msg.getRcode());
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_QR));
-    EXPECT_FALSE(msg.getHeaderFlag(Message::HEADERFLAG_AA));
-    EXPECT_TRUE(msg.getHeaderFlag(Message::HEADERFLAG_RD));
-}
-
-TEST_F(DataSrcTest, Wildcard) {
-    createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("www.wild.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.2", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("*.wild.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-    ++rit;
-    ++rit;
-
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, WildcardNodata) {
-    // Check that a query for a data type not covered by the wildcard
-    // returns NOERROR
-    createAndProcessQuery(Name("www.wild.example.com"), RRClass::IN(),
-                          RRType::AAAA());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 2, 0);
-}
-
-TEST_F(DataSrcTest, DISABLED_WildcardAgainstMultiLabel) {
-    // this qname shouldn't match *.wild.com.com (because * can only match
-    // a single label), and it should result in NXDOMAIN.
-    createAndProcessQuery(Name("www.xxx.wild.example.com"), RRClass::IN(),
-                          RRType::A());
-    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-}
-
-TEST_F(DataSrcTest, WildcardCname) {
-    // Check that wildcard answers containing CNAMES are followed
-    // correctly.  It should result in the same response for both
-    // class IN and ANY queries.
-    const RRClass classes[2] = { RRClass::IN(), RRClass::ANY() };
-
-    for (int i = 0; i < sizeof(classes) / sizeof(classes[0]); ++i) {
-        SCOPED_TRACE("Wildcard + CNAME test for class " + classes[i].toText());
-
-        msg.clear(Message::PARSE);
-
-        createAndProcessQuery(Name("www.wild2.example.com"), classes[i],
-                              RRType::A(), false);
-
-        headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                    QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 3, 3);
-
-        rrsetsCheck("www.wild2.example.com. 3600 IN CNAME www.example.com\n"
-                    "www.example.com. 3600 IN A 192.0.2.1\n",
-                    msg.beginSection(Message::SECTION_ANSWER),
-                    msg.endSection(Message::SECTION_ANSWER));
-        rrsetsCheck("example.com. 3600 IN NS dns01.example.com.\n"
-                    "example.com. 3600 IN NS dns02.example.com.\n"
-                    "example.com. 3600 IN NS dns03.example.com.",
-                    msg.beginSection(Message::SECTION_AUTHORITY),
-                    msg.endSection(Message::SECTION_AUTHORITY));
-        rrsetsCheck("dns01.example.com. 3600 IN A 192.0.2.1\n"
-                    "dns02.example.com. 3600 IN A 192.0.2.2\n"
-                    "dns03.example.com. 3600 IN A 192.0.2.3",
-                    msg.beginSection(Message::SECTION_ADDITIONAL),
-                    msg.endSection(Message::SECTION_ADDITIONAL));
-    }
-}
-
-TEST_F(DataSrcTest, WildcardCnameNodata) {
-    // A wildcard containing a CNAME whose target does not include
-    // data of this type.
-    createAndProcessQuery(Name("www.wild2.example.com"), RRClass::IN(),
-                          RRType::AAAA());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 0);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("www.wild2.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::CNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("www.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("*.wild2.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-    ++rit;
-    ++rit;
-
-    rrset = *rit;
-    EXPECT_EQ(Name("www.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, WildcardCnameNxdomain) {
-    // A wildcard containing a CNAME whose target does not exist
-    createAndProcessQuery(Name("www.wild3.example.com"), RRClass::IN(),
-                          RRType::A());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 6, 0);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("www.wild3.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::CNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("spork.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("*.wild3.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-    ++rit;
-    ++rit;
-
-    rrset = *rit;
-    EXPECT_EQ(Name("foo.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-    ++rit;
-    ++rit;
-
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-TEST_F(DataSrcTest, AuthDelegation) {
-    createAndProcessQuery(Name("www.sql1.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("www.sql1.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.2", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, Dname) {
-    createAndProcessQuery(Name("www.dname.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 5, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("dname.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::DNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("sql1.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    // XXX: check CNAME and A record too
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("dns01.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DnameExact) {
-    // The example.org test zone has a DNAME RR for dname2.foo.example.org.
-    // A query for that name with a different RR type than DNAME shouldn't
-    // confuse delegation processing.
-    createAndProcessQuery(Name("dname2.foo.example.org"), RRClass::IN(),
-                          RRType::A());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-}
-
-TEST_F(DataSrcTest, Cname) {
-    createAndProcessQuery(Name("foo.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 0, 0);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("foo.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::CNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("cnametest.example.net.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, CnameInt) {
-    createAndProcessQuery(Name("cname-int.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("cname-int.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::CNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("www.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    // XXX: check a record as well
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, CnameExt) {
-    createAndProcessQuery(Name("cname-ext.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 4, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("cname-ext.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::CNAME(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("www.sql1.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("sql1.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, Delegation) {
-    createAndProcessQuery(Name("www.subzone.example.com"), RRClass::IN(),
-                          RRType::A());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_FALSE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, NSDelegation) {
-    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
-                          RRType::NS());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_FALSE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, ANYZonecut) {
-    // An ANY query at a zone cut should behave the same as any other
-    // delegation
-    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
-                          RRType::ANY());
-}
-
-TEST_F(DataSrcTest, NSECZonecut) {
-    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
-                          RRType::NSEC());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 2, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
-    EXPECT_EQ(RRType::NSEC(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DNAMEZonecut) {
-    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
-                          RRType::DNAME());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 5, 2);
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("ns1.subzone.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_FALSE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("ns1.subzone.example.com"), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ("192.0.2.1", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, DS) {
-    createAndProcessQuery(Name("subzone.example.com"), RRClass::IN(),
-                          RRType::DS());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 3, 4, 6);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_ANSWER);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("subzone.example.com."), rrset->getName());
-    EXPECT_EQ(RRType::DS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    rrset = *rit;
-    EXPECT_EQ(Name("example.com"), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ("dns01.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns02.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_EQ("dns03.example.com.", it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-TEST_F(DataSrcTest, CNAMELoop) {
-    createAndProcessQuery(Name("one.loop.example"), RRClass::IN(),
-                          RRType::A());
-    EXPECT_EQ(Rcode::NOERROR(), msg.getRcode());
-
-    // one.loop.example points to two.loop.example, which points back
-    // to one.loop.example, so there should be exactly two CNAME records
-    // in the answer.
-    EXPECT_EQ(2, msg.getRRCount(Message::SECTION_ANSWER));
-}
-
-// NSEC query for the name of a zone cut for non-secure delegation.
-// Should return normal referral.
-TEST_F(DataSrcTest, NSECZonecutOfNonsecureZone) {
-    createAndProcessQuery(Name("sub.example.org"), RRClass::IN(),
-                          RRType::NSEC());
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 1, 1);
-
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    ConstRRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("sub.example.org."), rrset->getName());
-    EXPECT_EQ(RRType::NS(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    RdataIteratorPtr it = rrset->getRdataIterator();
-    EXPECT_EQ(createRdata(RRType::NS(), RRClass::IN(),
-                          "ns.sub.example.org.")->toText(),
-              it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-
-    rit = msg.beginSection(Message::SECTION_ADDITIONAL);
-    rrset = *rit;
-    EXPECT_EQ(Name("ns.sub.example.org."), rrset->getName());
-    EXPECT_EQ(RRType::A(), rrset->getType());
-    EXPECT_EQ(RRClass::IN(), rrset->getClass());
-
-    it = rrset->getRdataIterator();
-    EXPECT_EQ(createRdata(RRType::A(), RRClass::IN(), "192.0.2.101")->toText(),
-              it->getCurrent().toText());
-    it->next();
-    EXPECT_TRUE(it->isLast());
-}
-
-// Test sending a DS query to root (nonsense, but it should survive)
-TEST_F(DataSrcTest, RootDSQuery1) {
-    EXPECT_NO_THROW(createAndProcessQuery(Name("."), RRClass::IN(),
-                                          RRType::DS()));
-    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
-}
-
-// The same, but when we have the root zone
-// (which triggers rfc4035 section 3.1.4.1)
-TEST_F(DataSrcTest, RootDSQuery2) {
-    // The message
-    msg.makeResponse();
-    msg.setOpcode(Opcode::QUERY());
-    msg.addQuestion(Question(Name("."), RRClass::IN(), RRType::DS()));
-    msg.setHeaderFlag(Message::HEADERFLAG_RD);
-    // Prepare the source
-    DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc);
-    ConstElementPtr sqlite_root = Element::fromJSON(
-        "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
-    EXPECT_NO_THROW(sql3_source->init(sqlite_root));
-    // Make the query
-    EXPECT_NO_THROW(performQuery(*sql3_source, cache, msg));
-
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-}
-
-TEST_F(DataSrcTest, DSQueryFromCache) {
-    // explicitly enable hot spot cache
-    cache.setEnabled(true);
-
-    // The first query will create a negative cache for example.org/CNAME
-    createAndProcessQuery(Name("example.org"), RRClass::IN(), RRType::SOA());
-
-    // the cached CNAME shouldn't confuse subsequent query.
-    // there may be several different possible cases that could trigger a bug,
-    // but DS query is the only known example.
-    msg.clear(Message::PARSE);
-    createAndProcessQuery(Name("example.org"), RRClass::IN(), RRType::DS());
-
-    // returning refused is probably a bad behavior, but it's a different
-    // issue -- see Trac Ticket #306.
-    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 0, 0);
-}
-
-// Non-existent name in the "static" data source.  The purpose of this test
-// is to check a corner case behavior when atypical RRClass (CH in this case)
-// is specified.
-TEST_F(DataSrcTest, StaticNxDomain) {
-    createAndProcessQuery(Name("www.version.bind"), RRClass::CH(),
-                          RRType::TXT());
-    headerCheck(msg, qid, Rcode::NXDOMAIN(), opcodeval,
-                QR_FLAG | AA_FLAG | RD_FLAG, 1, 0, 1, 0);
-    RRsetIterator rit = msg.beginSection(Message::SECTION_AUTHORITY);
-    RRsetPtr rrset = *rit;
-    EXPECT_EQ(Name("version.bind"), rrset->getName());
-    EXPECT_EQ(RRType::SOA(), rrset->getType());
-    EXPECT_EQ(RRClass::CH(), rrset->getClass());
-}
-
-TEST_F(DataSrcTest, Nsec3Hash) {
-    vector<uint8_t> salt;
-    salt.push_back(0xfe);
-    salt.push_back(0xed);
-    salt.push_back(0xab);
-    salt.push_back(0xee);
-    Nsec3Param nsec3(1, 0, 10, salt);
-    EXPECT_EQ("VIR9KJAPN2FHRLS6EP0JBQ89MBLUE296", nsec3.getHash(Name("test1")));
-    EXPECT_EQ("FHA27EURONFH5640SFJQ8MJAKMCVB7UJ", nsec3.getHash(Name("test2")));
-    EXPECT_EQ("A4M93LR7A60IDDQMO6TCVUPCC60CU38A", nsec3.getHash(Name("test3")));
-}
-
-TEST_F(DataSrcTest, AddRemoveDataSrc) {
-    MetaDataSrc ds;
-    ConstDataSrcPtr tsp = ConstDataSrcPtr(new TestDataSrc);
-    EXPECT_EQ(0, ds.dataSrcCount());
-    ds.addDataSrc(tsp);
-    EXPECT_EQ(1, ds.dataSrcCount());
-    ds.removeDataSrc(tsp);
-    EXPECT_EQ(0, ds.dataSrcCount());
-}
-
-TEST_F(DataSrcTest, noNSZone) {
-    EXPECT_THROW(createAndProcessQuery(Name("www.nons.example"),
-                                       RRClass::IN(), RRType::A()),
-                 DataSourceError);
-}
-
-TEST_F(DataSrcTest, noNSButDnameZone) {
-    EXPECT_THROW(createAndProcessQuery(Name("www.nons-dname.example"),
-                                       RRClass::IN(), RRType::A()),
-                 DataSourceError);
-}
-
-TEST_F(DataSrcTest, noSOAZone) {
-    EXPECT_THROW(createAndProcessQuery(Name("notexist.nosoa.example"),
-                                       RRClass::IN(), RRType::A()),
-                 DataSourceError);
-}
-
-TEST_F(DataSrcTest, apexCNAMEZone) {
-    // The query name doesn't exist in the best matching zone,
-    // and there's a CNAME at the apex (which is bogus), so query handling
-    // will fail due to missing SOA.
-    EXPECT_THROW(createAndProcessQuery(Name("notexist.apexcname.example"),
-                                       RRClass::IN(), RRType::A()),
-                 DataSourceError);
-}
-
-TEST_F(DataSrcTest, incompleteGlue) {
-    // One of the NS names belong to a different zone (which is still
-    // authoritative), and the glue is missing in that zone.  We should
-    // still return the existent glue.
-    // (nons.example is also broken in that it doesn't have apex NS, but
-    // that doesn't matter for this test)
-    createAndProcessQuery(Name("www.incompletechild.nons.example"),
-                          RRClass::IN(), RRType::A());
-    headerCheck(msg, qid, Rcode::NOERROR(), opcodeval,
-                QR_FLAG | RD_FLAG, 1, 0, 2, 1);
-    rrsetsCheck("incompletechild.nons.example. 3600 IN NS ns.incompletechild.nons.example.\n"
-                "incompletechild.nons.example. 3600 IN NS nx.nosoa.example.",
-                msg.beginSection(Message::SECTION_AUTHORITY),
-                msg.endSection(Message::SECTION_AUTHORITY));
-    rrsetsCheck("ns.incompletechild.nons.example. 3600 IN A 192.0.2.1",
-                msg.beginSection(Message::SECTION_ADDITIONAL),
-                msg.endSection(Message::SECTION_ADDITIONAL));
-}
-
-// currently fails
-TEST_F(DataSrcTest, DISABLED_synthesizedCnameTooLong) {
-    // qname has the possible max length (255 octets).  it matches a DNAME,
-    // and the synthesized CNAME would exceed the valid length.
-    createAndProcessQuery(
-        Name("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
-             "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
-             "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde."
-             "0123456789abcdef0123456789abcdef0123456789a.dname.example.org."),
-        RRClass::IN(), RRType::A());
-}
-
-TEST_F(DataSrcTest, cacheDataInNonexistentZone) {
-    // This test emulates the situation where an RRset in some zone of some
-    // data source is cached and then the zone is removed from the data source.
-    // When there is such a substantial inconsistency between the cache and
-    // the real data source, we should honor the latter.  More important,
-    // the inconsistency shouldn't cause any disruption such as a crash.
-
-    const Name qname("nosuchzone.example");
-    RRsetPtr rrset(new RRset(qname, RRClass::IN(), RRType::A(), RRTTL(0)));
-    cache.addPositive(rrset, DataSrc::REFERRAL);
-
-    createAndProcessQuery(qname, RRClass::IN(), RRType::A(), false);
-    headerCheck(msg, qid, Rcode::REFUSED(), opcodeval, QR_FLAG | RD_FLAG,
-                1, 0, 0, 0);
-}
-
-// Tests of the DataSrcMatch class start here
-class DataSrcMatchTest : public ::testing::Test {
-protected:
-    DataSrcMatchTest() {
-        datasrc1.init();
-    }
-    // test data source serves example.com/IN.
-    TestDataSrc datasrc1;
-    // this data source is dummy.  Its content doesn't matter in the tests.
-    TestDataSrc datasrc2;
-};
-
-TEST_F(DataSrcMatchTest, match) {
-    DataSrcMatch match(Name("very.very.long.example.com"), RRClass::IN());
-    datasrc1.findClosestEnclosure(match);
-    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, matchWithWrongClass) {
-    DataSrcMatch match(Name("very.very.long.example.com"), RRClass::CH());
-    datasrc1.findClosestEnclosure(match);
-    // XXX: some deviant compilers seem to fail to recognize a NULL as a
-    // pointer type.  This explicit cast works around such compilers.
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, matchWithAnyClass) {
-    DataSrcMatch match(Name("very.very.long.example.com"), RRClass::ANY());
-    datasrc1.findClosestEnclosure(match);
-    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateWithWrongClass) {
-    DataSrcMatch match(Name("www.example.com"), RRClass::CH());
-
-    EXPECT_EQ(RRClass::IN(), datasrc2.getClass());
-    match.update(datasrc2, Name("com"));
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-
-    EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
-    match.update(datasrc1, Name("example.com"));
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateAgainstAnyClass) {
-    DataSrcMatch match(Name("www.example.com"), RRClass::ANY());
-    match.update(datasrc2, Name("com"));
-    EXPECT_EQ(Name("com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc2, match.getDataSource());
-
-    // the given class for search is ANY, so update should be okay.
-    EXPECT_EQ(RRClass::IN(), datasrc1.getClass());
-    match.update(datasrc1, Name("example.com"));
-    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateWithNoMatch) {
-    DataSrcMatch match(Name("www.example.com"), RRClass::IN());
-    match.update(datasrc1, Name("com"));
-    EXPECT_EQ(Name("com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-
-    // An attempt of update with a name that doesn't match.  This attempt
-    // should be ignored.
-    match.update(datasrc2, Name("example.org"));
-    EXPECT_EQ(Name("com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, initialUpdateWithNoMatch) {
-    DataSrcMatch match(Name("www.example.com"), RRClass::IN());
-
-    // An initial attempt of update with a name that doesn't match.
-    // Should be ignored.
-    match.update(datasrc1, Name("example.org"));
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(DataSrcMatchTest, updateWithShorterMatch) {
-    DataSrcMatch match(Name("www.example.com"), RRClass::IN());
-
-    match.update(datasrc1, Name("example.com"));
-    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-
-    // An attempt of update with a name that gives a shorter match.
-    // This attempt should be ignored.
-    match.update(datasrc2, Name("com"));
-    EXPECT_EQ(Name("example.com"), *match.getEnclosingZone());
-    EXPECT_EQ(&datasrc1, match.getDataSource());
-}
-
-}
diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc
index b702d28..f53fbd7 100644
--- a/src/lib/datasrc/tests/faked_nsec3.cc
+++ b/src/lib/datasrc/tests/faked_nsec3.cc
@@ -67,6 +67,17 @@ public:
             "00000000000000000000000000000000";
         map_[Name("largest.example.org")] =
             "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
+
+        // These are used by the findNSEC3Walk test.
+        map_[Name("n0.example.org")] = "00000000000000000000000000000000";
+        map_[Name("n1.example.org")] = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+        map_[Name("n2.example.org")] = "02UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+        map_[Name("n3.example.org")] = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+        map_[Name("n4.example.org")] = "11111111111111111111111111111111";
+        map_[Name("n5.example.org")] = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+        map_[Name("n6.example.org")] = "44444444444444444444444444444444";
+        map_[Name("n7.example.org")] = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+        map_[Name("n8.example.org")] = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
     }
     virtual string calculate(const Name& name) const {
         const NSEC3HashMap::const_iterator found = map_.find(name);
@@ -94,6 +105,12 @@ NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3&) const {
     return (new TestNSEC3Hash);
 }
 
+NSEC3Hash* TestNSEC3HashCreator::create(uint8_t, uint16_t,
+                                        const uint8_t*, size_t) const
+{
+    return (new TestNSEC3Hash);
+}
+
 void
 findNSEC3Check(bool expected_matched, uint8_t expected_labels,
                const string& expected_closest,
diff --git a/src/lib/datasrc/tests/faked_nsec3.h b/src/lib/datasrc/tests/faked_nsec3.h
index 8c1857c..26a7b8d 100644
--- a/src/lib/datasrc/tests/faked_nsec3.h
+++ b/src/lib/datasrc/tests/faked_nsec3.h
@@ -57,11 +57,15 @@ class TestNSEC3HashCreator : public isc::dns::NSEC3HashCreator {
 private:
     class TestNSEC3Hash;
 public:
+    TestNSEC3HashCreator() {}
     virtual isc::dns::NSEC3Hash* create(const
                                         isc::dns::rdata::generic::NSEC3PARAM&)
         const;
     virtual isc::dns::NSEC3Hash* create(const isc::dns::rdata::generic::NSEC3&)
         const;
+    virtual isc::dns::NSEC3Hash* create(uint8_t, uint16_t,
+                                        const uint8_t*, size_t)
+        const;
 };
 
 // Check the result against expected values. It directly calls EXPECT_ macros
diff --git a/src/lib/datasrc/tests/memory/.gitignore b/src/lib/datasrc/tests/memory/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/datasrc/tests/memory/Makefile.am b/src/lib/datasrc/tests/memory/Makefile.am
new file mode 100644
index 0000000..37e9043
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/Makefile.am
@@ -0,0 +1,51 @@
+SUBDIRS = testdata .
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/lib/dns
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_srcdir)/testdata\"
+
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += rdata_serialization_unittest.cc
+run_unittests_SOURCES += rdataset_unittest.cc
+run_unittests_SOURCES += domaintree_unittest.cc
+run_unittests_SOURCES += treenode_rrset_unittest.cc
+run_unittests_SOURCES += zone_table_unittest.cc
+run_unittests_SOURCES += zone_data_unittest.cc
+run_unittests_SOURCES += zone_finder_unittest.cc
+run_unittests_SOURCES += ../../tests/faked_nsec3.h ../../tests/faked_nsec3.cc
+run_unittests_SOURCES += memory_segment_test.h
+run_unittests_SOURCES += segment_object_holder_unittest.cc
+run_unittests_SOURCES += memory_client_unittest.cc
+run_unittests_SOURCES += zone_table_segment_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/datasrc/libb10-datasrc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/testutils/libb10-testutils.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/datasrc/tests/memory/domaintree_unittest.cc b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
new file mode 100644
index 0000000..cb16e02
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/domaintree_unittest.cc
@@ -0,0 +1,1292 @@
+// Copyright (C) 2010  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 <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/domaintree.h>
+
+#include <dns/tests/unittest_util.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dns;
+using isc::UnitTestUtil;
+using namespace isc::datasrc::memory;
+
+// XXX: some compilers cannot find class static constants used in
+// EXPECT_xxx macros, for which we need an explicit empty definition.
+const size_t Name::MAX_LABELS;
+
+/* The initial structure of dtree
+ *
+ *             .
+ *             |
+ *             b
+ *           /   \
+ *          a    d.e.f
+ *              /  |   \
+ *             c   |    g.h
+ *                 |     |
+ *                w.y    i
+ *              /  |  \   \
+ *             x   |   z   k
+ *                 |   |
+ *                 p   j
+ *               /   \
+ *              o     q
+ */
+
+namespace {
+
+void deleteData(int* i) {
+    delete i;
+}
+
+typedef DomainTree<int> TestDomainTree;
+typedef DomainTreeNode<int> TestDomainTreeNode;
+typedef DomainTreeNodeChain<int> TestDomainTreeNodeChain;
+
+class TreeHolder {
+public:
+    TreeHolder(util::MemorySegment& mem_sgmt, TestDomainTree* tree) :
+        mem_sgmt_(mem_sgmt), tree_(tree)
+    {}
+    ~TreeHolder() {
+        TestDomainTree::destroy(mem_sgmt_, tree_, deleteData);
+    }
+    TestDomainTree* get() { return (tree_); }
+private:
+    util::MemorySegment& mem_sgmt_;
+    TestDomainTree* tree_;
+};
+
+class DomainTreeTest : public::testing::Test {
+protected:
+    DomainTreeTest() :
+        dtree_holder_(mem_sgmt_, TestDomainTree::create(mem_sgmt_)),
+        dtree_expose_empty_node_holder_(mem_sgmt_,
+                                         TestDomainTree::create(mem_sgmt_, true)),
+        dtree(*dtree_holder_.get()),
+        dtree_expose_empty_node(*dtree_expose_empty_node_holder_.get()),
+        cdtnode(NULL)
+    {
+        const char* const domain_names[] = {
+            "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+            "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+        int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+        for (int i = 0; i < name_count; ++i) {
+            dtree.insert(mem_sgmt_, Name(domain_names[i]), &dtnode);
+            // Check the node doesn't have any data initially.
+            EXPECT_EQ(static_cast<int*>(NULL),
+                      dtnode->setData(new int(i + 1)));
+
+            dtree_expose_empty_node.insert(mem_sgmt_, Name(domain_names[i]),
+                                            &dtnode);
+            EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(i + 1)));
+        }
+    }
+
+    util::MemorySegmentLocal mem_sgmt_;
+    TreeHolder dtree_holder_;
+    TreeHolder dtree_expose_empty_node_holder_;
+    TestDomainTree& dtree;
+    TestDomainTree& dtree_expose_empty_node;
+    TestDomainTreeNode* dtnode;
+    const TestDomainTreeNode* cdtnode;
+    uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH];
+};
+
+TEST_F(DomainTreeTest, nodeCount) {
+    EXPECT_EQ(15, dtree.getNodeCount());
+
+    // Delete all nodes, then the count should be set to 0.  This also tests
+    // the behavior of deleteAllNodes().
+    dtree.deleteAllNodes(mem_sgmt_, deleteData);
+    EXPECT_EQ(0, dtree.getNodeCount());
+}
+
+TEST_F(DomainTreeTest, setGetData) {
+    // set new data to an existing node.  It should have some data.
+    int* newdata = new int(11);
+    int* olddata = dtnode->setData(newdata);
+    EXPECT_NE(static_cast<int*>(NULL), olddata);
+    deleteData(olddata);
+    EXPECT_EQ(11, *(dtnode->getData()));
+
+    // clear the node.  we should get the new data back we just passed.
+    olddata = dtnode->setData(NULL);
+    EXPECT_EQ(newdata, olddata);
+    deleteData(olddata);
+}
+
+TEST_F(DomainTreeTest, insertNames) {
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
+                                                        Name("d.e.f"),
+                                                        &dtnode));
+    EXPECT_EQ(Name("d.e.f"), dtnode->getName());
+    EXPECT_EQ(15, dtree.getNodeCount());
+
+    // insert not exist node
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("0"),
+                                                  &dtnode));
+    EXPECT_EQ(Name("0"), dtnode->getName());
+    EXPECT_EQ(16, dtree.getNodeCount());
+
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+                                                  Name("example.com"),
+                                                  &dtnode));
+    EXPECT_EQ(17, dtree.getNodeCount());
+    // add data to it; also make sure it doesn't have data right now
+    // (otherwise it would leak)
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(12)));
+
+    // return ALREADYEXISTS, since node "example.com" already has
+    // been explicitly inserted
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_,
+                                                        Name("example.com"),
+                                                        &dtnode));
+    EXPECT_EQ(17, dtree.getNodeCount());
+
+    // split the node "d.e.f"
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k.e.f"),
+                                                  &dtnode));
+    EXPECT_EQ(Name("k"), dtnode->getName());
+    EXPECT_EQ(19, dtree.getNodeCount());
+
+    // split the node "g.h"
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("h"),
+                                                        &dtnode));
+    EXPECT_EQ(Name("h"), dtnode->getName());
+    EXPECT_EQ(20, dtree.getNodeCount());
+
+    // add child domain
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+                                                  Name("m.p.w.y.d.e.f"),
+                                                  &dtnode));
+    EXPECT_EQ(Name("m"), dtnode->getName());
+    EXPECT_EQ(21, dtree.getNodeCount());
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+                                                  Name("n.p.w.y.d.e.f"),
+                                                  &dtnode));
+    EXPECT_EQ(Name("n"), dtnode->getName());
+    EXPECT_EQ(22, dtree.getNodeCount());
+
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l.a"),
+                                                  &dtnode));
+    EXPECT_EQ(Name("l"), dtnode->getName());
+    EXPECT_EQ(23, dtree.getNodeCount());
+
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("r.d.e.f"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("s.d.e.f"),
+                                                  &dtnode));
+    EXPECT_EQ(25, dtree.getNodeCount());
+
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+                                                  Name("h.w.y.d.e.f"),
+                                                  &dtnode));
+
+    // add more nodes one by one to cover leftRotate and rightRotate
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt_, Name("f"),
+                                                        &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("m"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("nm"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("om"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("k"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("l"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("fe"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ge"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("i"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("ae"),
+                                                  &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_, Name("n"),
+                                                  &dtnode));
+}
+
+TEST_F(DomainTreeTest, subTreeRoot) {
+    // This is a testcase for a particular issue that went unchecked in
+    // #2089's implementation, but was fixed in #2092. The issue was
+    // that when a node was fissioned, FLAG_SUBTREE_ROOT was not being
+    // copied correctly.
+
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("d.e.f"),
+                                              &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
+                                              &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
+                                              &dtnode));
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("example.com"),
+                                              &dtnode));
+    EXPECT_EQ(TestDomainTree::SUCCESS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("k.e.f"),
+                                              &dtnode));
+
+    // "g.h" is not a subtree root
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("g.h"), &dtnode));
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+    // fission the node "g.h"
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("h"),
+                                              &dtnode));
+
+    // the node "h" (h.down_ -> "g") should not be a subtree root. "g"
+    // should be a subtree root.
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+    // "g.h" should be a subtree root now.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("g.h"), &dtnode));
+    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+}
+
+TEST_F(DomainTreeTest, additionalNodeFission) {
+    // These are additional nodeFission tests added by #2054's rewrite
+    // of DomainTree::nodeFission(). These test specific corner cases that
+    // are not covered by other tests.
+
+    // Insert "t.0" (which becomes the left child of its parent)
+    EXPECT_EQ(TestDomainTree::SUCCESS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("t.0"),
+                                             &dtnode));
+
+    // "t.0" is not a subtree root
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("t.0"), &dtnode));
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+    // fission the node "t.0"
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS,
+              dtree_expose_empty_node.insert(mem_sgmt_, Name("0"),
+                                             &dtnode));
+
+    // the node "0" ("0".down_ -> "t") should not be a subtree root. "t"
+    // should be a subtree root.
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+
+    // "t.0" should be a subtree root now.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("t.0"), &dtnode));
+    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_SUBTREE_ROOT));
+}
+
+TEST_F(DomainTreeTest, findName) {
+    // find const dtnode
+    // exact match
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode));
+    EXPECT_EQ(Name("a"), cdtnode->getName());
+
+    // not found
+    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("d.e.f"), &cdtnode));
+    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("y.d.e.f"), &cdtnode));
+    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("x"), &cdtnode));
+    EXPECT_EQ(TestDomainTree::NOTFOUND, dtree.find(Name("m.n"), &cdtnode));
+
+    // if we expose empty node, we can get the empty node created during insert
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("d.e.f"), &cdtnode));
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name("w.y.d.e.f"), &cdtnode));
+
+    // partial match
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH, dtree.find(Name("m.b"), &cdtnode));
+    EXPECT_EQ(Name("b"), cdtnode->getName());
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              dtree_expose_empty_node.find(Name("m.d.e.f"), &cdtnode));
+
+    // find dtnode
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("q.w.y.d.e.f"),
+                                                   &dtnode));
+    EXPECT_EQ(Name("q"), dtnode->getName());
+}
+
+TEST_F(DomainTreeTest, findError) {
+    // For the version that takes a node chain, the chain must be empty.
+    TestDomainTreeNodeChain chain;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("a"), &cdtnode,
+                                                   chain));
+    // trying to reuse the same chain.  it should result in an exception.
+    EXPECT_THROW(dtree.find(Name("a"), &cdtnode, chain),
+                 BadValue);
+}
+
+TEST_F(DomainTreeTest, flags) {
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt_,
+                                                  Name("flags.example"),
+                                                  &dtnode));
+
+    // by default, flags are all off
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // set operation, by default it enables the flag
+    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // try disable the flag explicitly
+    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // try enable the flag explicitly
+    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, true);
+    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // setting an unknown flag will trigger an exception
+    EXPECT_THROW(dtnode->setFlag(static_cast<TestDomainTreeNode::Flags>(2), true),
+                 isc::InvalidParameter);
+}
+
+bool
+testCallback(const TestDomainTreeNode&, bool* callback_checker) {
+    *callback_checker = true;
+    return (false);
+}
+
+template <typename T>
+void
+performCallbackTest(TestDomainTree& dtree,
+                    util::MemorySegmentLocal& mem_sgmt,
+                    const T& name_called,
+                    const T& name_not_called)
+{
+    TestDomainTreeNode* dtnode;
+    const TestDomainTreeNode* cdtnode;
+
+    // by default callback isn't enabled
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
+                                                  Name("callback.example"),
+                                                  &dtnode));
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // enable/re-disable callback
+    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK, false);
+    EXPECT_FALSE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // enable again for subsequent tests
+    dtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+    // add more levels below and above the callback node for partial match.
+    TestDomainTreeNode* subdtnode;
+    EXPECT_EQ(TestDomainTree::SUCCESS, dtree.insert(mem_sgmt,
+                                                  Name("sub.callback.example"),
+                                                  &subdtnode));
+    EXPECT_EQ(static_cast<int*>(NULL), subdtnode->setData(new int(2)));
+    TestDomainTreeNode* parentdtnode;
+    EXPECT_EQ(TestDomainTree::ALREADYEXISTS, dtree.insert(mem_sgmt,
+                                                        Name("example"),
+                                                        &parentdtnode));
+    // the child/parent nodes shouldn't "inherit" the callback flag.
+    // "dtnode" may be invalid due to the insertion, so we need to re-find
+    // it.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("callback.example"),
+                                                   &dtnode));
+    EXPECT_TRUE(dtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+    EXPECT_FALSE(subdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+    EXPECT_FALSE(parentdtnode->getFlag(TestDomainTreeNode::FLAG_CALLBACK));
+
+    // check if the callback is called from find()
+    TestDomainTreeNodeChain node_path1;
+    bool callback_called = false;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree.find(name_called, &cdtnode, node_path1,
+                          testCallback, &callback_called));
+    EXPECT_TRUE(callback_called);
+
+    // enable callback at the parent node, but it doesn't have data so
+    // the callback shouldn't be called.
+    TestDomainTreeNodeChain node_path2;
+    parentdtnode->setFlag(TestDomainTreeNode::FLAG_CALLBACK);
+    callback_called = false;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree.find(name_not_called, &cdtnode, node_path2,
+                          testCallback, &callback_called));
+    EXPECT_FALSE(callback_called);
+}
+
+TEST_F(DomainTreeTest, callbackName) {
+    const Name n1("sub.callback.example");
+    const Name n2("callback.example");
+
+    performCallbackTest(dtree, mem_sgmt_, n1, n2);
+}
+
+TEST_F(DomainTreeTest, callbackLabelSequence) {
+    const Name n1("sub.callback.example");
+    const Name n2("callback.example");
+    const LabelSequence ls1(n1);
+    const LabelSequence ls2(n2);
+
+    performCallbackTest(dtree, mem_sgmt_, ls1, ls2);
+}
+
+TEST_F(DomainTreeTest, findInSubTree) {
+    // For the version that takes a node chain, the chain must be empty.
+    DomainTreeNodeChain<int> chain;
+    bool flag;
+
+    // Searching for a non-absolute (right-stripped) label sequence when
+    // chain is empty should throw.
+    const Name n0("w.y.d.e.f");
+    LabelSequence ls0(n0);
+    ls0.stripRight(1);
+    EXPECT_THROW(dtree_expose_empty_node.find(ls0, &cdtnode, chain,
+                                              testCallback, &flag),
+                 isc::BadValue);
+
+    // First, find a sub-tree node
+    chain.clear();
+    const LabelSequence ls1(n0);
+    DomainTree<int>::Result result =
+        dtree_expose_empty_node.find(ls1, &cdtnode, chain,
+                                     testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n0, chain.getAbsoluteName());
+
+    // Searching for an absolute label sequence when chain is already
+    // populated should throw.
+    const Name n2a("o");
+    const LabelSequence ls2a(n2a);
+    EXPECT_THROW(dtree_expose_empty_node.find(ls2a, &cdtnode, chain,
+                                              testCallback, &flag),
+                 isc::BadValue);
+
+    // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
+    // suffix to "o" (non-absolute).
+    const Name n2("o.w.y.d.e.f");
+    LabelSequence ls2(n2);
+    ls2.stripRight(6);
+    EXPECT_EQ("o", ls2.toText());
+
+    result = dtree_expose_empty_node.find(ls2, &cdtnode, chain,
+                                          testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n2, chain.getAbsoluteName());
+
+    // Another test. Start with "d.e.f." node.
+    chain.clear();
+    const Name n3("d.e.f");
+    const LabelSequence ls3(n3);
+    result =
+        dtree_expose_empty_node.find(ls3, &cdtnode, chain,
+                                     testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n3, chain.getAbsoluteName());
+
+    // Now, find "o.w.y.d.e.f." by right-stripping the "w.y.d.e.f."
+    // suffix to "o.w.y" (non-absolute).
+    const Name n4("o.w.y.d.e.f");
+    LabelSequence ls4(n2);
+    ls4.stripRight(4);
+    EXPECT_EQ("o.w.y", ls4.toText());
+
+    result = dtree_expose_empty_node.find(ls4, &cdtnode, chain,
+                                          testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n4, chain.getAbsoluteName());
+}
+
+TEST_F(DomainTreeTest, findInSubTreeSameLabelSequence) {
+    // For the version that takes a node chain, the chain must be empty.
+    DomainTreeNodeChain<int> chain;
+    bool flag;
+
+    const Name n1("c.g.h");
+
+    // First insert a "c.g.h." node.
+    dtree_expose_empty_node.insert(mem_sgmt_, n1, &dtnode);
+
+    /* Now, the tree looks like:
+     *
+     *             .
+     *             |
+     *             b
+     *           /   \
+     *          a    d.e.f
+     *              /  |  \____
+     *             c   |       \
+     *                 |        g.h
+     *                 |         |
+     *                w.y        i
+     *              /  |  \     / \
+     *             x   |   z   c   k
+     *                 |   |
+     *                 p   j
+     *               /   \
+     *              o     q
+     */
+
+    // Make a non-absolute label sequence. We will search for this same
+    // sequence in two places in the tree.
+    LabelSequence ls1(n1);
+    ls1.stripRight(3);
+    EXPECT_EQ("c", ls1.toText());
+
+    // First, find "g.h."
+    const Name n2("g.h");
+    const LabelSequence ls2(n2);
+    DomainTree<int>::Result result =
+        dtree_expose_empty_node.find(ls2, &cdtnode, chain,
+                                     testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n2, chain.getAbsoluteName());
+
+    // Now, find "c.g.h." by searching just the non-absolute ls1 label
+    // sequence.
+    result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
+                                          testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n1, chain.getAbsoluteName());
+
+    // Now, find "." (the root node)
+    chain.clear();
+    const Name n3(".");
+    const LabelSequence ls3(n3);
+    result =
+        dtree_expose_empty_node.find(ls3, &cdtnode, chain,
+                                     testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(n3, chain.getAbsoluteName());
+
+    // Now, find "c." by searching just the non-absolute ls1 label
+    // sequence.
+    result = dtree_expose_empty_node.find(ls1, &cdtnode, chain,
+                                          testCallback, &flag);
+    EXPECT_EQ(DomainTree<int>::EXACTMATCH, result);
+    EXPECT_EQ(Name("c."), chain.getAbsoluteName());
+}
+
+TEST_F(DomainTreeTest, chainLevel) {
+    TestDomainTreeNodeChain chain;
+
+    // by default there should be no level in the chain.
+    EXPECT_EQ(0, chain.getLevelCount());
+
+    // Copy should be consistent
+    TestDomainTreeNodeChain chain2(chain);
+    EXPECT_EQ(chain.getLevelCount(), chain2.getLevelCount());
+
+    // insert one node to the tree and find it.  there should be exactly
+    // one level in the chain.
+    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_, true));
+    TestDomainTree& tree(*tree_holder.get());
+    Name node_name(Name::ROOT_NAME());
+    EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
+                                                &dtnode));
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(node_name, &cdtnode, chain));
+    EXPECT_EQ(1, chain.getLevelCount());
+
+    // Copy should be consistent
+    TestDomainTreeNodeChain chain3(chain);
+    EXPECT_EQ(chain.getLevelCount(), chain3.getLevelCount());
+    EXPECT_EQ(chain.getAbsoluteName(), chain3.getAbsoluteName());
+
+    // Check the name of the found node (should have '.' as both non-absolute
+    // and absolute name
+    EXPECT_EQ(".", cdtnode->getLabels().toText());
+    EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+
+    /*
+     * Now creating a possibly deepest tree with MAX_LABELS levels.
+     * it should look like:
+     *           (.)
+     *            |
+     *            a
+     *            |
+     *            a
+     *            : (MAX_LABELS - 1) "a"'s
+     *
+     * then confirm that find() for the deepest name succeeds without any
+     * disruption, and the resulting chain has the expected level.
+     * Note that the root name (".") solely belongs to a single level,
+     * so the levels begin with 2.
+     */
+    for (unsigned int i = 2; i <= Name::MAX_LABELS; ++i) {
+        node_name = Name("a.").concatenate(node_name);
+        EXPECT_EQ(TestDomainTree::SUCCESS, tree.insert(mem_sgmt_, node_name,
+                                                    &dtnode));
+        TestDomainTreeNodeChain found_chain;
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  tree.find(node_name, &cdtnode, found_chain));
+        EXPECT_EQ(i, found_chain.getLevelCount());
+
+        // The non-absolute name should only have the first label
+        EXPECT_EQ("a", cdtnode->getLabels().toText());
+        // But the absolute name should have all labels
+        EXPECT_EQ(node_name.toText(),
+                  cdtnode->getAbsoluteLabels(buf).toText());
+    }
+
+    // Confirm the last inserted name has the possible maximum length with
+    // maximum label count.  This confirms the dtree and chain level cannot
+    // be larger.
+    EXPECT_EQ(Name::MAX_LABELS, node_name.getLabelCount());
+    EXPECT_THROW(node_name.concatenate(Name("a.")), TooLongName);
+}
+
+TEST_F(DomainTreeTest, getAbsoluteNameError) {
+    // an empty chain isn't allowed.
+    TestDomainTreeNodeChain chain;
+    EXPECT_THROW(chain.getAbsoluteName(), BadValue);
+}
+
+/*
+ * The domain order should be:
+ * ., a, b, c, d.e.f, x.d.e.f, w.y.d.e.f, o.w.y.d.e.f, p.w.y.d.e.f,
+ * q.w.y.d.e.f, z.d.e.f, j.z.d.e.f, g.h, i.g.h, k.g.h
+ *             . (no data, can't be found)
+ *             |
+ *             b
+ *           /   \
+ *          a    d.e.f
+ *              /  |   \
+ *             c   |    g.h
+ *                 |     |
+ *                w.y    i
+ *              /  |  \   \
+ *             x   |   z   k
+ *                 |   |
+ *                 p   j
+ *               /   \
+ *              o     q
+ */
+const char* const names[] = {
+    "a", "b", "c", "d.e.f", "x.d.e.f", "w.y.d.e.f", "o.w.y.d.e.f",
+    "p.w.y.d.e.f", "q.w.y.d.e.f", "z.d.e.f", "j.z.d.e.f",
+    "g.h", "i.g.h", "k.g.h"};
+const size_t name_count(sizeof(names) / sizeof(*names));
+
+const char* const upper_node_names[] = {
+    ".", ".", ".", ".", "d.e.f", "d.e.f", "w.y.d.e.f",
+    "w.y.d.e.f", "w.y.d.e.f", "d.e.f", "z.d.e.f",
+    ".", "g.h", "g.h"};
+
+TEST_F(DomainTreeTest, getUpperNode) {
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* node = NULL;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name(names[0]),
+                                            &node,
+                                            node_path));
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_NE(static_cast<void*>(NULL), node);
+
+        const TestDomainTreeNode* upper_node = node->getUpperNode();
+        if (upper_node_names[i] != NULL) {
+            const TestDomainTreeNode* upper_node2 = NULL;
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      dtree_expose_empty_node.find(Name(upper_node_names[i]),
+                                                    &upper_node2));
+            EXPECT_NE(static_cast<void*>(NULL), upper_node2);
+            EXPECT_EQ(upper_node, upper_node2);
+        } else {
+            EXPECT_EQ(static_cast<void*>(NULL), upper_node);
+        }
+
+        node = dtree_expose_empty_node.nextNode(node_path);
+    }
+
+    // We should have reached the end of the tree.
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+
+#if 0
+// Disabled and kept still, for use in case we make getSubTreeRoot() a
+// public function again.
+
+const char* const subtree_root_node_names[] = {
+    "b", "b", "b", "b", "w.y.d.e.f", "w.y.d.e.f", "p.w.y.d.e.f",
+    "p.w.y.d.e.f", "p.w.y.d.e.f", "w.y.d.e.f", "j.z.d.e.f",
+    "b", "i.g.h", "i.g.h"};
+
+TEST_F(DomainTreeTest, getSubTreeRoot) {
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* node = NULL;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree_expose_empty_node.find(Name(names[0]),
+                                            &node,
+                                            node_path));
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_NE(static_cast<void*>(NULL), node);
+
+        const TestDomainTreeNode* sr_node = node->getSubTreeRoot();
+        if (subtree_root_node_names[i] != NULL) {
+            const TestDomainTreeNode* sr_node2 = NULL;
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                dtree_expose_empty_node.find(Name(subtree_root_node_names[i]),
+                                             &sr_node2));
+            EXPECT_NE(static_cast<void*>(NULL), sr_node2);
+            EXPECT_EQ(sr_node, sr_node2);
+        } else {
+            EXPECT_EQ(static_cast<void*>(NULL), sr_node);
+        }
+
+        node = dtree_expose_empty_node.nextNode(node_path);
+    }
+
+    // We should have reached the end of the tree.
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+#endif // disabled getSubTreeRoot()
+
+
+TEST_F(DomainTreeTest, nextNode) {
+    TestDomainTreeNodeChain node_path;
+    const TestDomainTreeNode* node = NULL;
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              dtree.find(Name(names[0]), &node, node_path));
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_NE(static_cast<void*>(NULL), node);
+        EXPECT_EQ(Name(names[i]), node_path.getAbsoluteName());
+        node = dtree.nextNode(node_path);
+    }
+
+    // We should have reached the end of the tree.
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Just walk using previousNode until the beginning of the tree and check it is
+// OK
+//
+// dtree - the tree to walk
+// node - result of previous call to find(), starting position of the walk
+// node_path - the path from the previous call to find(), will be modified
+// chain_length - the number of names that should be in the chain to be walked
+//   (0 means it should be empty, 3 means 'a', 'b' and 'c' should be there -
+//   this is always from the beginning of the names[] list).
+// skip_first - if this is false, the node should already contain the node with
+//   the first name of the chain. If it is true, the node should be NULL
+//   (true is for finds that return no match, false for the ones that return
+//   match)
+void
+previousWalk(TestDomainTree& dtree, const TestDomainTreeNode* node,
+             TestDomainTreeNodeChain& node_path, size_t chain_length,
+             bool skip_first)
+{
+    if (skip_first) {
+        // If the first is not found, this is supposed to be NULL and we skip
+        // it in our checks.
+        EXPECT_EQ(static_cast<void*>(NULL), node);
+        node = dtree.previousNode(node_path);
+    }
+    for (size_t i(chain_length); i > 0; --i) {
+        EXPECT_NE(static_cast<void*>(NULL), node);
+        EXPECT_EQ(Name(names[i - 1]), node_path.getAbsoluteName());
+        // Find the node at the path and check the value is the same
+        // (that it really returns the correct corresponding node)
+        //
+        // The "empty" nodes can not be found
+        if (node->getData()) {
+            const TestDomainTreeNode* node2(NULL);
+            TestDomainTreeNodeChain node_path2;
+            EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                      dtree.find(Name(names[i - 1]), &node2, node_path2));
+            EXPECT_EQ(node, node2);
+        }
+        node = dtree.previousNode(node_path);
+    }
+
+    // We should have reached the start of the tree.
+    ASSERT_NE(static_cast<void*>(NULL), node);
+    EXPECT_EQ(".", node->getLabels().toText());
+
+    // With one more call it results in NULL
+    node = dtree.previousNode(node_path);
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+
+    // Calling previousNode() yet again should still return NULL without
+    // fail.
+    node = dtree.previousNode(node_path);
+    EXPECT_EQ(static_cast<void*>(NULL), node);
+}
+
+// Check the previousNode
+TEST_F(DomainTreeTest, previousNode) {
+    // First, iterate the whole tree from the end to the beginning.
+    TestDomainTreeNodeChain node_path;
+    EXPECT_THROW(dtree.previousNode(node_path), isc::BadValue) <<
+        "Throw before a search was done on the path";
+    const TestDomainTreeNode* node(NULL);
+    {
+        SCOPED_TRACE("Iterate through");
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  dtree.find(Name(names[name_count - 1]), &node, node_path));
+        previousWalk(dtree, node, node_path, name_count, false);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Iterate from the middle");
+        // Now, start somewhere in the middle, but within the real node.
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  dtree.find(Name(names[4]), &node, node_path));
+        previousWalk(dtree, node, node_path, 5, false);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start at the first");
+        // If we start at the lowest (which is "a"), we get to the beginning
+        // right away.
+        EXPECT_EQ(TestDomainTree::EXACTMATCH,
+                  dtree.find(Name(names[0]), &node, node_path));
+        EXPECT_NE(static_cast<void*>(NULL), node);
+        node = dtree.previousNode(node_path);
+        ASSERT_NE(static_cast<void*>(NULL), node);
+        EXPECT_EQ(".", node->getLabels().toText());
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start before the first");
+        // If we start before the lowest (. < 0. < a.), we should not get a
+        // node.  Its previous node should be the root.
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  dtree.find<void*>(Name("0"), &node, node_path, NULL, NULL));
+        EXPECT_EQ(static_cast<void*>(NULL), node);
+        node = dtree.previousNode(node_path);
+        ASSERT_NE(static_cast<void*>(NULL), node);
+        EXPECT_EQ(".", node->getLabels().toText());
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start after the last");
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  dtree.find(Name("z"), &node, node_path));
+        previousWalk(dtree, node, node_path, name_count, true);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start below a leaf");
+        // We exit a leaf by going down. We should start by the one
+        // we exited - 'c' (actually, we should get it by the find, as partial
+        // match).
+        EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+                  dtree.find(Name("b.c"), &node, node_path));
+        previousWalk(dtree, node, node_path, 3, false);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start to the right of a leaf");
+        // When searching for this, we exit the 'x' node to the right side,
+        // so we should go x afterwards.
+
+        // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+        // and not PARTIALMATCH.
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  dtree.find(Name("xy.d.e.f"), &node, node_path));
+        previousWalk(dtree, node, node_path, 5, true);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start to the left of a leaf");
+        // This is similar to the previous, but we exit the 'z' leaf to the
+        // left side, so should not visit z at all then.
+
+        // The d.e.f is empty node, so it is hidden by find. Therefore NOTFOUND
+        // and not PARTIALMATCH.
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  dtree.find(Name("yz.d.e.f"), &node, node_path));
+        previousWalk(dtree, node, node_path, 9, true);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start to the right of a parent");
+        // When searching for this, we exit the 'g.h' node to the right
+        // side, so we should go to g.h's children afterwards.
+
+        // 'g.h' is an empty node, so we get a NOTFOUND and not
+        // PARTIALMATCH.
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  dtree.find(Name("x.h"), &node, node_path));
+        // 'g.h' is the COMMONANCESTOR.
+        EXPECT_EQ(node_path.getLastComparedNode()->getName(), Name("g.h"));
+        EXPECT_EQ(NameComparisonResult::COMMONANCESTOR,
+                  node_path.getLastComparisonResult().getRelation());
+        // find() exits to the right of 'g.h'
+        EXPECT_GT(node_path.getLastComparisonResult().getOrder(), 0);
+        // We then descend into 'i.g.h' and walk all the nodes in the
+        // tree.
+        previousWalk(dtree, node, node_path, name_count, true);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Start inside a wrong node");
+        // The d.e.f is a single node, but we want only part of it. We
+        // should start iterating before it.
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  dtree.find(Name("e.f"), &node, node_path));
+        previousWalk(dtree, node, node_path, 3, true);
+        node = NULL;
+        node_path.clear();
+    }
+
+    {
+        SCOPED_TRACE("Lookup in empty tree");
+        // Just check it doesn't crash, etc.
+        TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+        TestDomainTree& empty_tree(*tree_holder.get());
+        EXPECT_EQ(TestDomainTree::NOTFOUND,
+                  empty_tree.find(Name("x"), &node, node_path));
+        EXPECT_EQ(static_cast<void*>(NULL), node);
+        EXPECT_EQ(static_cast<void*>(NULL),
+                  empty_tree.previousNode(node_path));
+        node = NULL;
+        node_path.clear();
+    }
+}
+
+TEST_F(DomainTreeTest, largestNode) {
+    cdtnode = dtree.largestNode();
+    EXPECT_EQ(Name("k"), cdtnode->getName());
+
+    // Check for largest node in an empty tree.
+    TreeHolder empty_tree_holder
+        (mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+    TestDomainTree& empty_tree(*empty_tree_holder.get());
+    EXPECT_EQ(static_cast<void*>(NULL), empty_tree.largestNode());
+}
+
+TEST_F(DomainTreeTest, nextNodeError) {
+    // Empty chain for nextNode() is invalid.
+    TestDomainTreeNodeChain chain;
+    EXPECT_THROW(dtree.nextNode(chain), BadValue);
+}
+
+// A helper function for getLastComparedNode() below.
+void
+comparisonChecks(const TestDomainTreeNodeChain& chain,
+                 int expected_order, int expected_common_labels,
+                 NameComparisonResult::NameRelation expected_reln)
+{
+    if (expected_order > 0) {
+        EXPECT_LT(0, chain.getLastComparisonResult().getOrder());
+    } else if (expected_order < 0) {
+        EXPECT_GT(0, chain.getLastComparisonResult().getOrder());
+    } else {
+        EXPECT_EQ(0, chain.getLastComparisonResult().getOrder());
+    }
+    EXPECT_EQ(expected_common_labels,
+              chain.getLastComparisonResult().getCommonLabels());
+    EXPECT_EQ(expected_reln,
+              chain.getLastComparisonResult().getRelation());
+}
+
+TEST_F(DomainTreeTest, getLastComparedNode) {
+    TestDomainTree& tree = dtree_expose_empty_node; // use the "empty OK" mode
+    TestDomainTreeNodeChain chain;
+
+    // initially there should be no 'last compared'.
+    EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
+
+    // A search for an empty tree should result in no 'last compared', too.
+    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+    TestDomainTree& empty_tree(*tree_holder.get());
+    EXPECT_EQ(TestDomainTree::NOTFOUND,
+              empty_tree.find(Name("a"), &cdtnode, chain));
+    EXPECT_EQ(static_cast<void*>(NULL), chain.getLastComparedNode());
+    chain.clear();
+
+    const TestDomainTreeNode* expected_node = NULL;
+
+    // Exact match case.  The returned node should be last compared.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(Name("x.d.e.f"), &expected_node, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // 1 = # labels of "x" (note: excluding ".")
+    comparisonChecks(chain, 0, 1, NameComparisonResult::EQUAL);
+    chain.clear();
+
+    // Partial match, search stopped at the matching node, which should be
+    // the last compared node.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(Name("k.g.h"), &expected_node));
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("x.k.g.h"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // k.g.h < x.k.g.h, 1 = # labels of "k"
+    comparisonChecks(chain, 1, 1, NameComparisonResult::SUBDOMAIN);
+    chain.clear();
+
+    // Partial match, search stopped in the subtree below the matching node
+    // after following a left branch.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(Name("x.d.e.f"), &expected_node));
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("a.d.e.f"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // a < x, no common labels
+    comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
+    chain.clear();
+
+    // Partial match, search stopped in the subtree below the matching node
+    // after following a right branch.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(Name("z.d.e.f"), &expected_node));
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("zz.d.e.f"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // zz > z, no common label
+    comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
+    chain.clear();
+
+    // Partial match, search stopped at a node for a super domain of the
+    // search name in the subtree below the matching node.
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              tree.find(Name("w.y.d.e.f"), &expected_node));
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("y.d.e.f"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // y < w.y, 1 = # labels of "y"
+    comparisonChecks(chain, -1, 1, NameComparisonResult::SUPERDOMAIN);
+    chain.clear();
+
+    // Partial match, search stopped at a node that share a common ancestor
+    // with the search name in the subtree below the matching node.
+    // (the expected node is the same as the previous case)
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("z.y.d.e.f"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // z.y > w.y, 1 = # labels of "y"
+    comparisonChecks(chain, 1, 1, NameComparisonResult::COMMONANCESTOR);
+    chain.clear();
+
+    // Search stops in the highest level (under ".") after following a left
+    // branch. (find() still returns PARTIALMATCH due to the top level ".")
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, tree.find(Name("c"), &expected_node));
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("bb"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // bb < c, no common label
+    comparisonChecks(chain, -1, 0, NameComparisonResult::NONE);
+    chain.clear();
+
+    // Search stops in the highest level (under ".") after following a right
+    // branch. (the expected node is the same as the previous case)
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              tree.find(Name("d"), &cdtnode, chain));
+    EXPECT_EQ(expected_node, chain.getLastComparedNode());
+    // d > c, no common label
+    comparisonChecks(chain, 1, 0, NameComparisonResult::NONE);
+    chain.clear();
+}
+
+TEST_F(DomainTreeTest, dumpTree) {
+    std::ostringstream str;
+    std::ostringstream str2;
+    dtree.dumpTree(str);
+    str2 << "tree has 15 node(s)\n"
+            ". (black) [invisible] [subtreeroot]\n"
+            "     begin down from .\n"
+            "     b (black) [subtreeroot]\n"
+            "          a (black)\n"
+            "               NULL\n"
+            "               NULL\n"
+            "          d.e.f (black) [invisible]\n"
+            "               begin down from d.e.f\n"
+            "               w.y (black) [invisible] [subtreeroot]\n"
+            "                    begin down from w.y\n"
+            "                    p (black) [subtreeroot]\n"
+            "                         o (red)\n"
+            "                              NULL\n"
+            "                              NULL\n"
+            "                         q (red)\n"
+            "                              NULL\n"
+            "                              NULL\n"
+            "                    end down from w.y\n"
+            "                    x (red)\n"
+            "                         NULL\n"
+            "                         NULL\n"
+            "                    z (red)\n"
+            "                         begin down from z\n"
+            "                         j (black) [subtreeroot]\n"
+            "                              NULL\n"
+            "                              NULL\n"
+            "                         end down from z\n"
+            "                         NULL\n"
+            "                         NULL\n"
+            "               end down from d.e.f\n"
+            "               c (red)\n"
+            "                    NULL\n"
+            "                    NULL\n"
+            "               g.h (red)\n"
+            "                    begin down from g.h\n"
+            "                    i (black) [subtreeroot]\n"
+            "                         NULL\n"
+            "                         k (red)\n"
+            "                              NULL\n"
+            "                              NULL\n"
+            "                    end down from g.h\n"
+            "                    NULL\n"
+            "                    NULL\n"
+            "     end down from .\n"
+            "     NULL\n"
+            "     NULL\n";
+    EXPECT_EQ(str2.str(), str.str());
+}
+
+TEST_F(DomainTreeTest, swap) {
+    // Store info about the first tree
+    std::ostringstream str1;
+    dtree.dumpTree(str1);
+    size_t count1(dtree.getNodeCount());
+
+    // Create second one and store state
+    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+    TestDomainTree& tree2(*tree_holder.get());
+    TestDomainTreeNode* node;
+    tree2.insert(mem_sgmt_, Name("second"), &node);
+    std::ostringstream str2;
+    tree2.dumpTree(str2);
+
+    // Swap them
+    ASSERT_NO_THROW(tree2.swap(dtree));
+
+    // Check their sizes
+    ASSERT_EQ(1, dtree.getNodeCount());
+    ASSERT_EQ(count1, tree2.getNodeCount());
+
+    // And content
+    std::ostringstream out;
+    dtree.dumpTree(out);
+    ASSERT_EQ(str2.str(), out.str());
+    out.str("");
+    tree2.dumpTree(out);
+    ASSERT_EQ(str1.str(), out.str());
+}
+
+// Matching in the "root zone" may be special (e.g. there's no parent,
+// any domain names should be considered a subdomain of it), so it makes
+// sense to test cases with the root zone explicitly.
+TEST_F(DomainTreeTest, root) {
+    TreeHolder tree_holder(mem_sgmt_, TestDomainTree::create(mem_sgmt_));
+    TestDomainTree& root(*tree_holder.get());
+    root.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(1)));
+
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              root.find(Name::ROOT_NAME(), &cdtnode));
+    EXPECT_EQ(dtnode, cdtnode);
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              root.find(Name("example.com"), &cdtnode));
+    EXPECT_EQ(dtnode, cdtnode);
+
+    // Insert a new name that better matches the query name.  find() should
+    // find the better one.
+    root.insert(mem_sgmt_, Name("com"), &dtnode);
+    EXPECT_EQ(static_cast<int*>(NULL), dtnode->setData(new int(2)));
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              root.find(Name("example.com"), &cdtnode));
+    EXPECT_EQ(dtnode, cdtnode);
+
+    // Perform the same tests for the tree that allows matching against empty
+    // nodes.
+    TreeHolder tree_holder_emptyok(mem_sgmt_,
+                                   TestDomainTree::create(mem_sgmt_, true));
+    TestDomainTree& root_emptyok(*tree_holder_emptyok.get());
+    root_emptyok.insert(mem_sgmt_, Name::ROOT_NAME(), &dtnode);
+    EXPECT_EQ(TestDomainTree::EXACTMATCH,
+              root_emptyok.find(Name::ROOT_NAME(), &cdtnode));
+    EXPECT_EQ(dtnode, cdtnode);
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              root_emptyok.find(Name("example.com"), &cdtnode));
+    EXPECT_EQ(dtnode, cdtnode);
+
+    root.insert(mem_sgmt_, Name("com"), &dtnode);
+    EXPECT_EQ(TestDomainTree::PARTIALMATCH,
+              root.find(Name("example.com"), &cdtnode));
+    EXPECT_EQ(dtnode, cdtnode);
+}
+
+TEST_F(DomainTreeTest, getAbsoluteLabels) {
+    // The full absolute names of the nodes in the tree
+    // with the addition of the explicit root node
+    const char* const domain_names[] = {
+        "c", "b", "a", "x.d.e.f", "z.d.e.f", "g.h", "i.g.h", "o.w.y.d.e.f",
+        "j.z.d.e.f", "p.w.y.d.e.f", "q.w.y.d.e.f", "k.g.h"};
+    // The names of the nodes themselves, as they end up in the tree
+    const char* const first_labels[] = {
+        "c", "b", "a", "x", "z", "g.h", "i", "o",
+        "j", "p", "q", "k"};
+
+    const int name_count = sizeof(domain_names) / sizeof(domain_names[0]);
+    for (int i = 0; i < name_count; ++i) {
+        EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name(domain_names[i]),
+                  &cdtnode));
+
+        // First make sure the names themselves are not absolute
+        const LabelSequence ls(cdtnode->getLabels());
+        EXPECT_EQ(first_labels[i], ls.toText());
+        EXPECT_FALSE(ls.isAbsolute());
+
+        // Now check the absolute names
+        const LabelSequence abs_ls(cdtnode->getAbsoluteLabels(buf));
+        EXPECT_EQ(Name(domain_names[i]).toText(), abs_ls.toText());
+        EXPECT_TRUE(abs_ls.isAbsolute());
+    }
+
+    // Explicitly add and find a root node, to see that getAbsoluteLabels
+    // also works when getLabels() already returns an absolute LabelSequence
+    dtree.insert(mem_sgmt_, Name("."), &dtnode);
+    dtnode->setData(new int(1));
+
+    EXPECT_EQ(TestDomainTree::EXACTMATCH, dtree.find(Name("."), &cdtnode));
+
+    EXPECT_TRUE(cdtnode->getLabels().isAbsolute());
+    EXPECT_EQ(".", cdtnode->getLabels().toText());
+    EXPECT_TRUE(cdtnode->getAbsoluteLabels(buf).isAbsolute());
+    EXPECT_EQ(".", cdtnode->getAbsoluteLabels(buf).toText());
+}
+}
diff --git a/src/lib/datasrc/tests/memory/memory_client_unittest.cc b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
new file mode 100644
index 0000000..58979a4
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/memory_client_unittest.cc
@@ -0,0 +1,777 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/masterload.h>
+#include <dns/nsec3hash.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrsetlist.h>
+#include <dns/rrttl.h>
+#include <dns/masterload.h>
+
+#include <datasrc/result.h>
+#include <datasrc/data_source.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+#include <datasrc/memory/memory_client.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include "memory_segment_test.h"
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+
+namespace {
+
+const char* rrset_data[] = {
+    "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+    "68 3600 300 3600000 3600",
+    "a.example.org. 3600 IN A 192.168.0.1\n" // RRset containing 2 RRs
+    "a.example.org. 3600 IN A 192.168.0.2",
+    "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+    "40430 example.org. FAKEFAKE",
+    "a.example.org. 3600 IN MX 10 mail.example.org.",
+    "a.example.org. 3600 IN RRSIG MX 5 3 3600 20150420235959 20051021000000 "
+    "40430 example.org. FAKEFAKEFAKE",
+    NULL
+};
+
+// RRsets that emulate the "separate RRs" mode.
+const char* rrset_data_separated[] = {
+    "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+    "68 3600 300 3600000 3600",
+    "a.example.org. 3600 IN A 192.168.0.1", // these two belong to the same
+    "a.example.org. 3600 IN A 192.168.0.2", // RRset, but are separated.
+    NULL
+};
+
+// Similar to the previous one, but with separated RRSIGs
+const char* rrset_data_sigseparated[] = {
+    "example.org. 3600 IN SOA ns1.example.org. bugs.x.w.example.org. "
+    "68 3600 300 3600000 3600",
+    "a.example.org. 3600 IN A 192.168.0.1",
+    "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+    "40430 example.org. FAKEFAKE",
+    "a.example.org. 3600 IN RRSIG A 5 3 3600 20150420235959 20051021000000 "
+    "53535 example.org. FAKEFAKE",
+    NULL
+};
+
+class MockIterator : public ZoneIterator {
+private:
+    MockIterator(const char** rrset_data_ptr, bool pass_empty_rrsig) :
+        rrset_data_ptr_(rrset_data_ptr),
+        pass_empty_rrsig_(pass_empty_rrsig)
+    {
+    }
+
+    const char** rrset_data_ptr_;
+    // If true, emulate an unexpected bogus case where an RRSIG RRset is
+    // returned without the RDATA.  For brevity allow tests tweak it directly.
+    bool pass_empty_rrsig_;
+
+public:
+    virtual ConstRRsetPtr getNextRRset() {
+        if (*rrset_data_ptr_ == NULL) {
+             return (ConstRRsetPtr());
+        }
+
+        ConstRRsetPtr result(textToRRset(*rrset_data_ptr_,
+                                         RRClass::IN(), Name("example.org")));
+        if (pass_empty_rrsig_ && result->getType() == RRType::RRSIG()) {
+            result.reset(new RRset(result->getName(), result->getClass(),
+                                   result->getType(), result->getTTL()));
+        }
+        ++rrset_data_ptr_;
+
+        return (result);
+    }
+
+    virtual ConstRRsetPtr getSOA() const {
+        isc_throw(isc::NotImplemented, "Not implemented");
+    }
+
+    static ZoneIteratorPtr makeIterator(const char** rrset_data_ptr,
+                                        bool pass_empty_rrsig = false)
+    {
+        return (ZoneIteratorPtr(new MockIterator(rrset_data_ptr,
+                                                 pass_empty_rrsig)));
+    }
+};
+
+class MemoryClientTest : public ::testing::Test {
+protected:
+    MemoryClientTest() : zclass_(RRClass::IN()),
+                         client_(new InMemoryClient(mem_sgmt_, zclass_))
+    {}
+    ~MemoryClientTest() {
+        if (client_ != NULL) {
+            delete client_;
+        }
+    }
+    void TearDown() {
+        delete client_;
+        client_ = NULL;
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+    }
+    const RRClass zclass_;
+    test::MemorySegmentTest mem_sgmt_;
+    InMemoryClient* client_;
+};
+
+TEST_F(MemoryClientTest, loadRRsetDoesntMatchOrigin) {
+    // Attempting to load example.org to example.com zone should result
+    // in an exception.
+    EXPECT_THROW(client_->load(Name("example.com"),
+                               TEST_DATA_DIR "/example.org-empty.zone"),
+                 MasterLoadError);
+}
+
+TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak1) {
+    // Attempting to load broken example.org zone should result in an
+    // exception. This should not leak ZoneData and other such
+    // allocations.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR "/example.org-broken1.zone"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadErrorsInParsingZoneMustNotLeak2) {
+    // Attempting to load broken example.org zone should result in an
+    // exception. This should not leak ZoneData and other such
+    // allocations.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR "/example.org-broken2.zone"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNonExistentZoneFile) {
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR "/somerandomfilename"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadEmptyZoneFileThrows) {
+    // When an empty zone file is loaded, the origin doesn't even have
+    // an SOA RR. This condition should be avoided, and hence load()
+    // should throw when an empty zone is loaded.
+
+    EXPECT_EQ(0, client_->getZoneCount());
+
+    EXPECT_THROW(client_->load(Name("."),
+                               TEST_DATA_DIR "/empty.zone"),
+                 InMemoryClient::EmptyZone);
+
+    EXPECT_EQ(0, client_->getZoneCount());
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, load) {
+    // This is a simple load check for a "full" and correct zone that
+    // should not result in any exceptions.
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_FALSE(zone_data->isSigned());
+    EXPECT_FALSE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadFromIterator) {
+    client_->load(Name("example.org"),
+                  *MockIterator::makeIterator(rrset_data));
+
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset(iterator->getNextRRset());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // RRType::MX() RRset
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::MX(), rrset->getType());
+    EXPECT_EQ(1, rrset->getRRsigDataCount()); // this RRset is signed
+
+    // RRType::A() RRset
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+    EXPECT_EQ(1, rrset->getRRsigDataCount()); // also signed
+
+    // There's nothing else in this iterator
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+    // Iterating past the end should result in an exception
+    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+
+    // Loading the zone with an iterator separating RRs of the same RRset
+    // will fail because the resulting sequence doesn't meet assumptions of
+    // the (current) in-memory implementation.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               *MockIterator::makeIterator(
+                                   rrset_data_separated)),
+                 InMemoryClient::AddError);
+
+    // Similar to the previous case, but with separated RRSIGs.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               *MockIterator::makeIterator(
+                                   rrset_data_sigseparated)),
+                 InMemoryClient::AddError);
+
+    // Emulating bogus iterator implementation that passes empty RRSIGs.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               *MockIterator::makeIterator(rrset_data, true)),
+                 isc::Unexpected);
+}
+
+TEST_F(MemoryClientTest, loadMemoryAllocationFailures) {
+    // Just to check that things get cleaned up
+
+    for (int i = 1; i < 16; i++) {
+        mem_sgmt_.setThrowCount(i);
+        EXPECT_THROW(client_->load(Name("example.org"),
+                                   TEST_DATA_DIR "/example.org.zone"),
+                     std::bad_alloc);
+    }
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3Signed) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-nsec3-signed.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_TRUE(zone_data->isSigned());
+    EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadNSEC3EmptySalt) {
+    // Load NSEC3 with empty ("-") salt. This should not throw or crash
+    // or anything.
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-nsec3-empty-salt.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_TRUE(zone_data->isSigned());
+    EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadNSEC3SignedNoParam) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-nsec3-signed-no-param.zone");
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    ASSERT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+    EXPECT_TRUE(zone_data->isSigned());
+    EXPECT_TRUE(zone_data->isNSEC3Signed());
+}
+
+TEST_F(MemoryClientTest, loadReloadZone) {
+    // Because we reload the same zone, also check that the zone count
+    // doesn't increase.
+    EXPECT_EQ(0, client_->getZoneCount());
+
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-empty.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+
+    // Reload zone with same data
+
+    client_->load(Name("example.org"),
+                  client_->getFileName(Name("example.org")));
+    EXPECT_EQ(1, client_->getZoneCount());
+
+    const ZoneData* zone_data =
+        client_->findZoneData(Name("example.org"));
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+
+    /* Check SOA */
+    const ZoneNode* node = zone_data->getOriginNode();
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    const RdataSet* set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::SOA(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    /* Check ns1.example.org */
+    const ZoneTree& tree = zone_data->getZoneTree();
+    ZoneTree::Result zresult(tree.find(Name("ns1.example.org"), &node));
+    EXPECT_NE(ZoneTree::EXACTMATCH, zresult);
+
+    // Reload zone with different data
+
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+
+    zone_data = client_->findZoneData(Name("example.org"));
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+
+    /* Check SOA */
+    node = zone_data->getOriginNode();
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::SOA(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    /* Check ns1.example.org */
+    const ZoneTree& tree2 = zone_data->getZoneTree();
+    ZoneTree::Result zresult2(tree2.find(Name("ns1.example.org"), &node));
+    EXPECT_EQ(ZoneTree::EXACTMATCH, zresult2);
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::AAAA(), set->type);
+
+    set = set->getNext();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::A(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDuplicateType) {
+    // This should not result in any exceptions:
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-duplicate-type.zone");
+
+    // This should throw:
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-duplicate-type-bad.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleCNAMEThrows) {
+    // Multiple CNAME RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-cname.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleDNAMEThrows) {
+    // Multiple DNAME RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-dname.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleNSEC3Throws) {
+    // Multiple NSEC3 RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-nsec3.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadMultipleNSEC3PARAMThrows) {
+    // Multiple NSEC3PARAM RRs should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-multiple-nsec3param.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadOutOfZoneThrows) {
+    // Out of zone names should throw.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-out-of-zone.zone"),
+                 MasterLoadError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardNSThrows) {
+    // Wildcard NS names should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-wildcard-ns.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardDNAMEThrows) {
+    // Wildcard DNAME names should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-wildcard-dname.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadWildcardNSEC3Throws) {
+    // Wildcard NSEC3 names should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-wildcard-nsec3.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3WithFewerLabelsThrows) {
+    // NSEC3 names with labels != (origin_labels + 1) should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-nsec3-fewer-labels.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadNSEC3WithMoreLabelsThrows) {
+    // NSEC3 names with labels != (origin_labels + 1) should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-nsec3-more-labels.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadCNAMEAndNotNSECThrows) {
+    // CNAME and not NSEC should throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-cname-and-not-nsec-1.zone"),
+                 InMemoryClient::AddError);
+
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-cname-and-not-nsec-2.zone"),
+                 InMemoryClient::AddError);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSApex1) {
+    // DNAME + NS (apex) is OK
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR
+                  "/example.org-dname-ns-apex-1.zone");
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSApex2) {
+    // DNAME + NS (apex) is OK (reverse order)
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR
+                  "/example.org-dname-ns-apex-2.zone");
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex1) {
+    // DNAME + NS (non-apex) must throw
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-dname-ns-nonapex-1.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadDNAMEAndNSNonApex2) {
+    // DNAME + NS (non-apex) must throw (reverse order)
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-dname-ns-nonapex-2.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGFollowsNothing) {
+    // This causes the situation where an RRSIG is added without a covered
+    // RRset.  Such cases are currently rejected.
+    EXPECT_THROW(client_->load(Name("example.org"),
+                               TEST_DATA_DIR
+                               "/example.org-rrsig-follows-nothing.zone"),
+                 InMemoryClient::AddError);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, loadRRSIGs) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+}
+
+TEST_F(MemoryClientTest, loadRRSIGsRdataMixedCoveredTypes) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    RRsetPtr rrset(new RRset(Name("example.org"),
+                             RRClass::IN(), RRType::A(), RRTTL(3600)));
+    rrset->addRdata(in::A("192.0.2.1"));
+    rrset->addRdata(in::A("192.0.2.2"));
+
+    RRsetPtr rrsig(new RRset(Name("example.org"), zclass_,
+                             RRType::RRSIG(), RRTTL(300)));
+    rrsig->addRdata(generic::RRSIG("A 5 3 3600 20000101000000 20000201000000 "
+                                   "12345 example.org. FAKEFAKEFAKE"));
+    rrsig->addRdata(generic::RRSIG("NS 5 3 3600 20000101000000 20000201000000 "
+                                   "54321 example.org. FAKEFAKEFAKEFAKE"));
+    rrset->addRRsig(rrsig);
+
+    EXPECT_THROW(client_->add(Name("example.org"), rrset),
+                 InMemoryClient::AddError);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, getZoneCount) {
+    EXPECT_EQ(0, client_->getZoneCount());
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    EXPECT_EQ(1, client_->getZoneCount());
+}
+
+TEST_F(MemoryClientTest, getFileNameForNonExistentZone) {
+    // Zone "example.org." doesn't exist
+    EXPECT_TRUE(client_->getFileName(Name("example.org.")).empty());
+}
+
+TEST_F(MemoryClientTest, getFileName) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    EXPECT_EQ(TEST_DATA_DIR "/example.org-empty.zone",
+              client_->getFileName(Name("example.org")));
+}
+
+TEST_F(MemoryClientTest, getIteratorForNonExistentZone) {
+    // Zone "." doesn't exist
+    EXPECT_THROW(client_->getIterator(Name(".")), DataSourceError);
+}
+
+TEST_F(MemoryClientTest, getIterator) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset_soa(iterator->getNextRRset());
+    EXPECT_TRUE(rrset_soa);
+    EXPECT_EQ(RRType::SOA(), rrset_soa->getType());
+
+    // There's nothing else in this iterator
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+    // Iterating past the end should result in an exception
+    EXPECT_THROW(iterator->getNextRRset(), isc::Unexpected);
+}
+
+TEST_F(MemoryClientTest, getIteratorSeparateRRs) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-multiple.zone");
+
+    // separate_rrs = false
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset(iterator->getNextRRset());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // Only one RRType::A() RRset
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // There's nothing else in this zone
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+
+
+    // separate_rrs = true
+    ZoneIteratorPtr iterator2(client_->getIterator(Name("example.org"), true));
+
+    // First we have the SOA
+    rrset = iterator2->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // First RRType::A() RRset
+    rrset = iterator2->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // Second RRType::A() RRset
+    rrset = iterator2->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    // There's nothing else in this iterator
+    EXPECT_EQ(ConstRRsetPtr(), iterator2->getNextRRset());
+}
+
+TEST_F(MemoryClientTest, getIteratorGetSOAThrowsNotImplemented) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // This method is not implemented.
+    EXPECT_THROW(iterator->getSOA(), isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, addRRsetToNonExistentZoneThrows) {
+    // The zone "example.org" doesn't exist, so we can't add an RRset to
+    // it.
+    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+                               RRTTL(300)));
+    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+    EXPECT_THROW(client_->add(Name("example.org"), rrset_a), DataSourceError);
+}
+
+TEST_F(MemoryClientTest, addOutOfZoneThrows) {
+    // Out of zone names should throw.
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-empty.zone");
+
+    RRsetPtr rrset_a(new RRset(Name("a.example.com"),
+                               RRClass::IN(), RRType::A(), RRTTL(300)));
+    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+
+    EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
+                 OutOfZone);
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, addNullRRsetThrows) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    EXPECT_THROW(client_->add(Name("example.org"), ConstRRsetPtr()),
+                 InMemoryClient::NullRRset);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, addEmptyRRsetThrows) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+                               RRTTL(300)));
+    EXPECT_THROW(client_->add(Name("example.org"), rrset_a),
+                 InMemoryClient::AddError);
+
+    // Teardown checks for memory segment leaks
+}
+
+TEST_F(MemoryClientTest, add) {
+    client_->load(Name("example.org"), TEST_DATA_DIR "/example.org-empty.zone");
+
+    // Add another RRset
+    RRsetPtr rrset_a(new RRset(Name("example.org"), RRClass::IN(), RRType::A(),
+                               RRTTL(300)));
+    rrset_a->addRdata(rdata::in::A("192.0.2.1"));
+    client_->add(Name("example.org"), rrset_a);
+
+    ZoneIteratorPtr iterator(client_->getIterator(Name("example.org")));
+
+    // First we have the SOA
+    ConstRRsetPtr rrset(iterator->getNextRRset());
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::A(), rrset->getType());
+
+    rrset = iterator->getNextRRset();
+    EXPECT_TRUE(rrset);
+    EXPECT_EQ(RRType::SOA(), rrset->getType());
+
+    // There's nothing else in this zone
+    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+}
+
+TEST_F(MemoryClientTest, findZoneData) {
+    client_->load(Name("example.org"),
+                  TEST_DATA_DIR "/example.org-rrsigs.zone");
+
+    const ZoneData* zone_data = client_->findZoneData(Name("example.com"));
+    EXPECT_EQ(static_cast<const ZoneData*>(NULL), zone_data);
+
+    zone_data = client_->findZoneData(Name("example.org"));
+    EXPECT_NE(static_cast<const ZoneData*>(NULL), zone_data);
+
+    /* Check SOA */
+    const ZoneNode* node = zone_data->getOriginNode();
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    const RdataSet* set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::SOA(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+
+    /* Check ns1.example.org */
+    const ZoneTree& tree = zone_data->getZoneTree();
+    ZoneTree::Result result3(tree.find(Name("ns1.example.org"), &node));
+    EXPECT_EQ(ZoneTree::EXACTMATCH, result3);
+    EXPECT_NE(static_cast<const ZoneNode*>(NULL), node);
+
+    set = node->getData();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::AAAA(), set->type);
+
+    set = set->getNext();
+    EXPECT_NE(static_cast<const RdataSet*>(NULL), set);
+    EXPECT_EQ(RRType::A(), set->type);
+
+    set = set->getNext();
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL), set);
+}
+
+TEST_F(MemoryClientTest, getUpdaterThrowsNotImplemented) {
+    // This method is not implemented.
+    EXPECT_THROW(client_->getUpdater(Name("."), false, false),
+                 isc::NotImplemented);
+}
+
+TEST_F(MemoryClientTest, getJournalReaderNotImplemented) {
+    // This method is not implemented.
+    EXPECT_THROW(client_->getJournalReader(Name("."), 0, 0),
+                 isc::NotImplemented);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/memory_segment_test.h b/src/lib/datasrc/tests/memory/memory_segment_test.h
new file mode 100644
index 0000000..3195a9b
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/memory_segment_test.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DATASRC_MEMORY_SEGMENT_TEST_H
+#define DATASRC_MEMORY_SEGMENT_TEST_H 1
+
+#include <util/memory_segment_local.h>
+
+#include <cstddef>              // for size_t
+#include <new>                  // for bad_alloc
+
+namespace isc {
+namespace datasrc {
+namespace memory {
+namespace test {
+
+// A special memory segment that can be used for tests.  It normally behaves
+// like a "local" memory segment.  If "throw count" is set to non 0 via
+// setThrowCount(), it continues the normal behavior until the specified
+// number of calls to allocate(), exclusive, and throws an exception at the
+// next call.  For example, if count is set to 3, the next two calls to
+// allocate() will succeed, and the 3rd call will fail with an exception.
+// This segment object can be used after the exception is thrown, and the
+// count is internally reset to 0.
+class MemorySegmentTest : public isc::util::MemorySegmentLocal {
+public:
+    MemorySegmentTest() : throw_count_(0) {}
+    virtual void* allocate(std::size_t size) {
+        if (throw_count_ > 0) {
+            if (--throw_count_ == 0) {
+                throw std::bad_alloc();
+            }
+        }
+        return (isc::util::MemorySegmentLocal::allocate(size));
+    }
+    void setThrowCount(std::size_t count) { throw_count_ = count; }
+
+private:
+    std::size_t throw_count_;
+};
+
+} // namespace test
+} // namespace memory
+} // namespace datasrc
+} // namespace isc
+
+#endif // DATASRC_MEMORY_SEGMENT_TEST_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc b/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
new file mode 100644
index 0000000..58c6cb1
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
@@ -0,0 +1,839 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+
+#include <util/unittests/wiredata.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <cstring>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+
+using isc::util::unittests::matchWireData;
+using std::string;
+using std::vector;
+
+// A trick to steal some private definitions of the implementation we use here
+
+namespace isc {
+namespace datasrc{
+namespace memory {
+
+#include <datasrc/memory/rdata_serialization_priv.cc>
+
+}
+}
+}
+
+namespace {
+// This defines a tuple of test data used in test_rdata_list below.
+struct TestRdata {
+    const char* const rrclass;  // RR class, textual form
+    const char* const rrtype;   // RR type, textual form
+    const char* const rdata;    // textual RDATA
+    const size_t n_varlen_fields; // expected # of variable-len fields
+};
+
+// This test data consist of (almost) all supported types of RDATA (+ some
+// unusual and corner cases).
+const TestRdata test_rdata_list[] = {
+    {"IN", "A", "192.0.2.1", 0},
+    {"IN", "NS", "ns.example.com", 0},
+    {"IN", "CNAME", "cname.example.com", 0},
+    {"IN", "SOA", "ns.example.com root.example.com 0 0 0 0 0", 0},
+    {"IN", "PTR", "reverse.example.com", 0},
+    {"IN", "HINFO", "\"cpu-info\" \"OS-info\"", 1},
+    {"IN", "MINFO", "root.example.com mbox.example.com", 0},
+    {"IN", "MX", "10 mx.example.com", 0},
+    {"IN", "TXT", "\"test1\" \"test 2\"", 1},
+    {"IN", "RP", "root.example.com. rp-text.example.com", 0},
+    {"IN", "AFSDB", "1 afsdb.example.com", 0},
+    {"IN", "AAAA", "2001:db8::1", 0},
+    {"IN", "SRV", "1 0 10 target.example.com", 0},
+    {"IN", "NAPTR", "100 50 \"s\" \"http\" \"\" _http._tcp.example.com", 1},
+    {"IN", "DNAME", "dname.example.com", 0},
+    {"IN", "DS", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+    {"IN", "SSHFP", "1 1 dd465c09cfa51fb45020cc83316fff", 1},
+    // We handle RRSIG separately, so it's excluded from the list
+    {"IN", "NSEC", "next.example.com. A AAAA NSEC RRSIG", 1},
+    {"IN", "DNSKEY", "256 3 5 FAKEFAKE", 1},
+    {"IN", "DHCID", "FAKEFAKE", 1},
+    {"IN", "NSEC3", "1 1 12 AABBCCDD FAKEFAKE A RRSIG", 1},
+    {"IN", "NSEC3PARAM", "1 0 12 AABBCCDD", 1},
+    {"IN", "SPF", "v=spf1 +mx a:colo.example.com/28 -all", 1},
+    {"IN", "DLV", "12892 5 2 5F0EB5C777586DE18DA6B5", 1},
+    {"IN", "TYPE65000", "\\# 3 010203", 1}, // some "custom" type
+    {"IN", "TYPE65535", "\\# 0", 1},        // max RR type, 0-length RDATA
+    {"CH", "A", "\\# 2 0102", 1}, // A RR for non-IN class; varlen data
+    {"CH", "NS", "ns.example.com", 0}, // class CH, generic data
+    {"CH", "TXT", "BIND10", 1},        // ditto
+    {"HS", "A", "\\# 5 0102030405", 1}, // A RR for non-IN class; varlen data
+    {NULL, NULL, NULL, 0}
+};
+
+// The following two functions will be used to generate wire format data
+// from encoded representation of each RDATA.
+void
+renderNameField(MessageRenderer* renderer, bool additional_required,
+                const LabelSequence& labels, RdataNameAttributes attributes)
+{
+    EXPECT_EQ(additional_required, (attributes & NAMEATTR_ADDITIONAL) != 0);
+    renderer->writeName(labels, (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+}
+
+void
+renderDataField(MessageRenderer* renderer, const void* data, size_t data_len) {
+    renderer->writeData(data, data_len);
+}
+
+class RdataSerializationTest : public ::testing::Test {
+protected:
+    RdataSerializationTest() : a_rdata_(createRdata(RRType::A(), RRClass::IN(),
+                                                    "192.0.2.53")),
+                         aaaa_rdata_(createRdata(RRType::AAAA(), RRClass::IN(),
+                                                 "2001:db8::53")),
+                         rrsig_rdata_(createRdata(
+                                          RRType::RRSIG(), RRClass::IN(),
+                                          "A 5 2 3600 20120814220826 "
+                                          "20120715220826 12345 com. FAKE"))
+    {}
+
+    // A wraper for RdataEncoder::encode() with buffer overrun check.
+    void encodeWrapper(size_t data_len);
+
+    // Some commonly used RDATA
+    const ConstRdataPtr a_rdata_;
+    const ConstRdataPtr aaaa_rdata_;
+    const ConstRdataPtr rrsig_rdata_;
+
+    RdataEncoder encoder_;
+    vector<uint8_t> encoded_data_;
+    MessageRenderer expected_renderer_;
+    MessageRenderer actual_renderer_;
+    vector<ConstRdataPtr> rdata_list_;
+};
+
+// There are several ways to decode the data. For one, there are
+// more interfaces uses for RdataReader, and we use our own decoder,
+// to check the actual encoded data.
+//
+// These decoding ways are provided by the template parameter.
+template<class DecoderStyle>
+class RdataEncodeDecodeTest : public RdataSerializationTest {
+public:
+    // This helper test method encodes the given list of RDATAs
+    // (in rdata_list), and then iterates over the data, rendering the fields
+    // in the wire format.  It then compares the wire data with the one
+    // generated by the normal libdns++ interface to see the encoding/decoding
+    // works as intended.
+    void checkEncode(RRClass rrclass, RRType rrtype,
+                     const vector<ConstRdataPtr>& rdata_list,
+                     size_t expected_varlen_fields,
+                     const vector<ConstRdataPtr>& rrsig_list =
+                     vector<ConstRdataPtr>());
+
+    void addRdataCommon(const vector<ConstRdataPtr>& rrsigs);
+    void addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs);
+};
+
+// Used across more classes and scopes. But it's just uninteresting
+// constant.
+const Name& dummyName2() {
+    static const Name result("example.com");
+    return (result);
+}
+
+bool
+additionalRequired(const RRType& type) {
+    // The set of RR types that require additional section processing.
+    // We'll use it to determine what value should the renderNameField get
+    // and, if the stored attributes are as expected.
+    static std::set<RRType> need_additionals;
+    if (need_additionals.empty()) {
+        need_additionals.insert(RRType::NS());
+        need_additionals.insert(RRType::MX());
+        need_additionals.insert(RRType::SRV());
+    }
+
+    return (need_additionals.find(type) != need_additionals.end());
+}
+
+// A decoder that does not use RdataReader. Not recommended for use,
+// but it allows the tests to check the internals of the data.
+class ManualDecoderStyle {
+public:
+    static void foreachRdataField(RRClass rrclass, RRType rrtype,
+                                  size_t rdata_count,
+                                  const vector<uint8_t>& encoded_data,
+                                  const vector<uint16_t>& varlen_list,
+                                  RdataReader::NameAction name_callback,
+                                  RdataReader::DataAction data_callback)
+    {
+        const RdataEncodeSpec& encode_spec = getRdataEncodeSpec(rrclass,
+                                                                rrtype);
+
+        size_t off = 0;
+        size_t varlen_count = 0;
+        size_t name_count = 0;
+        for (size_t count = 0; count < rdata_count; ++count) {
+            for (size_t i = 0; i < encode_spec.field_count; ++i) {
+                const RdataFieldSpec& field_spec = encode_spec.fields[i];
+                switch (field_spec.type) {
+                    case RdataFieldSpec::FIXEDLEN_DATA:
+                        if (data_callback) {
+                            data_callback(&encoded_data.at(off),
+                                          field_spec.fixeddata_len);
+                        }
+                        off += field_spec.fixeddata_len;
+                        break;
+                    case RdataFieldSpec::VARLEN_DATA:
+                        {
+                            const size_t varlen = varlen_list.at(varlen_count);
+                            if (data_callback && varlen > 0) {
+                                data_callback(&encoded_data.at(off), varlen);
+                            }
+                            off += varlen;
+                            ++varlen_count;
+                            break;
+                        }
+                    case RdataFieldSpec::DOMAIN_NAME:
+                        {
+                            ++name_count;
+                            const LabelSequence labels(&encoded_data.at(off));
+                            if (name_callback) {
+                                name_callback(labels,
+                                              field_spec.name_attributes);
+                            }
+                            off += labels.getSerializedLength();
+                            break;
+                        }
+                }
+            }
+        }
+        assert(name_count == encode_spec.name_count * rdata_count);
+        assert(varlen_count == encode_spec.varlen_count * rdata_count);
+    }
+
+    static void foreachRRSig(const vector<uint8_t>& encoded_data,
+                             const vector<uint16_t>& rrsiglen_list,
+                             RdataReader::DataAction data_callback)
+    {
+        size_t rrsig_totallen = 0;
+        for (vector<uint16_t>::const_iterator it = rrsiglen_list.begin();
+             it != rrsiglen_list.end();
+             ++it) {
+            rrsig_totallen += *it;
+        }
+        assert(encoded_data.size() >= rrsig_totallen);
+
+        const uint8_t* dp = &encoded_data[encoded_data.size() -
+            rrsig_totallen];
+        for (size_t i = 0; i < rrsiglen_list.size(); ++i) {
+            data_callback(dp, rrsiglen_list[i]);
+            dp += rrsiglen_list[i];
+        }
+    }
+
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count,
+                       size_t rrsig_count,
+                       size_t expected_varlen_fields,
+                       // Warning: this test actualy might change the
+                       // encoded_data !
+                       vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        // If this type of RDATA is expected to contain variable-length fields,
+        // we brute force the encoded data, exploiting our knowledge of actual
+        // encoding, then adjust the encoded data excluding the list of length
+        // fields.  This is ugly, but for tests only.
+        vector<uint16_t> varlen_list;
+        if (expected_varlen_fields > 0) {
+            const size_t varlen_list_size =
+                rdata_count * expected_varlen_fields * sizeof(uint16_t);
+            ASSERT_LE(varlen_list_size, encoded_data.size());
+            varlen_list.resize(rdata_count * expected_varlen_fields);
+            std::memcpy(&varlen_list[0], &encoded_data[0], varlen_list_size);
+            encoded_data.assign(encoded_data.begin() + varlen_list_size,
+                                encoded_data.end());
+        }
+
+        // If RRSIGs are given, we need to extract the list of the RRSIG
+        // lengths and adjust encoded_data_ further.
+        vector<uint16_t> rrsiglen_list;
+        if (rrsig_count > 0) {
+            const size_t rrsig_len_size = rrsig_count * sizeof(uint16_t);
+            ASSERT_LE(rrsig_len_size, encoded_data.size());
+            rrsiglen_list.resize(rrsig_count * rrsig_len_size);
+            std::memcpy(&rrsiglen_list[0], &encoded_data[0], rrsig_len_size);
+            encoded_data.assign(encoded_data.begin() + rrsig_len_size,
+                                encoded_data.end());
+        }
+
+        // Create wire-format data from the encoded data
+        foreachRdataField(rrclass, rrtype, rdata_count, encoded_data,
+                          varlen_list,
+                          boost::bind(renderNameField, &renderer,
+                                      additionalRequired(rrtype), _1, _2),
+                          boost::bind(renderDataField, &renderer, _1, _2));
+
+        // 2nd dummy name
+        renderer.writeName(dummyName2());
+        // Finally, dump any RRSIGs in wire format.
+        foreachRRSig(encoded_data, rrsiglen_list,
+                     boost::bind(renderDataField, &renderer, _1, _2));
+    }
+};
+
+// Check using callbacks and calling next until the end.
+class CallbackDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        RdataReader reader(rrclass, rrtype, &encoded_data[0], rdata_count,
+                           sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(renderDataField, &renderer, _1, _2));
+        while (reader.next() != RdataReader::RRSET_BOUNDARY) {}
+        renderer.writeName(dummyName2());
+        while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+    }
+};
+
+// Check using callbacks and calling iterate.
+class IterateDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(renderDataField, &renderer, _1, _2));
+        reader.iterate();
+        renderer.writeName(dummyName2());
+        reader.iterateAllSigs();
+    }
+};
+
+namespace {
+
+// Render the data to renderer, if one is set, or put it inside
+// a data buffer.
+void
+appendOrRenderData(vector<uint8_t>* where, MessageRenderer** renderer,
+                   const void* data, size_t size)
+{
+    if (*renderer != NULL) {
+        (*renderer)->writeData(data, size);
+    } else {
+        where->insert(where->end(), reinterpret_cast<const uint8_t*>(data),
+                      reinterpret_cast<const uint8_t*>(data) + size);
+    }
+}
+
+}
+
+// Similar to IterateDecoder, but it first iterates a little and rewinds
+// before actual rendering.
+class RewindAndDecode {
+private:
+    static void writeName(MessageRenderer** renderer,
+                          const LabelSequence& labels,
+                          RdataNameAttributes attributes)
+    {
+        (*renderer)->writeName(labels,
+                               (attributes & NAMEATTR_COMPRESSIBLE) != 0);
+    }
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        MessageRenderer dump; // A place to dump the extra data from before
+                              // actual rendering.
+        MessageRenderer* current = &dump;
+        vector<uint8_t> placeholder; // boost::bind does not like NULL
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(writeName, &current, _1, _2),
+                           boost::bind(appendOrRenderData, &placeholder,
+                                       &current, _1, _2));
+        // Iterate a little and rewind
+        reader.next();
+        reader.nextSig();
+        reader.rewind();
+        // Do the actual rendering
+        current = &renderer;
+        reader.iterate();
+        renderer.writeName(dummyName2());
+        reader.iterateAllSigs();
+    }
+};
+
+// Decode using the iteration over one rdata each time.
+// We also count there's the correct count of Rdatas.
+class SingleIterateDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data, size_t,
+                       MessageRenderer& renderer)
+    {
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(renderDataField, &renderer, _1, _2));
+        size_t actual_count = 0;
+        while (reader.iterateRdata()) {
+            ++actual_count;
+        }
+        EXPECT_EQ(rdata_count, actual_count);
+        actual_count = 0;
+        renderer.writeName(dummyName2());
+        while (reader.iterateSingleSig()) {
+            ++actual_count;
+        }
+        EXPECT_EQ(sig_count, actual_count);
+    }
+};
+
+// This one does not adhere to the usual way the reader is used, trying
+// to confuse it. It iterates part of the data manually and then reads
+// the rest through iterate. It also reads the signatures in the middle
+// of rendering.
+template<bool start_data, bool start_sig>
+class HybridDecoder {
+public:
+    static void decode(const isc::dns::RRClass& rrclass,
+                       const isc::dns::RRType& rrtype,
+                       size_t rdata_count, size_t sig_count, size_t,
+                       const vector<uint8_t>& encoded_data,
+                       size_t encoded_data_len,
+                       MessageRenderer& renderer)
+    {
+        vector<uint8_t> data;
+        MessageRenderer* current;
+        RdataReader reader(rrclass, rrtype, &encoded_data[0],
+                           rdata_count, sig_count,
+                           boost::bind(renderNameField, &renderer,
+                                       additionalRequired(rrtype), _1, _2),
+                           boost::bind(appendOrRenderData, &data, &current, _1,
+                                       _2));
+        // The size matches
+        EXPECT_EQ(encoded_data_len, reader.getSize());
+        if (start_sig) {
+            current = NULL;
+            reader.nextSig();
+        }
+        // Render first part of data. If there's none, return empty Result and
+        // do nothing.
+        if (start_data) {
+            current = &renderer;
+            reader.next();
+        }
+        // Now, we let all sigs to be copied to data. We disable the
+        // renderer for this.
+        current = NULL;
+        reader.iterateAllSigs();
+        // Now return the renderer and render the rest of the data
+        current = &renderer;
+        reader.iterate();
+        // Now, this should not break anything and should be valid, but should
+        // return ends.
+        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.next());
+        EXPECT_EQ(RdataReader::RRSET_BOUNDARY, reader.nextSig());
+        // Render the name and the sigs
+        renderer.writeName(dummyName2());
+        renderer.writeData(&data[0], data.size());
+        // The size matches even after use
+        EXPECT_EQ(encoded_data_len, reader.getSize());
+    }
+};
+
+typedef ::testing::Types<ManualDecoderStyle,
+                         CallbackDecoder, IterateDecoder, SingleIterateDecoder,
+                         HybridDecoder<true, true>, HybridDecoder<true, false>,
+                         HybridDecoder<false, true>,
+                         HybridDecoder<false, false> >
+    DecoderStyles;
+// Each decoder style must contain a decode() method. Such method is expected
+// to decode the passed data, first render the Rdata into the passed renderer,
+// then write the dummyName2() there and write the RRSig data after that.
+// It may do other checks too.
+//
+// There are some slight differences to how to do the decoding, that's why we
+// have the typed test.
+TYPED_TEST_CASE(RdataEncodeDecodeTest, DecoderStyles);
+
+void
+RdataSerializationTest::encodeWrapper(size_t data_len) {
+    // make sure the data buffer is large enough for the canary
+    encoded_data_.resize(data_len + 2);
+    // set the canary data
+    encoded_data_.at(data_len) = 0xde;
+    encoded_data_.at(data_len + 1) = 0xad;
+    // encode, then check the canary is intact
+    encoder_.encode(&encoded_data_[0], data_len);
+    EXPECT_EQ(0xde, encoded_data_.at(data_len));
+    EXPECT_EQ(0xad, encoded_data_.at(data_len + 1));
+    // shrink the data buffer to the originally expected size (some tests
+    // expect that).  the actual encoded data should be intact.
+    encoded_data_.resize(data_len);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+checkEncode(RRClass rrclass, RRType rrtype,
+            const vector<ConstRdataPtr>& rdata_list,
+            size_t expected_varlen_fields,
+            const vector<ConstRdataPtr>& rrsig_list)
+{
+    // These two names will be rendered before and after the test RDATA,
+    // to check in case the RDATA contain a domain name whether it's
+    // compressed or not correctly.  The names in the RDATA should basically
+    // a subdomain of example.com, so it can be compressed due to dummyName2().
+    // Likewise, dummyName2() should be able to be fully compressed due to
+    // the name in the RDATA.
+    const Name dummy_name("com");
+
+    expected_renderer_.clear();
+    actual_renderer_.clear();
+    encoded_data_.clear();
+
+    // Build expected wire-format data
+    expected_renderer_.writeName(dummy_name);
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+        rdata->toWire(expected_renderer_);
+    }
+    expected_renderer_.writeName(dummyName2());
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+        rdata->toWire(expected_renderer_);
+    }
+
+    // Then build wire format data using the encoded data.
+    // 1st dummy name
+    actual_renderer_.writeName(dummy_name);
+
+    // Create encoded data
+    encoder_.start(rrclass, rrtype);
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rdata_list) {
+        encoder_.addRdata(*rdata);
+    }
+    BOOST_FOREACH(const ConstRdataPtr& rdata, rrsig_list) {
+        encoder_.addSIGRdata(*rdata);
+    }
+    const size_t storage_len = encoder_.getStorageLength();
+    encodeWrapper(storage_len);
+
+    DecoderStyle::decode(rrclass, rrtype, rdata_list.size(), rrsig_list.size(),
+                         expected_varlen_fields, encoded_data_, storage_len,
+                         actual_renderer_);
+
+    // Two sets of wire-format data should be identical.
+    matchWireData(expected_renderer_.getData(), expected_renderer_.getLength(),
+                  actual_renderer_.getData(), actual_renderer_.getLength());
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataCommon(const vector<ConstRdataPtr>& rrsigs) {
+    // Basic check on the encoded data for (most of) all supported RR types,
+    // in a comprehensive manner.
+    for (size_t i = 0; test_rdata_list[i].rrclass != NULL; ++i) {
+        SCOPED_TRACE(string(test_rdata_list[i].rrclass) + "/" +
+                     test_rdata_list[i].rrtype);
+        const RRClass rrclass(test_rdata_list[i].rrclass);
+        const RRType rrtype(test_rdata_list[i].rrtype);
+        const ConstRdataPtr rdata = createRdata(rrtype, rrclass,
+                                                test_rdata_list[i].rdata);
+        rdata_list_.clear();
+        rdata_list_.push_back(rdata);
+        checkEncode(rrclass, rrtype, rdata_list_,
+                    test_rdata_list[i].n_varlen_fields, rrsigs);
+    }
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdata) {
+    vector<ConstRdataPtr> rrsigs;
+    this->addRdataCommon(rrsigs); // basic tests without RRSIGs (empty vector)
+
+    // Test with RRSIGs (covered type doesn't always match, but the encoder
+    // doesn't check that)
+    rrsigs.push_back(this->rrsig_rdata_);
+    this->addRdataCommon(rrsigs);
+}
+
+template<class DecoderStyle>
+void
+RdataEncodeDecodeTest<DecoderStyle>::
+addRdataMultiCommon(const vector<ConstRdataPtr>& rrsigs) {
+    // Similar to addRdata(), but test with multiple RDATAs.
+    // Four different cases are tested: a single fixed-len RDATA (A),
+    // fixed-len data + domain name (MX), variable-len data only (TXT),
+    // variable-len data + domain name (NAPTR).
+    ConstRdataPtr a_rdata2 = createRdata(RRType::A(), RRClass::IN(),
+                                         "192.0.2.54");
+    rdata_list_.clear();
+    rdata_list_.push_back(a_rdata_);
+    rdata_list_.push_back(a_rdata2);
+    checkEncode(RRClass::IN(), RRType::A(), rdata_list_, 0, rrsigs);
+
+    ConstRdataPtr mx_rdata1 = createRdata(RRType::MX(), RRClass::IN(),
+                                          "5 mx1.example.com");
+    ConstRdataPtr mx_rdata2 = createRdata(RRType::MX(), RRClass::IN(),
+                                          "10 mx2.example.com");
+    rdata_list_.clear();
+    rdata_list_.push_back(mx_rdata1);
+    rdata_list_.push_back(mx_rdata2);
+    checkEncode(RRClass::IN(), RRType::MX(), rdata_list_, 0, rrsigs);
+
+    ConstRdataPtr txt_rdata1 = createRdata(RRType::TXT(), RRClass::IN(),
+                                           "foo bar baz");
+    ConstRdataPtr txt_rdata2 = createRdata(RRType::TXT(), RRClass::IN(),
+                                          "another text data");
+    rdata_list_.clear();
+    rdata_list_.push_back(txt_rdata1);
+    rdata_list_.push_back(txt_rdata2);
+    checkEncode(RRClass::IN(), RRType::TXT(), rdata_list_, 1, rrsigs);
+
+    ConstRdataPtr naptr_rdata1 =
+        createRdata(RRType::NAPTR(), RRClass::IN(),
+                    "100 50 \"s\" \"http\" \"\" _http._tcp.example.com");
+    ConstRdataPtr naptr_rdata2 =
+        createRdata(RRType::NAPTR(), RRClass::IN(),
+                    "200 100 \"s\" \"http\" \"\" _http._tcp.example.com");
+    rdata_list_.clear();
+    rdata_list_.push_back(naptr_rdata1);
+    rdata_list_.push_back(naptr_rdata2);
+    checkEncode(RRClass::IN(), RRType::NAPTR(), rdata_list_, 1, rrsigs);
+}
+
+void ignoreName(const LabelSequence&, unsigned) {
+}
+
+void
+checkLargeData(const in::DHCID* decoded, bool* called, const void* encoded,
+               size_t length)
+{
+    EXPECT_FALSE(*called); // Called exactly once
+    *called = true;
+
+    // Reconstruct the Rdata and check it.
+    isc::util::InputBuffer ib(encoded, length);
+    const in::DHCID reconstructed(ib, ib.getLength());
+    EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, encodeLargeRdata) {
+    // There should be no reason for a large RDATA to fail in encoding,
+    // but we check such a case explicitly.
+
+    encoded_data_.resize(65535); // max unsigned 16-bit int
+    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+    const in::DHCID large_dhcid(buffer, encoded_data_.size());
+
+    encoder_.start(RRClass::IN(), RRType::DHCID());
+    encoder_.addRdata(large_dhcid);
+    encodeWrapper(encoder_.getStorageLength());
+
+    // The encoded data should be identical to the original one.
+    bool called = false;
+    RdataReader reader(RRClass::IN(), RRType::DHCID(), &encoded_data_[0], 1, 0,
+                       ignoreName, boost::bind(checkLargeData, &large_dhcid,
+                                               &called, _1, _2));
+    reader.iterate();
+    EXPECT_TRUE(called);
+    called = false;
+    reader.iterateAllSigs();
+    EXPECT_FALSE(called);
+}
+
+TYPED_TEST(RdataEncodeDecodeTest, addRdataMulti) {
+    vector<ConstRdataPtr> rrsigs;
+    this->addRdataMultiCommon(rrsigs); // test without RRSIGs (empty vector)
+
+    // Tests with two RRSIGs
+    rrsigs.push_back(this->rrsig_rdata_);
+    rrsigs.push_back(createRdata(RRType::RRSIG(), RRClass::IN(),
+                                 "A 5 2 3600 20120814220826 "
+                                 "20120715220826 54321 com. FAKE"));
+    this->addRdataMultiCommon(rrsigs);
+}
+
+TEST_F(RdataSerializationTest, badAddRdata) {
+    // Some operations must follow start().
+    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::InvalidOperation);
+    EXPECT_THROW(encoder_.getStorageLength(), isc::InvalidOperation);
+    // will allocate space of some arbitrary size (256 bytes)
+    EXPECT_THROW(encodeWrapper(256), isc::InvalidOperation);
+
+    // Bad buffer for encode
+    encoder_.start(RRClass::IN(), RRType::A());
+    encoder_.addRdata(*a_rdata_);
+    const size_t buf_len = encoder_.getStorageLength();
+    // NULL buffer for encode
+    EXPECT_THROW(encoder_.encode(NULL, buf_len), isc::BadValue);
+    // buffer length is too short (we don't use the wrraper because we don't
+    // like to tweak the length arg to encode()).
+    encoded_data_.resize(buf_len - 1);
+    EXPECT_THROW(encoder_.encode(&encoded_data_[0], buf_len - 1),
+                 isc::BadValue);
+
+    // Type of RDATA and the specified RR type don't match.  addRdata() should
+    // detect this inconsistency.
+    encoder_.start(RRClass::IN(), RRType::AAAA());
+    EXPECT_THROW(encoder_.addRdata(*a_rdata_), isc::BadValue);
+
+    // Likewise.
+    encoder_.start(RRClass::IN(), RRType::A());
+    EXPECT_THROW(encoder_.addRdata(*aaaa_rdata_), isc::BadValue);
+
+    // Likewise.  The encoder expects the first name completes the data, and
+    // throws on the second due as an unexpected name field.
+    const ConstRdataPtr rp_rdata =
+        createRdata(RRType::RP(), RRClass::IN(), "a.example. b.example");
+    encoder_.start(RRClass::IN(), RRType::NS());
+    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+    // Likewise.  The encoder considers the name data a variable length data
+    // field, and throws on the first name.
+    encoder_.start(RRClass::IN(), RRType::DHCID());
+    EXPECT_THROW(encoder_.addRdata(*rp_rdata), isc::BadValue);
+
+    // Likewise.  The text RDATA (2 bytes) will be treated as MX preference,
+    // and the encoder will still expect to see a domain name.
+    const ConstRdataPtr txt_rdata = createRdata(RRType::TXT(), RRClass::IN(),
+                                                "a");
+    encoder_.start(RRClass::IN(), RRType::MX());
+    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+    // Similar to the previous one, but in this case there's no data field
+    // in the spec.
+    encoder_.start(RRClass::IN(), RRType::NS());
+    EXPECT_THROW(encoder_.addRdata(*txt_rdata), isc::BadValue);
+
+    // Likewise.  Inconsistent name compression policy.
+    const ConstRdataPtr ns_rdata =
+        createRdata(RRType::NS(), RRClass::IN(), "ns.example");
+    encoder_.start(RRClass::IN(), RRType::DNAME());
+    EXPECT_THROW(encoder_.addRdata(*ns_rdata), isc::BadValue);
+
+    // Same as the previous one, opposite inconsistency.
+    const ConstRdataPtr dname_rdata =
+        createRdata(RRType::DNAME(), RRClass::IN(), "dname.example");
+    encoder_.start(RRClass::IN(), RRType::NS());
+    EXPECT_THROW(encoder_.addRdata(*dname_rdata), isc::BadValue);
+
+    // RDATA len exceeds the 16-bit range.  Technically not invalid, but
+    // we don't support that (and it's practically useless anyway).
+    encoded_data_.resize(65536); // use encoded_data_ for placeholder
+    isc::util::InputBuffer buffer(&encoded_data_[0], encoded_data_.size());
+    encoder_.start(RRClass::IN(), RRType::DHCID());
+    EXPECT_THROW(encoder_.addRdata(in::DHCID(buffer, encoded_data_.size())),
+                                   RdataEncodingError);
+
+    // RRSIG cannot be used as the main RDATA type (can only be added as
+    // a signature for some other type of RDATAs).
+    EXPECT_THROW(encoder_.start(RRClass::IN(), RRType::RRSIG()),
+                 isc::BadValue);
+}
+
+void
+checkSigData(const ConstRdataPtr& decoded, bool* called, const void* encoded,
+             size_t length)
+{
+    EXPECT_FALSE(*called); // Called exactly once
+    *called = true;
+
+    // Reconstruct the RRSig and check it.
+    isc::util::InputBuffer ib(encoded, length);
+    const generic::RRSIG reconstructed(ib, ib.getLength());
+    EXPECT_EQ(0, reconstructed.compare(*decoded));
+}
+
+TEST_F(RdataSerializationTest, addSIGRdataOnly) {
+    // Encoded data that only contain RRSIGs.  Mostly useless, but can happen
+    // (in a partially broken zone) and it's accepted.
+    encoder_.start(RRClass::IN(), RRType::A());
+    encoder_.addSIGRdata(*rrsig_rdata_);
+    encodeWrapper(encoder_.getStorageLength());
+    ASSERT_LT(sizeof(uint16_t), encoder_.getStorageLength());
+
+    bool called = false;
+    RdataReader reader(RRClass::IN(), RRType::A(), &encoded_data_[0], 0, 1,
+                       ignoreName, boost::bind(checkSigData, rrsig_rdata_,
+                                               &called, _1, _2));
+    reader.iterate();
+    EXPECT_FALSE(called);
+    reader.iterateAllSigs();
+    EXPECT_TRUE(called);
+}
+
+TEST_F(RdataSerializationTest, badAddSIGRdata) {
+    // try adding SIG before start
+    EXPECT_THROW(encoder_.addSIGRdata(*rrsig_rdata_), isc::InvalidOperation);
+
+    // Very big RRSIG.  This implementation rejects it.
+    isc::util::OutputBuffer ob(0);
+    rrsig_rdata_->toWire(ob);
+    // append dummy trailing signature to make it too big
+    vector<uint8_t> dummy_sig(65536 - ob.getLength());
+    ob.writeData(&dummy_sig[0], dummy_sig.size());
+    ASSERT_EQ(65536, ob.getLength());
+
+    isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+    const generic::RRSIG big_sigrdata(ib, ob.getLength());
+    encoder_.start(RRClass::IN(), RRType::A());
+    EXPECT_THROW(encoder_.addSIGRdata(big_sigrdata), RdataEncodingError);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/rdataset_unittest.cc b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
new file mode 100644
index 0000000..897e53c
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/rdataset_unittest.cc
@@ -0,0 +1,298 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrset.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using boost::lexical_cast;
+
+namespace {
+
+class RdataSetTest : public ::testing::Test {
+protected:
+    RdataSetTest() :
+        // 1076895760 = 0x40302010.  Use this so we fill in all 8-bit "field"
+        // of the 32-bit TTL
+        a_rrset_(textToRRset("www.example.com. 1076895760 IN A 192.0.2.1")),
+        rrsig_rrset_(textToRRset("www.example.com. 1076895760 IN RRSIG "
+                                 "A 5 2 3600 20120814220826 20120715220826 "
+                                 "1234 example.com. FAKE"))
+    {}
+    void TearDown() {
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    ConstRRsetPtr a_rrset_, rrsig_rrset_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+    RdataEncoder encoder_;
+};
+
+// Convert the given 32-bit integer (network byte order) to the corresponding
+// RRTTL object.
+RRTTL
+restoreTTL(const void* ttl_data) {
+    isc::util::InputBuffer b(ttl_data, sizeof(uint32_t));
+    return (RRTTL(b));
+}
+
+// A helper callback for checkRdataSet.  This confirms the given data
+// is the expected in::A RDATA (the value is taken from the RdataSetTest
+// constructor).
+void
+checkData(const void* data, size_t size) {
+    isc::util::InputBuffer b(data, size);
+    EXPECT_EQ(0, in::A(b, size).compare(in::A("192.0.2.1")));
+}
+
+// This is a set of checks for an RdataSet created with some simple
+// conditions.  with_rrset/with_rrsig is true iff the RdataSet is supposed to
+// contain normal/RRSIG RDATA.
+void
+checkRdataSet(const RdataSet& rdataset, bool with_rrset, bool with_rrsig) {
+    EXPECT_FALSE(rdataset.next); // by default the next pointer should be NULL
+    EXPECT_EQ(RRType::A(), rdataset.type);
+    // See the RdataSetTest constructor for the magic number.
+    EXPECT_EQ(RRTTL(1076895760), restoreTTL(rdataset.getTTLData()));
+    EXPECT_EQ(with_rrset ? 1 : 0, rdataset.getRdataCount());
+    EXPECT_EQ(with_rrsig ? 1 : 0, rdataset.getSigRdataCount());
+
+    // A simple test for the data content.  Details tests for the encoder/
+    // reader should be basically sufficient for various cases of the data,
+    // and the fact that this test doesn't detect memory leak should be
+    // reasonably sufficient that the implementation handles the data region
+    // correctly.  Here we check one simple case for a simple form of RDATA,
+    // mainly for checking the behavior of getDataBuf().
+    RdataReader reader(RRClass::IN(), RRType::A(),
+                       reinterpret_cast<const uint8_t*>(
+                           rdataset.getDataBuf()),
+                       rdataset.getRdataCount(), rdataset.getSigRdataCount(),
+                       &RdataReader::emptyNameAction, checkData);
+    reader.iterate();
+}
+
+TEST_F(RdataSetTest, create) {
+    // A simple case of creating an RdataSet.  Confirming the resulting
+    // fields have the expected values, and then destroying it (TearDown()
+    // would detect any memory leak)
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+    checkRdataSet(*rdataset, true, false);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, getNext) {
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          ConstRRsetPtr());
+
+    // By default, the next pointer should be NULL (already tested in other
+    // test cases), which should be the case with getNext().  We test both
+    // mutable and immutable versions of getNext().
+    EXPECT_EQ(static_cast<RdataSet*>(NULL), rdataset->getNext());
+    EXPECT_EQ(static_cast<const RdataSet*>(NULL),
+              static_cast<const RdataSet*>(rdataset)->getNext());
+
+    // making a link (it would form an infinite loop, but it doesn't matter
+    // in this test), and check the pointer returned by getNext().
+    rdataset->next = rdataset;
+    EXPECT_EQ(rdataset, static_cast<const RdataSet*>(rdataset)->getNext());
+
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRsetWithRdataCount(size_t rdata_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(), RRType::TXT(),
+                             RRTTL(3600)));
+    for (size_t i = 0; i < rdata_count; ++i) {
+        rrset->addRdata(rdata::createRdata(RRType::TXT(), RRClass::IN(),
+                                           lexical_cast<std::string>(i)));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRs) {
+    // RRset with possible maximum number of RDATAs
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_,
+                                          getRRsetWithRdataCount(8191),
+                                          ConstRRsetPtr());
+    EXPECT_EQ(8191, rdataset->getRdataCount());
+    EXPECT_EQ(0, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding that will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(8192),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_,
+                                  getRRsetWithRdataCount(65535),
+                                  ConstRRsetPtr()),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIG) {
+    // Normal case.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Unusual case: TTL doesn't match.  This implementation accepts that,
+    // using the TTL of the covered RRset.
+    ConstRRsetPtr rrsig_badttl(textToRRset(
+                                   "www.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 "
+                                   "20120715220826 1234 example.com. FAKE"));
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_, rrsig_badttl);
+    checkRdataSet(*rdataset, true, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+// A helper function to create an RRSIG RRset containing the given number of
+// unique RDATAs.
+ConstRRsetPtr
+getRRSIGWithRdataCount(size_t sig_count) {
+    RRsetPtr rrset(new RRset(Name("example.com"), RRClass::IN(),
+                             RRType::RRSIG(), RRTTL(3600)));
+    // We use a base wire-format image and tweak the original TTL field to
+    // generate unique RDATAs in the loop.  (Creating them from corresponding
+    // text is simpler, but doing so for a large number of RRSIGs is
+    // relatively heavy and could be too long for unittests).
+    ConstRdataPtr rrsig_base =
+        rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                           "A 5 2 3600 20120814220826 20120715220826 1234 "
+                           "example.com. FAKE");
+    isc::util::OutputBuffer ob(0);
+    rrsig_base->toWire(ob);
+    for (size_t i = 0; i < sig_count; ++i) {
+        ob.writeUint16At((i >> 16) & 0xffff, 4);
+        ob.writeUint16At(i & 0xffff, 6);
+        isc::util::InputBuffer ib(ob.getData(), ob.getLength());
+        rrset->addRdata(rdata::createRdata(RRType::RRSIG(), RRClass::IN(),
+                                           ib, ib.getLength()));
+    }
+    return (rrset);
+}
+
+TEST_F(RdataSetTest, createManyRRSIGs) {
+    // 7 has a special meaning in the implementation: if the number of the
+    // RRSIGs reaches this value, an extra 'sig count' field will be created.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                          getRRSIGWithRdataCount(7));
+    EXPECT_EQ(7, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // 8 would cause overflow in the normal 3-bit field if there were no extra
+    // count field.
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(8));
+    EXPECT_EQ(8, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Up to 2^16-1 RRSIGs are allowed (although that would be useless
+    // in practice)
+    rdataset = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                getRRSIGWithRdataCount(65535));
+    EXPECT_EQ(65535, rdataset->getSigRdataCount());
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+
+    // Exceeding this limit will result in an exception.
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(65536)),
+                 RdataSetError);
+    // To be very sure even try larger number than the threshold
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  getRRSIGWithRdataCount(70000)),
+                 RdataSetError);
+}
+
+TEST_F(RdataSetTest, createWithRRSIGOnly) {
+    // A rare, but allowed, case: RdataSet without the main RRset but with
+    // RRSIG.
+    RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                          rrsig_rrset_);
+    checkRdataSet(*rdataset, false, true);
+    RdataSet::destroy(mem_sgmt_, RRClass::IN(), rdataset);
+}
+
+TEST_F(RdataSetTest, badCeate) {
+    // Neither the RRset nor RRSIG RRset is given
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  ConstRRsetPtr()), isc::BadValue);
+
+    // Empty RRset (An RRset without RDATA)
+    ConstRRsetPtr empty_rrset(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::A(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, empty_rrset,
+                                  ConstRRsetPtr()), isc::BadValue);
+    ConstRRsetPtr empty_rrsig(new RRset(Name("example.com"), RRClass::IN(),
+                                        RRType::RRSIG(), RRTTL(3600)));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, ConstRRsetPtr(),
+                                  empty_rrsig), isc::BadValue);
+
+    // The RRset type and RRSIG's type covered don't match
+    ConstRRsetPtr bad_rrsig(textToRRset(
+                                "www.example.com. 1076895760 IN RRSIG "
+                                "NS 5 2 3600 20120814220826 20120715220826 "
+                                "1234 example.com. FAKE"));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, bad_rrsig),
+                 isc::BadValue);
+
+    // Pass non RRSIG for the sig parameter
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_, a_rrset_),
+                 isc::BadValue);
+
+    // Pass RRSIG for normal RRset (the RdataEncoder will catch this and throw)
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, rrsig_rrset_,
+                                  rrsig_rrset_),
+                 isc::BadValue);
+
+    // RR class doesn't match between RRset and RRSIG
+    ConstRRsetPtr badclass_rrsig(textToRRset(
+                                     "www.example.com. 1076895760 CH RRSIG "
+                                     "A 5 2 3600 20120814220826 "
+                                     "20120715220826 1234 example.com. FAKE",
+                                     RRClass::CH()));
+    EXPECT_THROW(RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                  badclass_rrsig),
+                 isc::BadValue);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/run_unittests.cc b/src/lib/datasrc/tests/memory/run_unittests.cc
new file mode 100644
index 0000000..6321976
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/run_unittests.cc
@@ -0,0 +1,26 @@
+// 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 <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    isc::log::initLogger();
+
+    return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc b/src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc
new file mode 100644
index 0000000..d27e364
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/segment_object_holder_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/segment_object_holder.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::util;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::detail;
+
+namespace {
+const int TEST_ARG_VAL = 42;    // arbitrary chosen magic number
+
+class TestObject {
+public:
+    static void destroy(MemorySegment& sgmt, TestObject* obj, int arg) {
+        sgmt.deallocate(obj, sizeof(*obj));
+        EXPECT_EQ(TEST_ARG_VAL, arg);
+    }
+};
+
+void
+useHolder(MemorySegment& sgmt, TestObject* obj, bool release) {
+    // Create a holder object, check the return value of get(), and,
+    // if requested, release the held object.  At the end of function
+    // the holder is destructed, and if the object hasn't been released by
+    // then, it should be deallocated.  Passed argument is checked in its
+    // deallocate().
+
+    typedef SegmentObjectHolder<TestObject, int> HolderType;
+    HolderType holder(sgmt, obj, TEST_ARG_VAL);
+    EXPECT_EQ(obj, holder.get());
+    if (release) {
+        EXPECT_EQ(obj, holder.release());
+    }
+}
+
+TEST(SegmentObjectHolderTest, foo) {
+    MemorySegmentLocal sgmt;
+    void* p = sgmt.allocate(sizeof(TestObject));
+    TestObject* obj = new(p) TestObject;
+
+    // Use holder, and release the content.  The memory shouldn't be
+    // deallocated.
+    useHolder(sgmt, obj, true);
+    EXPECT_FALSE(sgmt.allMemoryDeallocated());
+
+    // Use holder, and let it deallocate the object.  The memory segment
+    // should now be empty.
+    useHolder(sgmt, obj, false);
+    EXPECT_TRUE(sgmt.allMemoryDeallocated());
+}
+}
diff --git a/src/lib/datasrc/tests/memory/testdata/Makefile.am b/src/lib/datasrc/tests/memory/testdata/Makefile.am
new file mode 100644
index 0000000..dddbf0a
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/Makefile.am
@@ -0,0 +1,31 @@
+CLEANFILES = *.copied
+
+EXTRA_DIST =  empty.zone
+EXTRA_DIST += example.org.zone
+EXTRA_DIST += example.org-empty.zone
+
+EXTRA_DIST += example.org-broken1.zone
+EXTRA_DIST += example.org-broken2.zone
+EXTRA_DIST += example.org-cname-and-not-nsec-1.zone
+EXTRA_DIST += example.org-cname-and-not-nsec-2.zone
+EXTRA_DIST += example.org-dname-ns-apex-1.zone
+EXTRA_DIST += example.org-dname-ns-apex-2.zone
+EXTRA_DIST += example.org-dname-ns-nonapex-1.zone
+EXTRA_DIST += example.org-dname-ns-nonapex-2.zone
+EXTRA_DIST += example.org-duplicate-type-bad.zone
+EXTRA_DIST += example.org-duplicate-type.zone
+EXTRA_DIST += example.org-multiple-cname.zone
+EXTRA_DIST += example.org-multiple-dname.zone
+EXTRA_DIST += example.org-multiple-nsec3.zone
+EXTRA_DIST += example.org-multiple-nsec3param.zone
+EXTRA_DIST += example.org-multiple.zone
+EXTRA_DIST += example.org-nsec3-empty-salt.zone
+EXTRA_DIST += example.org-nsec3-fewer-labels.zone example.org-nsec3-more-labels.zone
+EXTRA_DIST += example.org-nsec3-signed-no-param.zone
+EXTRA_DIST += example.org-nsec3-signed.zone
+EXTRA_DIST += example.org-out-of-zone.zone
+EXTRA_DIST += example.org-rrsig-follows-nothing.zone
+EXTRA_DIST += example.org-rrsigs.zone
+EXTRA_DIST += example.org-wildcard-dname.zone
+EXTRA_DIST += example.org-wildcard-ns.zone
+EXTRA_DIST += example.org-wildcard-nsec3.zone
diff --git a/src/lib/datasrc/tests/memory/testdata/empty.zone b/src/lib/datasrc/tests/memory/testdata/empty.zone
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone
new file mode 100644
index 0000000..317095d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-broken1.zone
@@ -0,0 +1 @@
+This is a broken zone that should not parse.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone
new file mode 100644
index 0000000..2f9fd98
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-broken2.zone
@@ -0,0 +1,5 @@
+;; broken example.org zone, where some RRs are OK, but others aren't
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 73 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A		192.0.2.1
+ns2.example.org.		      3600 IN A		192.0.2.2
+ns2.a.example.com.		      3600 IN AAAA
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone
new file mode 100644
index 0000000..5533663
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-1.zone
@@ -0,0 +1,4 @@
+;; CNAME + other is an error
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091009 7200 3600 2592000 1200
+a.example.org.				      7200  IN A	192.168.0.1
+a.example.org.				      3600  IN CNAME	foo.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone
new file mode 100644
index 0000000..966aeeb
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-cname-and-not-nsec-2.zone
@@ -0,0 +1,4 @@
+;; CNAME + other is an error
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091007 7200 3600 2592000 1200
+a.example.org.				      3600  IN CNAME	foo.example.com.
+a.example.org.				      7200  IN A	192.168.0.1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone
new file mode 100644
index 0000000..f57c25d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-1.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
+example.org.				      3600  IN DNAME	foo.example.com.
+example.org.				      3600  IN NS	bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone
new file mode 100644
index 0000000..bb3f191
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-apex-2.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091016 7200 3600 2592000 1200
+example.org.				      3600  IN NS	bar.example.com.
+example.org.				      3600  IN DNAME	foo.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone
new file mode 100644
index 0000000..68a9805
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-1.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (non-apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091014 7200 3600 2592000 1200
+ns1.example.org.			      3600  IN DNAME	foo.example.com.
+ns1.example.org.			      3600  IN NS	bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone
new file mode 100644
index 0000000..1b671dd
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-dname-ns-nonapex-2.zone
@@ -0,0 +1,4 @@
+;; DNAME + NS (non-apex)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091015 7200 3600 2592000 1200
+ns1.example.org.			      3600  IN NS	bar.example.com.
+ns1.example.org.			      3600  IN DNAME	foo.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone
new file mode 100644
index 0000000..06c7dff
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type-bad.zone
@@ -0,0 +1,4 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 77 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A	 	192.168.0.1
+ns1.example.org.		      3600 IN AAAA 	::1
+ns1.example.org.		      3600 IN A	 	192.168.0.2
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone
new file mode 100644
index 0000000..6e5c1b8
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-duplicate-type.zone
@@ -0,0 +1,4 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 76 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A	 	192.168.0.1
+ns1.example.org.		      3600 IN A	 	192.168.0.2
+ns1.example.org.		      3600 IN AAAA 	::1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-empty.zone b/src/lib/datasrc/tests/memory/testdata/example.org-empty.zone
new file mode 100644
index 0000000..f11b9b8
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-empty.zone
@@ -0,0 +1,2 @@
+;; empty example.org zone
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone
new file mode 100644
index 0000000..0a0c983
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-cname.zone
@@ -0,0 +1,3 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+ns1.example.org.		      3600 IN CNAME	foo.example.com.
+ns1.example.org.		      3600 IN CNAME	bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone
new file mode 100644
index 0000000..3d581d0
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-dname.zone
@@ -0,0 +1,3 @@
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+ns1.example.org.		      3600 IN DNAME	foo.example.com.
+ns1.example.org.		      3600 IN DNAME	bar.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone
new file mode 100644
index 0000000..874023a
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3.zone
@@ -0,0 +1,3 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012090702 7200 3600 2592000 1200
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone
new file mode 100644
index 0000000..5e69518
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple-nsec3param.zone
@@ -0,0 +1,3 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012090700 7200 3600 2592000 1200
+example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
+example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone b/src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone
new file mode 100644
index 0000000..f473ae6
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-multiple.zone
@@ -0,0 +1,4 @@
+;; Multiple RDATA for testing separate RRs iterator
+example.org.  		   	 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+a.example.org.		   	 3600 IN A	192.168.0.1
+a.example.org.		   	 3600 IN A	192.168.0.2
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone
new file mode 100644
index 0000000..6c20ee9
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-empty-salt.zone
@@ -0,0 +1,14 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012092602 7200 3600 2592000 1200
+example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org.				      86400 IN NS	ns.example.org.
+example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+example.org.				      0	IN NSEC3PARAM	1 0 10 -
+example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org.				      86400 IN A	192.0.2.1
+ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 - RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 - 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone
new file mode 100644
index 0000000..0221269
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-fewer-labels.zone
@@ -0,0 +1,3 @@
+;; NSEC3 names with labels != (origin_labels + 1)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091001 7200 3600 2592000 1200
+example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone
new file mode 100644
index 0000000..efebcfb
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-more-labels.zone
@@ -0,0 +1,3 @@
+;; NSEC3 names with labels != (origin_labels + 1)
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012091002 7200 3600 2592000 1200
+a.b.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone
new file mode 100644
index 0000000..5caa308
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed-no-param.zone
@@ -0,0 +1,15 @@
+;; This file intentionally removes NSEC3PARAM from example.org.nsec3-signed
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
+example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org.				      86400 IN NS	ns.example.org.
+example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+;; example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
+;; example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org.				      86400 IN A	192.0.2.1
+ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone
new file mode 100644
index 0000000..9c1195f
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-nsec3-signed.zone
@@ -0,0 +1,14 @@
+example.org.				      86400 IN SOA	ns.example.org. ns.example.org. 2012013000 7200 3600 2592000 1200
+example.org.				      86400 IN RRSIG	SOA 7 2 86400 20120301040838 20120131040838 19562 example.org. Jt9wCRLS5TQxZH0IBqrM9uMGD453rIoxYopfM9AjjRZfEx+HGlBpOZeR pGN7yLcN+URnicOD0ydLHiakaBODiZyNoYCKYG5d2ZOhL+026REnDKNM 0m5T3X3sczP+l55An/GITheTdrKt3Y1Ouc2yKI8ro8JjOxV/a4nGDWjK x9A=
+example.org.				      86400 IN NS	ns.example.org.
+example.org.				      86400 IN RRSIG	NS 7 2 86400 20120301040838 20120131040838 19562 example.org. gYXL3xK4IFdJU6TtiVuzqDBb2MeA8xB3AKtHlJGFTfTRNHyuej0ZGovx TeUYsLYmoiGYaJG66iD1tYYFq0qdj0xWq+LEa53ACtKvYf9IIwK4ijJs k0g6xCNavc6/qPppymDhN7MvoFVkW59uJa0HPWlsIIuRlEAr7xyt85vq yoA=
+example.org.				      86400 IN DNSKEY	256 3 7 AwEAAbrBkKf2gmGtG4aogZY4svIZCrOLLZlQzVHwz7WxJdTR8iEnvz/x Q/jReDroS5CBZWvzwLlhPIpsJAojx0oj0RvfJNsz3+6LN8q7x9u6+86B 85CYjTk3dcFOebgkF4fXr7/kkOX+ZY94Zk0Z1+pUC3eY4gkKcyME/Uxm O18PBTeB
+example.org.				      86400 IN RRSIG	DNSKEY 7 2 86400 20120301040838 20120131040838 19562 example.org. d0eLF8JqNHaGuBSX0ashU5c1O/wyWU43UUsKGrMQIoBDiJ588MWQOnas rwvW6vdkLNqRqCsP/B4epV/EtLL0tBsk5SHkTYbNo80gGrBufQ6YrWRr Ile8Z+h+MR4y9DybbjmuNKqaO4uQMg/X6+4HqRAKx1lmZMTcrcVeOwDM ZA4=
+example.org.				      0	IN NSEC3PARAM	1 0 10 AABBCCDD
+example.org.				      0	IN RRSIG	NSEC3PARAM 7 2 0 20120301040838 20120131040838 19562 example.org. Ggs5MiQDlXXt22Fz9DNg3Ujc0T6MBfumlRkd8/enBbJwLmqw2QXAzDEk pjUeGstCEHKzxJDJstavGoCpTDJgoV4Fd9szooMx69rzPrq9tdoM46jG xZHqw+Pv2fkRGC6aP7ZX1r3Qnpwpk47AQgATftbO4G6KcMcO8JoKE47x XLM=
+ns.example.org.				      86400 IN A	192.0.2.1
+ns.example.org.				      86400 IN RRSIG	A 7 3 86400 20120301040838 20120131040838 19562 example.org. dOH+Dxib8VcGnjLrKILsqDhS1wki6BWk1dZwpOGUGHyLWcLNW8ygWY2o r29jPhHtaFCNWpn46JebgnXDPRiQjaY3dQqL8rcf2QX1e3+Cicw1OSrs S0sUDE5DmMNEac+ZCUQ0quCStZTCldl05hlspV2RS92TpApnoOK0nXMp Uak=
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
+09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKO yfZc8wKRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVv cD3dFksPyiKHf/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3 CTM=
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD 09GM5T42SMIMT7R8DF6RTG80SFMS1NLU NS SOA RRSIG DNSKEY NSEC3PARAM
+RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD.example.org. 1200 IN RRSIG	NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org. j7d8GL4YqX035FBcPPsEcSWHjWcKdlQMHLL4TB67xVNFnl4SEFQCp4OO AtPap5tkKakwgWxoQVN9XjnqrBz+oQhofDkB3aTatAjIIkcwcnrm3AYQ rTI3E03ySiRwuCPKVmHOLUV2cG6O4xzcmP+MYZcvPTS8V3F5LlaU22i7 A3E=
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone b/src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone
new file mode 100644
index 0000000..e3afb74
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-out-of-zone.zone
@@ -0,0 +1,5 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 75 3600 300 3600000 3600
+a.example.com.	  		      3600 IN A	 	192.168.0.1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone b/src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone
new file mode 100644
index 0000000..ef5f887
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-rrsig-follows-nothing.zone
@@ -0,0 +1,5 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 68 3600 300 3600000 3600
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone b/src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone
new file mode 100644
index 0000000..1c780b1
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-rrsigs.zone
@@ -0,0 +1,8 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 74 3600 300 3600000 3600
+ns1.example.org.		      3600 IN A	 	192.168.0.1
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN AAAA 	::1
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone
new file mode 100644
index 0000000..0d03b0d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-dname.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard DNAME names
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+*.example.org. 3600 IN DNAME dname.example.com.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone
new file mode 100644
index 0000000..2933515
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-ns.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard NS names
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 78 3600 300 3600000 3600
+*.example.org.			      3600 IN NS	ns1.example.org.
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone
new file mode 100644
index 0000000..feee116
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org-wildcard-nsec3.zone
@@ -0,0 +1,4 @@
+;; test zone file with wildcard NS names
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 79 3600 300 3600000 3600
+*.example.org. 1200 IN NSEC3	1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG
diff --git a/src/lib/datasrc/tests/memory/testdata/example.org.zone b/src/lib/datasrc/tests/memory/testdata/example.org.zone
new file mode 100644
index 0000000..e499e0d
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/testdata/example.org.zone
@@ -0,0 +1,81 @@
+;; test zone file used for ZoneFinderContext tests.
+;; RRSIGs are (obviouslly) faked ones for testing.
+
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 67 3600 300 3600000 3600
+example.org.			      3600 IN NS	ns1.example.org.
+example.org.			      3600 IN NS	ns2.example.org.
+example.org.			      3600 IN MX	1 mx1.example.org.
+example.org.			      3600 IN MX	2 mx2.example.org.
+example.org.			      3600 IN MX	3 mx.a.example.org.
+
+ns1.example.org.		      3600 IN A		192.0.2.1
+ns1.example.org.		      3600 IN RRSIG	A 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKE
+ns1.example.org.		      3600 IN AAAA	2001:db8::1
+ns1.example.org.		      3600 IN RRSIG	AAAA 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
+ns2.example.org.		      3600 IN A		192.0.2.2
+ns2.example.org.		      3600 IN TXT	"text data"
+
+mx1.example.org.		      3600 IN A		192.0.2.10
+mx2.example.org.		      3600 IN AAAA	2001:db8::10
+
+;; delegation
+a.example.org.			      3600 IN NS	ns1.a.example.org.
+a.example.org.			      3600 IN NS	ns2.a.example.org.
+a.example.org.			      3600 IN NS	ns.example.com.
+
+ns1.a.example.org.		      3600 IN A		192.0.2.5
+ns2.a.example.org.		      3600 IN A		192.0.2.6
+ns2.a.example.org.		      3600 IN AAAA	2001:db8::6
+mx.a.example.org.		      3600 IN A		192.0.2.7
+
+;; delegation, one of its NS names is at zone cut.
+b.example.org.			      3600 IN NS	ns.b.example.org.
+b.example.org.			      3600 IN NS	b.example.org.
+b.example.org.			      3600 IN AAAA	2001:db8::8
+
+ns.b.example.org.		      3600 IN A		192.0.2.9
+
+;; The MX name is at a zone cut.  shouldn't be included in the
+;; additional section.
+mxatcut.example.org.		      3600 IN MX	1 b.example.org.
+
+;; delegation, one of its NS names is under a DNAME delegation point;
+;; another is at that point; and yet another is under DNAME below a
+;; zone cut.
+c.example.org. 	      	      3600 IN NS	ns.dname.example.org.
+c.example.org. 	      	      3600 IN NS	dname.example.org.
+c.example.org.      	      3600 IN NS	ns.deepdname.example.org.
+ns.dname.example.org.		      3600 IN A		192.0.2.11
+dname.example.org.		      3600 IN A		192.0.2.12
+ns.deepdname.example.org.	      3600 IN AAAA	2001:db8::9
+
+;; delegation, one of its NS name is at an empty non terminal.
+d.example.org. 	      	      3600 IN NS	ns.empty.example.org.
+d.example.org. 	      	      3600 IN NS	ns1.example.org.
+;; by adding these two we can create an empty RB node for
+;; ns.empty.example.org in the in-memory zone
+foo.ns.empty.example.org.     3600 IN A		192.0.2.13
+bar.ns.empty.example.org.     3600 IN A		192.0.2.14
+
+;; delegation; the NS name matches a wildcard (and there's no exact
+;; match).  One of the NS names matches an empty wildcard node, for
+;; which no additional record should be provided (or any other
+;; disruption should happen).
+e.example.org. 	      	      3600 IN NS	ns.wild.example.org.
+e.example.org. 	      	      3600 IN NS	ns.emptywild.example.org.
+e.example.org. 	      	      3600 IN NS	ns2.example.org.
+*.wild.example.org.	      3600 IN A		192.0.2.15
+a.*.emptywild.example.org.    3600 IN AAAA	2001:db8::2
+
+;; additional for an answer RRset (MX) as a result of wildcard
+;; expansion
+*.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
+
+;; CNAME
+alias.example.org. 3600 IN CNAME cname.example.org.
+
+;; DNAME
+dname.example.org. 3600 IN DNAME dname.example.com.
+
+;; DNAME under a NS (strange one)
+deepdname.c.example.org. 3600 IN DNAME deepdname.example.com.
diff --git a/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc b/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
new file mode 100644
index 0000000..02ad2bd
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/treenode_rrset_unittest.cc
@@ -0,0 +1,631 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/buffer.h>
+#include <util/memory_segment_local.h>
+
+#include <datasrc/memory/treenode_rrset.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <util/unittests/wiredata.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+using std::vector;
+using std::string;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::testutils;
+using isc::util::unittests::matchWireData;
+using isc::util::OutputBuffer;
+
+namespace {
+
+class TreeNodeRRsetTest : public ::testing::Test {
+protected:
+    TreeNodeRRsetTest() :
+        rrclass_(RRClass::IN()),
+        origin_name_("example.com"), www_name_("www.example.com"),
+        wildcard_name_("*.example.com"), match_name_("match.example.com"),
+        ns_rrset_(textToRRset("example.com. 3600 IN NS ns.example.com.")),
+        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1\n"
+                             "www.example.com. 3600 IN A 192.0.2.2")),
+        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA "
+                                "2001:db8::1\n")),
+        dname_rrset_(textToRRset("example.com. 3600 IN DNAME d.example.org.")),
+        a_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 20120715220826 "
+                                   "1234 example.com. FAKE")),
+        aaaa_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG AAAA 5 2"
+                                      " 3600 20120814220826 20120715220826 "
+                                      "1234 example.com. FAKE\n"
+                                      "www.example.com. 3600 IN RRSIG AAAA 5 2"
+                                      " 3600 20120814220826 20120715220826 "
+                                      "4321 example.com. FAKE\n")),
+        txt_rrsig_rrset_(textToRRset("www.example.com. 3600 IN RRSIG TXT 5 2"
+                                     " 3600 20120814220826 20120715220826 "
+                                     "1234 example.com. FAKE\n")),
+        wildmatch_rrset_(textToRRset(
+                             "match.example.com. 3600 IN A 192.0.2.1\n"
+                             "match.example.com. 3600 IN A 192.0.2.2")),
+        wildmatch_rrsig_rrset_(textToRRset(
+                                   "match.example.com. 3600 IN RRSIG "
+                                   "A 5 2 3600 20120814220826 20120715220826 "
+                                   "1234 example.com. FAKE")),
+        zone_data_(NULL), origin_node_(NULL), www_node_(NULL),
+        wildcard_node_(NULL), ns_rdataset_(NULL), dname_rdataset_(NULL),
+        a_rdataset_(NULL), aaaa_rdataset_(NULL), rrsig_only_rdataset_(NULL),
+        wildcard_rdataset_(NULL)
+    {}
+    void SetUp() {
+        // We create some common test data here in SetUp() so it will be
+        // as exception safe as possible.
+
+        zone_data_ = ZoneData::create(mem_sgmt_, origin_name_);
+
+        zone_data_->insertName(mem_sgmt_, origin_name_, &origin_node_);
+        ns_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, ns_rrset_,
+                                        ConstRRsetPtr());
+        origin_node_->setData(ns_rdataset_);
+        dname_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, dname_rrset_,
+                                           ConstRRsetPtr());
+        ns_rdataset_->next = dname_rdataset_;
+
+        zone_data_->insertName(mem_sgmt_, www_name_, &www_node_);
+        a_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                       a_rrsig_rrset_);
+        www_node_->setData(a_rdataset_);
+
+        aaaa_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_,
+                                          aaaa_rrsig_rrset_);
+        a_rdataset_->next = aaaa_rdataset_;
+
+        // A rare (half broken) case of RRSIG-only set
+        rrsig_only_rdataset_ = RdataSet::create(mem_sgmt_, encoder_,
+                                                ConstRRsetPtr(),
+                                                txt_rrsig_rrset_);
+        aaaa_rdataset_->next = rrsig_only_rdataset_;
+
+        zone_data_->insertName(mem_sgmt_, wildcard_name_, &wildcard_node_);
+        wildcard_rdataset_ = RdataSet::create(mem_sgmt_, encoder_, a_rrset_,
+                                              a_rrsig_rrset_);
+        wildcard_node_->setData(wildcard_rdataset_);
+    }
+    void TearDown() {
+        ZoneData::destroy(mem_sgmt_, zone_data_, rrclass_);
+        // detect any memory leak
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    const RRClass rrclass_;
+    const Name origin_name_, www_name_, wildcard_name_, match_name_;
+    isc::util::MemorySegmentLocal mem_sgmt_;
+    RdataEncoder encoder_;
+    MessageRenderer renderer_, renderer_expected_;
+    ConstRRsetPtr ns_rrset_, a_rrset_, aaaa_rrset_, dname_rrset_,
+        a_rrsig_rrset_, aaaa_rrsig_rrset_, txt_rrsig_rrset_,
+        wildmatch_rrset_, wildmatch_rrsig_rrset_;
+    ZoneData* zone_data_;
+    ZoneNode* origin_node_;
+    ZoneNode* www_node_;
+    ZoneNode* wildcard_node_;
+    RdataSet* ns_rdataset_;
+    RdataSet* dname_rdataset_;
+    RdataSet* a_rdataset_;
+    RdataSet* aaaa_rdataset_;
+    RdataSet* rrsig_only_rdataset_;
+    RdataSet* wildcard_rdataset_; // for wildcard (type doesn't matter much)
+};
+
+void
+compareRRSIGData(RdataIteratorPtr rit, const void* data, size_t data_len) {
+    ASSERT_FALSE(rit->isLast());
+
+    OutputBuffer buffer(0);
+    rit->getCurrent().toWire(buffer);
+    matchWireData(data, data_len, buffer.getData(), buffer.getLength());
+    rit->next();
+}
+
+// Check some trivial fields of a constructed TreeNodeRRset (passed as
+// AbstractRRset as we'd normally use it in polymorphic way).
+// Other complicated fields are checked through rendering tests.
+void
+checkBasicFields(const AbstractRRset& actual_rrset, const RdataSet* rdataset,
+                 const Name& expected_name,
+                 const RRClass& expected_class, const RRType& expected_type,
+                 const uint32_t expected_ttl,
+                 size_t expected_rdatacount, size_t expected_sigcount)
+{
+    EXPECT_EQ(expected_name, actual_rrset.getName());
+    EXPECT_EQ(expected_class, actual_rrset.getClass());
+    EXPECT_EQ(expected_type, actual_rrset.getType());
+    EXPECT_EQ(RRTTL(expected_ttl), actual_rrset.getTTL());
+    EXPECT_EQ(expected_rdatacount, actual_rrset.getRdataCount());
+    EXPECT_EQ(expected_sigcount, actual_rrset.getRRsigDataCount());
+
+    // getRRsig() should return non NULL iff the RRset is expected to be signed
+    if (expected_sigcount == 0) {
+        EXPECT_FALSE(actual_rrset.getRRsig());
+    } else {
+        ConstRRsetPtr actual_sigrrset = actual_rrset.getRRsig();
+        ASSERT_TRUE(actual_sigrrset);
+        EXPECT_EQ(expected_name, actual_sigrrset->getName());
+        EXPECT_EQ(expected_class, actual_sigrrset->getClass());
+        EXPECT_EQ(RRType::RRSIG(), actual_sigrrset->getType());
+        EXPECT_EQ(RRTTL(expected_ttl), actual_sigrrset->getTTL());
+        EXPECT_EQ(expected_sigcount, actual_sigrrset->getRdataCount());
+
+        // Compare each RRSIG RDATA
+        RdataIteratorPtr rit = actual_sigrrset->getRdataIterator();
+        RdataReader reader(expected_class, expected_type,
+                           rdataset->getDataBuf(), expected_rdatacount,
+                           expected_sigcount, &RdataReader::emptyNameAction,
+                           boost::bind(compareRRSIGData, rit, _1, _2));
+        while (reader.nextSig() != RdataReader::RRSET_BOUNDARY) {}
+        EXPECT_TRUE(rit->isLast()); // should check all RDATAs
+    }
+}
+
+// The following two are trivial wrapper to create a shared pointer
+// version of TreeNodeRRset object in order to work around dubious
+// behavior of some C++ compiler: they reject getting a const reference to
+// a temporary non-copyable object.
+boost::shared_ptr<TreeNodeRRset>
+createRRset(const RRClass& rrclass, const ZoneNode* node,
+            const RdataSet* rdataset, bool dnssec_ok)
+{
+    return (boost::shared_ptr<TreeNodeRRset>(
+                new TreeNodeRRset(rrclass, node, rdataset, dnssec_ok)));
+}
+
+boost::shared_ptr<TreeNodeRRset>
+createRRset(const Name& realname, const RRClass& rrclass, const ZoneNode* node,
+            const RdataSet* rdataset, bool dnssec_ok)
+{
+    return (boost::shared_ptr<TreeNodeRRset>(
+                new TreeNodeRRset(realname, rrclass, node, rdataset,
+                                  dnssec_ok)));
+}
+
+TEST_F(TreeNodeRRsetTest, create) {
+    // Constructed with RRSIG, and it should be visible.
+    checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, true),
+                     a_rdataset_, www_name_, rrclass_, RRType::A(), 3600, 2,
+                     1);
+    // Constructed with RRSIG, and it should be invisible.
+    checkBasicFields(*createRRset(rrclass_, www_node_, a_rdataset_, false),
+                     a_rdataset_, www_name_, rrclass_, RRType::A(), 3600, 2,
+                     0);
+    // Constructed without RRSIG, and it would be visible (but of course won't)
+    checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
+                     ns_rdataset_, origin_name_, rrclass_, RRType::NS(), 3600,
+                     1, 0);
+    // Constructed without RRSIG, and it should be visible
+    checkBasicFields(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
+                     ns_rdataset_, origin_name_, rrclass_, RRType::NS(), 3600,
+                     1, 0);
+    // RRSIG-only case (note the RRset's type is covered type)
+    checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                  true),
+                     rrsig_only_rdataset_, www_name_, rrclass_, RRType::TXT(),
+                     3600, 0, 1);
+    // RRSIG-only case (note the RRset's type is covered type), but it's
+    // invisible
+    checkBasicFields(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                  false),
+                     rrsig_only_rdataset_, www_name_, rrclass_, RRType::TXT(),
+                     3600, 0, 0);
+    // Wildcard substitution
+    checkBasicFields(*createRRset(match_name_, rrclass_,
+                                  wildcard_node_, wildcard_rdataset_,
+                                  true),
+                     wildcard_rdataset_, match_name_, rrclass_, RRType::A(),
+                     3600, 2, 1);
+}
+
+// The following two templated functions are helper to encapsulate the
+// concept truncation and handle MessageRenderer and OutputBuffer transparently
+// in templated test cases.
+template <typename OutputType>
+void
+setOutputLengthLimit(OutputType& output, size_t len_limit) {
+    output.setLengthLimit(len_limit);
+}
+template <>
+void
+setOutputLengthLimit<OutputBuffer>(OutputBuffer&, size_t) {
+}
+
+template <typename OutputType>
+bool
+isOutputTruncated(OutputType& output) {
+    return (output.isTruncated());
+}
+template <>
+bool
+isOutputTruncated<OutputBuffer>(OutputBuffer&) {
+    return (false);
+}
+
+// Templated so we so can support OutputBuffer version of toWire().
+// We use the above helper templated functions for some renderer only methods.
+// We test two sets of cases: normal rendering case and case when truncation
+// is expected.  The latter is effectively for MessageRenderer only.
+// If len_limit == 0, we consider it the normal case; otherwise it's for
+// truncation.  prepended_name isn't used for the truncation case.
+template <typename OutputType>
+void
+checkToWireResult(OutputType& expected_output, OutputType& actual_output,
+                  const AbstractRRset& actual_rrset,
+                  const Name& prepended_name,
+                  ConstRRsetPtr rrset, ConstRRsetPtr rrsig_rrset,
+                  bool dnssec_ok,
+                  size_t len_limit = 0,
+                  size_t expected_result = 0)
+{
+    expected_output.clear();
+    actual_output.clear();
+
+    if (len_limit == 0) {       // normal rendering
+        // Prepare "actual" rendered data.  We prepend a name to confirm the
+        // owner name should be compressed in both cases.
+        prepended_name.toWire(actual_output);
+        const size_t rdata_count = rrset ? rrset->getRdataCount() : 0;
+        const int expected_ret = (dnssec_ok && rrsig_rrset) ?
+            rdata_count + rrsig_rrset->getRdataCount() : rdata_count;
+        EXPECT_EQ(expected_ret, actual_rrset.toWire(actual_output));
+    } else {                    // truncation
+        setOutputLengthLimit(actual_output, len_limit);
+        EXPECT_EQ(expected_result, actual_rrset.toWire(actual_output));
+        EXPECT_TRUE(isOutputTruncated(actual_output)); // always true here
+    }
+
+    // Prepare "expected" data.
+    if (len_limit == 0) {       // normal rendering
+        prepended_name.toWire(expected_output);
+    } else {                    // truncation
+        setOutputLengthLimit(expected_output, len_limit);
+    }
+    if (rrset) {
+        rrset->toWire(expected_output);
+    }
+    if (!isOutputTruncated(expected_output) && dnssec_ok && rrsig_rrset) {
+        rrsig_rrset->toWire(expected_output);
+    }
+
+    // Compare the two.
+    matchWireData(expected_output.getData(), expected_output.getLength(),
+                  actual_output.getData(), actual_output.getLength());
+}
+
+TEST_F(TreeNodeRRsetTest, toWire) {
+    MessageRenderer expected_renderer, actual_renderer;
+    OutputBuffer expected_buffer(0), actual_buffer(0);
+
+    {
+        SCOPED_TRACE("with RRSIG, DNSSEC OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, a_rrset_, a_rrsig_rrset_, true);
+        // Currently the buffer version throws
+        EXPECT_THROW(
+            checkToWireResult(expected_buffer, actual_buffer, rrset,
+                              www_name_, a_rrset_, a_rrsig_rrset_, true),
+            isc::Unexpected);
+    }
+
+    {
+        SCOPED_TRACE("with RRSIG, DNSSEC not OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, a_rrset_, a_rrsig_rrset_, false);
+    }
+
+    {
+        SCOPED_TRACE("without RRSIG, DNSSEC OK");
+        const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_, true);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          origin_name_, ns_rrset_, ConstRRsetPtr(), true);
+    }
+
+    {
+        SCOPED_TRACE("without RRSIG, DNSSEC not OK");
+        const TreeNodeRRset rrset(rrclass_, origin_node_, ns_rdataset_,
+                                  false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          origin_name_, ns_rrset_, ConstRRsetPtr(), false);
+    }
+
+    {
+        // RDATA of DNAME DR shouldn't be compressed.  Prepending "example.org"
+        // will check that.
+        SCOPED_TRACE("uncompressed RDATA");
+        const TreeNodeRRset rrset(rrclass_, origin_node_, dname_rdataset_,
+                                  false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          Name("example.org"), dname_rrset_, ConstRRsetPtr(),
+                          false);
+    }
+
+    {
+        SCOPED_TRACE("wildcard with RRSIG");
+        checkToWireResult(expected_renderer, actual_renderer,
+                          *createRRset(match_name_, rrclass_, wildcard_node_,
+                                       wildcard_rdataset_, true),
+                          origin_name_, wildmatch_rrset_,
+                          wildmatch_rrsig_rrset_, true);
+    }
+
+    {
+        SCOPED_TRACE("wildcard without RRSIG");
+        checkToWireResult(expected_renderer, actual_renderer,
+                          *createRRset(match_name_, rrclass_, wildcard_node_,
+                                       wildcard_rdataset_, false),
+                          origin_name_, wildmatch_rrset_,
+                          wildmatch_rrsig_rrset_, false);
+    }
+
+    {
+        // Very unusual case: the set only contains RRSIG (already rare)
+        // and it's requested to be dumped to wire (can only happen in
+        // ANY or type-RRSIG queries, which are rare also).  But can still
+        // happen.
+        SCOPED_TRACE("RRSIG only, DNSSEC OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                  true);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,true);
+    }
+
+    {
+        // Similar to the previous case, but DNSSEC records aren't requested.
+        // In practice this case wouldn't happen, but API-wise possible, so
+        // we test it explicitly.
+        SCOPED_TRACE("RRSIG only, DNSSEC not OK");
+        const TreeNodeRRset rrset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                  false);
+        checkToWireResult(expected_renderer, actual_renderer, rrset,
+                          www_name_, ConstRRsetPtr(), txt_rrsig_rrset_,false);
+    }
+}
+
+TEST_F(TreeNodeRRsetTest, toWireTruncated) {
+    MessageRenderer expected_renderer, actual_renderer;
+    // dummy parameter to checkToWireResult (unused for the this test case)
+    const Name& name = Name::ROOT_NAME();
+
+    // Set the truncation limit to name len + 14 bytes of fixed data for A RR
+    // (type, class, TTL, rdlen, and 4-byte IPv4 address).  Then we can only
+    // render just one RR, without any garbage trailing data.
+    checkToWireResult(expected_renderer, actual_renderer,
+                      *createRRset(rrclass_, www_node_, a_rdataset_, true),
+                      name, a_rrset_, a_rrsig_rrset_, true,
+                      www_name_.getLength() + 14,
+                      1);   // 1 main RR, no RRSIG
+
+    // The first main RRs should fit in the renderer (the name will be
+    // fully compressed, so its size is 2 bytes), but the RRSIG doesn't.
+    checkToWireResult(expected_renderer, actual_renderer,
+                      *createRRset(rrclass_, www_node_, a_rdataset_, true),
+                      name, a_rrset_, a_rrsig_rrset_, true,
+                      www_name_.getLength() + 14 + 2 + 14,
+                      2);   // 2 main RR, no RRSIG
+
+    // This RRset has one main RR and two RRSIGs.  Rendering the second RRSIG
+    // causes truncation.
+    // First, compute the rendered length for the main RR and a single RRSIG.
+    // The length of the RRSIG should be the same if we "accidentally"
+    // rendered the RRSIG for the A RR (which only contains one RRSIG).
+    expected_renderer.clear();
+    aaaa_rrset_->toWire(expected_renderer);
+    a_rrsig_rrset_->toWire(expected_renderer);
+    const size_t limit_len = expected_renderer.getLength();
+    // Then perform the test
+    checkToWireResult(expected_renderer, actual_renderer,
+                      *createRRset(rrclass_, www_node_, aaaa_rdataset_, true),
+                      name, aaaa_rrset_, aaaa_rrsig_rrset_, true, limit_len,
+                      2);   // 1 main RR, 1 RRSIG
+
+    // RRSIG only case.  Render length limit being 1, so it won't fit,
+    // and will cause truncation.
+    checkToWireResult(expected_renderer, actual_renderer,
+                      *createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                   true),
+                      name, ConstRRsetPtr(), txt_rrsig_rrset_, true, 1,
+                      0);   // no RR
+}
+
+void
+checkRdataIterator(const vector<string>& expected, RdataIteratorPtr rit) {
+    for (vector<string>::const_iterator it = expected.begin();
+         it != expected.end();
+         ++it)
+    {
+        ASSERT_FALSE(rit->isLast());
+        EXPECT_EQ(*it, rit->getCurrent().toText());
+        rit->next();
+    }
+    // We should have reached the end of RDATA
+    EXPECT_TRUE(rit->isLast());
+
+    // move to the first RDATA again, and check the value.
+    rit->first();
+    if (!expected.empty()) {
+        EXPECT_EQ(expected[0], rit->getCurrent().toText());
+    } else {
+        EXPECT_TRUE(rit->isLast());
+    }
+}
+
+TEST_F(TreeNodeRRsetTest, getRdataIterator) {
+    // This RRset should have 2 A RDATAs
+    vector<string> expected;
+    expected.push_back("192.0.2.1");
+    expected.push_back("192.0.2.2");
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, www_node_, a_rdataset_, true).
+                       getRdataIterator());
+
+    // The iterator shouldn't work different with or without RRSIG
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, www_node_, a_rdataset_, false).
+                       getRdataIterator());
+
+    // This RRset should have 1 NS RDATA (containing name field)
+    expected.clear();
+    expected.push_back("ns.example.com.");
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, origin_node_, ns_rdataset_,
+                                     false).getRdataIterator());
+
+    // RRSIG only.  Iterator will be empty and shouldn't cause any disruption.
+    expected.clear();
+    checkRdataIterator(expected,
+                       TreeNodeRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                                     true).getRdataIterator());
+}
+
+void
+checkToText(const AbstractRRset& actual_rrset,
+            ConstRRsetPtr expected_rrset, ConstRRsetPtr expected_sig_rrset)
+{
+    const string actual_text = actual_rrset.toText();
+    const string expected_text =
+        (expected_rrset ? expected_rrset->toText() : "") +
+        (expected_sig_rrset ? expected_sig_rrset->toText() : "");
+    EXPECT_EQ(expected_text, actual_text);
+}
+
+TEST_F(TreeNodeRRsetTest, toText) {
+    // Constructed with RRSIG, and it should be visible.
+    checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, true),
+                a_rrset_, a_rrsig_rrset_);
+    // Constructed with RRSIG, and it should be invisible.
+    checkToText(*createRRset(rrclass_, www_node_, a_rdataset_, false),
+                a_rrset_, ConstRRsetPtr());
+    // Constructed without RRSIG, and it would be visible (but of course won't)
+    checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, true),
+                ns_rrset_, ConstRRsetPtr());
+    // Constructed without RRSIG, and it should be visible
+    checkToText(*createRRset(rrclass_, origin_node_, ns_rdataset_, false),
+                ns_rrset_, ConstRRsetPtr());
+    // Wildcard expanded name with RRSIG
+    checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
+                             wildcard_rdataset_, true),
+                wildmatch_rrset_, wildmatch_rrsig_rrset_);
+    // Wildcard expanded name without RRSIG
+    checkToText(*createRRset(match_name_, rrclass_, wildcard_node_,
+                             wildcard_rdataset_, false),
+                wildmatch_rrset_, ConstRRsetPtr());
+    // RRSIG case
+    checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                             true),
+                ConstRRsetPtr(), txt_rrsig_rrset_);
+    // Similar to the previous case, but completely empty.
+    checkToText(*createRRset(rrclass_, www_node_, rrsig_only_rdataset_,
+                             false),
+                ConstRRsetPtr(), ConstRRsetPtr());
+}
+
+TEST_F(TreeNodeRRsetTest, isSameKind) {
+    const TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+    // Same name (node), same type (rdataset) => same kind
+    EXPECT_TRUE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
+                                              a_rdataset_, true)));
+
+    // Same name (node), different type (rdataset) => not same kind
+    EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, www_node_,
+                                               aaaa_rdataset_, true)));
+
+    // Different name, different type => not same kind
+    EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
+                                               ns_rdataset_, true)));
+
+    // Different name, same type => not same kind.
+    // Note: this shouldn't happen in our in-memory data source implementation,
+    // but API doesn't prohibit it.
+    EXPECT_FALSE(rrset.isSameKind(*createRRset(rrclass_, origin_node_,
+                                               a_rdataset_, true)));
+
+    // Wildcard and expanded RRset
+    const TreeNodeRRset wildcard_rrset(rrclass_, wildcard_node_,
+                                       wildcard_rdataset_, true);
+    const TreeNodeRRset match_rrset(match_name_, rrclass_, wildcard_node_,
+                                    wildcard_rdataset_, true);
+    EXPECT_FALSE(wildcard_rrset.isSameKind(match_rrset));
+    EXPECT_FALSE(match_rrset.isSameKind(wildcard_rrset));
+
+    // Both are wildcard expanded, and have different names
+    const TreeNodeRRset match2_rrset(Name("match2.example.com"), rrclass_,
+                                     wildcard_node_, wildcard_rdataset_, true);
+    EXPECT_FALSE(match_rrset.isSameKind(match2_rrset));
+    EXPECT_FALSE(match2_rrset.isSameKind(match_rrset));
+
+    // Pathological case.  "badwild" is constructed as if expanded due to
+    // a wildcard, but has the same owner name of the wildcard itself.
+    // Technically, they should be considered of the same kind, but this
+    // implementation considers they are not.  But this case shouldn't happen
+    // as long as the RRsets are only constructed inside the in-memory
+    // zone finder implementation.
+    const TreeNodeRRset badwild_rrset(wildcard_name_, rrclass_, wildcard_node_,
+                                      wildcard_rdataset_, true);
+    EXPECT_FALSE(wildcard_rrset.isSameKind(badwild_rrset));
+    EXPECT_EQ(wildcard_rrset.toText(), badwild_rrset.toText());
+
+    // Pathological case:  Same name, same type, but different class.
+    // This case should be impossible because if the RRsets share the same
+    // tree node, they must belong to the same RR class.  This case is
+    // a caller's bug, and the isSameKind() implementation returns the
+    // "wrong" (= true) answer.
+    EXPECT_TRUE(rrset.isSameKind(*createRRset(RRClass::CH(), www_node_,
+                                              a_rdataset_, true)));
+
+    // Same kind of different RRset class
+    EXPECT_TRUE(rrset.isSameKind(*a_rrset_));
+
+    // Different kind of different RRset class
+    EXPECT_FALSE(rrset.isSameKind(*aaaa_rrset_));
+}
+
+TEST_F(TreeNodeRRsetTest, unexpectedMethods) {
+    // Note: buffer version of toWire() is checked in the toWire test.
+
+    TreeNodeRRset rrset(rrclass_, www_node_, a_rdataset_, true);
+
+    EXPECT_THROW(rrset.setTTL(RRTTL(0)), isc::Unexpected);
+    EXPECT_THROW(rrset.setName(Name("example")), isc::Unexpected);
+    EXPECT_THROW(rrset.addRdata(createRdata(RRType::A(), rrclass_, "0.0.0.0")),
+                 isc::Unexpected);
+    RdataPtr sig_rdata = createRdata(
+        RRType::RRSIG(), rrclass_,
+        "A 5 2 3600 20120814220826 20120715220826 5300 example.com. FAKE");
+    EXPECT_THROW(rrset.addRRsig(sig_rdata), isc::Unexpected);
+    EXPECT_THROW(rrset.addRRsig(*a_rrsig_rrset_), isc::Unexpected);
+    EXPECT_THROW(rrset.addRRsig(a_rrsig_rrset_), isc::Unexpected);
+    EXPECT_THROW(rrset.addRRsig(RRsetPtr()), isc::Unexpected);
+    EXPECT_THROW(rrset.removeRRsig(), isc::Unexpected);
+}
+}
diff --git a/src/lib/datasrc/tests/memory/zone_data_unittest.cc b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
new file mode 100644
index 0000000..d15fe8b
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_data_unittest.cc
@@ -0,0 +1,255 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "memory_segment_test.h"
+
+#include <dns/rdataclass.h>
+
+#include <exceptions/exceptions.h>
+
+#include <dns/name.h>
+#include <dns/labelsequence.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/memory/rdataset.h>
+#include <datasrc/memory/zone_data.h>
+
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc::memory;
+using namespace isc::datasrc::memory::test;
+using namespace isc::testutils;
+
+namespace {
+
+// With this single fixture we'll test both NSEC3Data and ZoneData
+class ZoneDataTest : public ::testing::Test {
+protected:
+    ZoneDataTest() :
+        nsec3_data_(NULL), param_rdata_("1 0 12 aabbccdd"),
+        param_rdata_nosalt_("1 1 10 -"),
+        param_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a')),
+        nsec3_rdata_("1 0 12 aabbccdd TDK23RP6 SOA"),
+        nsec3_rdata_nosalt_("1 1 10 - TDK23RP6 SOA"),
+        nsec3_rdata_largesalt_("2 0 5 " + std::string(255 * 2, 'a') +
+                               " TDK23RP6 SOA"),
+        zname_("example.com"),
+        zone_data_(ZoneData::create(mem_sgmt_, zname_)),
+        a_rrset_(textToRRset("www.example.com. 3600 IN A 192.0.2.1")),
+        aaaa_rrset_(textToRRset("www.example.com. 3600 IN AAAA 2001:db8::1")),
+        nsec3_rrset_(textToRRset("TDK23RP6.example.com. 3600 IN NSEC3 "
+                                 "1 0 12 aabbccdd TDK23RP6 SOA"))
+    {}
+    void TearDown() {
+        if (nsec3_data_ != NULL) {
+            NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+        }
+        if (zone_data_ != NULL) {
+            ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+        }
+        // detect any memory leak in the test memory segment
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    MemorySegmentTest mem_sgmt_;
+    NSEC3Data* nsec3_data_;
+    const generic::NSEC3PARAM param_rdata_, param_rdata_nosalt_,
+        param_rdata_largesalt_;
+    const generic::NSEC3 nsec3_rdata_, nsec3_rdata_nosalt_,
+        nsec3_rdata_largesalt_;
+    const Name zname_;
+    ZoneData* zone_data_;
+    const ConstRRsetPtr a_rrset_, aaaa_rrset_, nsec3_rrset_;
+    RdataEncoder encoder_;
+};
+
+// Shared by both test cases using NSEC3 and NSEC3PARAM Rdata
+template <typename RdataType>
+void
+checkNSEC3Data(MemorySegmentTest& mem_sgmt, const RdataType& expect_rdata) {
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt, expect_rdata);
+
+    // Internal tree should be created and empty.
+    EXPECT_EQ(0, nsec3_data->getNSEC3Tree().getNodeCount());
+
+    EXPECT_EQ(expect_rdata.getHashalg(), nsec3_data->hashalg);
+    EXPECT_EQ(expect_rdata.getFlags(), nsec3_data->flags);
+    EXPECT_EQ(expect_rdata.getIterations(), nsec3_data->iterations);
+    EXPECT_EQ(expect_rdata.getSalt().size(), nsec3_data->getSaltLen());
+    if (expect_rdata.getSalt().size() > 0) {
+        EXPECT_EQ(0, memcmp(&expect_rdata.getSalt()[0],
+                            nsec3_data->getSaltData(),
+                            expect_rdata.getSalt().size()));
+    }
+
+    NSEC3Data::destroy(mem_sgmt, nsec3_data, RRClass::IN());
+}
+
+void
+checkFindRdataSet(const ZoneTree& tree, const Name& name, RRType type,
+                  const RdataSet* expected_set)
+{
+    ZoneNode* node = NULL;
+    tree.find(name, &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_EQ(expected_set, RdataSet::find(node->getData(), type));
+}
+
+TEST_F(ZoneDataTest, createNSEC3Data) {
+    // Create an NSEC3Data object from various types of RDATA (of NSEC3PARAM
+    // and of NSEC3), check if the resulting parameters match.
+    checkNSEC3Data(mem_sgmt_, param_rdata_); // one 'usual' form of params
+    checkNSEC3Data(mem_sgmt_, param_rdata_nosalt_); // empty salt
+    checkNSEC3Data(mem_sgmt_, param_rdata_largesalt_); // max-len salt
+
+    // Same concepts of the tests, using NSEC3 RDATA.
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_);
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_nosalt_);
+    checkNSEC3Data(mem_sgmt_, nsec3_rdata_largesalt_);
+}
+
+TEST_F(ZoneDataTest, addNSEC3) {
+    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+
+    ZoneNode* node = NULL;
+    nsec3_data_->insertName(mem_sgmt_, nsec3_rrset_->getName(), &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+    RdataSet* rdataset_nsec3 =
+        RdataSet::create(mem_sgmt_, encoder_, nsec3_rrset_, ConstRRsetPtr());
+    node->setData(rdataset_nsec3);
+
+    // Confirm we can find the added ones from the zone data.
+    checkFindRdataSet(nsec3_data_->getNSEC3Tree(), nsec3_rrset_->getName(),
+                      RRType::NSEC3(), rdataset_nsec3);
+
+    // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getOriginNode) {
+    EXPECT_EQ(LabelSequence(zname_), zone_data_->getOriginNode()->getLabels());
+}
+
+TEST_F(ZoneDataTest, exceptionSafetyOnCreate) {
+    // Note: below, we use our knowledge of how memory allocation happens
+    // within the NSEC3Data, the zone data and the underlying domain tree
+    // implementation.  We'll emulate rare situations where allocate() fails
+    // with an exception, and confirm it doesn't cause any harsh disruption
+    // or leak.
+
+    // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+    // will fail due to bad_alloc.  It shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+
+    // allocate() will throw on the insertion of the origin node.
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+    // allocate() will throw on creating the zone data.
+    mem_sgmt_.setThrowCount(3);
+    EXPECT_THROW(ZoneData::create(mem_sgmt_, zname_), std::bad_alloc);
+
+    // These incomplete create() attempts shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneDataTest, addRdataSets) {
+    // Insert a name to the zone, and add a couple the data (RdataSet) objects
+    // to the corresponding node.
+
+    ZoneNode* node = NULL;
+    zone_data_->insertName(mem_sgmt_, a_rrset_->getName(), &node);
+    ASSERT_NE(static_cast<ZoneNode*>(NULL), node);
+    EXPECT_TRUE(node->isEmpty()); // initially it should be empty
+
+    RdataSet* rdataset_a =
+        RdataSet::create(mem_sgmt_, encoder_, a_rrset_, ConstRRsetPtr());
+    node->setData(rdataset_a);
+
+    RdataSet* rdataset_aaaa =
+        RdataSet::create(mem_sgmt_, encoder_, aaaa_rrset_, ConstRRsetPtr());
+    // make a linked list and replace the list head
+    rdataset_aaaa->next = rdataset_a;
+    node->setData(rdataset_aaaa);
+
+    // Confirm we can find the added ones from the zone data.
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::A(), rdataset_a);
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::AAAA(), rdataset_aaaa);
+    // There's no NS (or anything other than AAAA or A) RdataSet in the list
+    checkFindRdataSet(zone_data_->getZoneTree(), a_rrset_->getName(),
+                      RRType::NS(), NULL);
+
+    // TearDown() will confirm there's no leak on destroy
+}
+
+TEST_F(ZoneDataTest, getSetNSEC3Data) {
+    // Initially there's no NSEC3 data
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+    // isNSEC3Signed is true iff zone data has non NULL NSEC3 data
+    EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+    // Set a new one.  The set method should return NULL.  The get method
+    // should return the new one.
+    NSEC3Data* nsec3_data = NSEC3Data::create(mem_sgmt_, param_rdata_);
+    NSEC3Data* old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data);
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), old_nsec3_data);
+    EXPECT_EQ(nsec3_data, zone_data_->getNSEC3Data());
+    EXPECT_TRUE(zone_data_->isNSEC3Signed());
+
+    // Replace an existing one with a yet another one.
+    // We're responsible for destroying the old one.
+    NSEC3Data* nsec3_data2 = NSEC3Data::create(mem_sgmt_, nsec3_rdata_);
+    old_nsec3_data = zone_data_->setNSEC3Data(nsec3_data2);
+    EXPECT_EQ(nsec3_data, old_nsec3_data);
+    EXPECT_EQ(nsec3_data2, zone_data_->getNSEC3Data());
+    EXPECT_TRUE(zone_data_->isNSEC3Signed());
+    NSEC3Data::destroy(mem_sgmt_, old_nsec3_data, RRClass::IN());
+
+    // Setting NULL clears any existing one.
+    old_nsec3_data = zone_data_->setNSEC3Data(NULL);
+    EXPECT_EQ(nsec3_data2, old_nsec3_data);
+    EXPECT_EQ(static_cast<NSEC3Data*>(NULL), zone_data_->getNSEC3Data());
+    EXPECT_FALSE(zone_data_->isNSEC3Signed());
+
+    // Then set it again.  The zone data should destroy it on its own
+    // destruction.
+    zone_data_->setNSEC3Data(old_nsec3_data);
+}
+
+TEST_F(ZoneDataTest, isSigned) {
+    // By default it's considered unsigned
+    EXPECT_FALSE(zone_data_->isSigned());
+
+    // declare it's signed, the isSigned() says so too
+    zone_data_->setSigned(true);
+    EXPECT_TRUE(zone_data_->isSigned());
+
+    // change it to unsigned again
+    zone_data_->setSigned(false);
+    EXPECT_FALSE(zone_data_->isSigned());
+}
+}
diff --git a/src/lib/datasrc/tests/memory/zone_finder_unittest.cc b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
new file mode 100644
index 0000000..a536bf5
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_finder_unittest.cc
@@ -0,0 +1,1604 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include "memory_segment_test.h"
+
+// NOTE: this faked_nsec3 inclusion (and all related code below)
+// was ported during #2109 for the convenience of implementing #2218
+// In #2218 the NSEC3 test code in this file is expected to be finalized.
+// In #2219 the original is expected to be removed, and this file should
+// probably be moved here (and any leftover code not handled in #2218 should
+// be cleaned up)
+#include "../../tests/faked_nsec3.h"
+
+#include <datasrc/memory/zone_finder.h>
+#include <datasrc/memory/rdata_serialization.h>
+#include <datasrc/data_source.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <boost/foreach.hpp>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+using boost::shared_ptr;
+using namespace isc::datasrc::test;
+using namespace isc::datasrc::memory::test;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Commonly used result codes (Who should write the prefix all the time)
+using result::SUCCESS;
+using result::EXIST;
+
+/// \brief expensive rrset converter
+///
+/// converts any specialized rrset (which may not have implemented some
+/// methods for efficiency) into a 'full' RRsetPtr, for easy use in test
+/// checks.
+///
+/// Done very inefficiently through text representation, speed should not
+/// be a concern here.
+ConstRRsetPtr
+convertRRset(ConstRRsetPtr src) {
+    return (textToRRset(src->toText()));
+}
+
+/// \brief Test fixture for the InMemoryZoneFinder class
+class InMemoryZoneFinderTest : public ::testing::Test {
+    // A straightforward pair of textual RR(set) and a RRsetPtr variable
+    // to store the RRset.  Used to build test data below.
+    struct RRsetData {
+        const char* const text; // textual representation of an RRset
+        RRsetPtr* rrset;
+    };
+protected:
+    // The following sub tests are shared by multiple test cases, changing
+    // the zone's DNSSEC status (unsigned, NSEC-signed or NSEC3-signed).
+    // expected_flags is set to either RESULT_NSEC_SIGNED or
+    // RESULT_NSEC3_SIGNED when it's NSEC/NSEC3 signed respectively and
+    // find() is expected to set the corresponding flags.
+    // find_options should be set to FIND_DNSSEC for NSEC-signed case when
+    // NSEC is expected to be returned.
+    void findCheck(ZoneFinder::FindResultFlags expected_flags =
+                   ZoneFinder::RESULT_DEFAULT,
+                   ZoneFinder::FindOptions find_options =
+                   ZoneFinder::FIND_DEFAULT);
+    void emptyNodeCheck(ZoneFinder::FindResultFlags expected_flags =
+                        ZoneFinder::RESULT_DEFAULT);
+    void wildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                       ZoneFinder::RESULT_DEFAULT,
+                       ZoneFinder::FindOptions find_options =
+                       ZoneFinder::FIND_DEFAULT);
+    void doCancelWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                               ZoneFinder::RESULT_DEFAULT,
+                               ZoneFinder::FindOptions find_options =
+                               ZoneFinder::FIND_DEFAULT);
+    void anyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                          ZoneFinder::RESULT_DEFAULT);
+    void emptyWildcardCheck(ZoneFinder::FindResultFlags expected_flags =
+                            ZoneFinder::RESULT_DEFAULT);
+    void findNSECENTCheck(const Name& ent_name,
+                          ConstRRsetPtr expected_nsec,
+                          ZoneFinder::FindResultFlags expected_flags =
+                          ZoneFinder::RESULT_DEFAULT);
+
+public:
+    InMemoryZoneFinderTest() :
+        class_(RRClass::IN()),
+        origin_("example.org"),
+        zone_data_(ZoneData::create(mem_sgmt_, origin_)),
+        zone_finder_(*zone_data_, class_)
+    {
+        // Build test RRsets.  Below, we construct an RRset for
+        // each textual RR(s) of zone_data, and assign it to the corresponding
+        // rr_xxx.
+        // Note that this contains an out-of-zone RR, and due to the
+        // validation check of masterLoad() used below, we cannot add SOA.
+        const RRsetData zone_data[] = {
+            {"example.org. 300 IN NS ns.example.org.", &rr_ns_},
+            {"example.org. 300 IN A 192.0.2.1", &rr_a_},
+            {"ns.example.org. 300 IN A 192.0.2.2", &rr_ns_a_},
+            // This one will place rr_ns_a_ at a zone cut, making it a glue:
+            {"ns.example.org. 300 IN NS 192.0.2.2", &rr_ns_ns_},
+            {"ns.example.org. 300 IN AAAA 2001:db8::2", &rr_ns_aaaa_},
+            {"cname.example.org. 300 IN CNAME canonical.example.org",
+             &rr_cname_},
+            {"cname.example.org. 300 IN A 192.0.2.3", &rr_cname_a_},
+            {"dname.example.org. 300 IN DNAME target.example.org.",
+             &rr_dname_},
+            {"dname.example.org. 300 IN A 192.0.2.39", &rr_dname_a_},
+            {"dname.example.org. 300 IN NS ns.dname.example.org.",
+             &rr_dname_ns_},
+            {"example.org. 300 IN DNAME example.com.", &rr_dname_apex_},
+            {"child.example.org. 300 IN NS ns.child.example.org.",
+             &rr_child_ns_},
+            {"child.example.org. 300 IN DS 12345 5 1 DEADBEEF",
+             &rr_child_ds_},
+            {"ns.child.example.org. 300 IN A 192.0.2.153",
+             &rr_child_glue_},
+            {"grand.child.example.org. 300 IN NS ns.grand.child.example.org.",
+             &rr_grandchild_ns_},
+            {"ns.grand.child.example.org. 300 IN AAAA 2001:db8::253",
+             &rr_grandchild_glue_},
+            {"dname.child.example.org. 300 IN DNAME example.com.",
+             &rr_child_dname_},
+            {"example.com. 300 IN A 192.0.2.10", &rr_out_},
+            {"*.wild.example.org. 300 IN A 192.0.2.1", &rr_wild_},
+            {"*.cnamewild.example.org. 300 IN CNAME canonial.example.org.",
+             &rr_cnamewild_},
+            {"foo.wild.example.org. 300 IN A 192.0.2.3", &rr_under_wild_},
+            {"wild.*.foo.example.org. 300 IN A 192.0.2.1", &rr_emptywild_},
+            {"wild.*.foo.*.bar.example.org. 300 IN A 192.0.2.1",
+             &rr_nested_emptywild_},
+            {"*.nswild.example.org. 300 IN NS nswild.example.", &rr_nswild_},
+            {"*.dnamewild.example.org. 300 IN DNAME dnamewild.example.",
+             &rr_dnamewild_},
+            {"*.child.example.org. 300 IN A 192.0.2.1", &rr_child_wild_},
+            {"bar.foo.wild.example.org. 300 IN A 192.0.2.2", &rr_not_wild_},
+            {"baz.foo.wild.example.org. 300 IN A 192.0.2.3",
+             &rr_not_wild_another_},
+            {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
+             "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
+             &rr_nsec3_},
+            {"example.org. 300 IN NSEC wild.*.foo.example.org. "
+             "NS SOA RRSIG NSEC DNSKEY", &rr_nsec_},
+            // Together with the apex NSEC, these next NSECs make a complete
+            // chain in the case of the zone for the emptyNonterminal tests
+            // (We may want to clean up this generator code and/or masterLoad
+            // so that we can prepare conflicting datasets better)
+            {"wild.*.foo.example.org. 3600 IN NSEC ns.example.org. "
+             "A RRSIG NSEC", &rr_ent_nsec2_},
+            {"ns.example.org. 3600 IN NSEC foo.wild.example.org. A RRSIG NSEC",
+             &rr_ent_nsec3_},
+            {"foo.wild.example.org. 3600 IN NSEC example.org. A RRSIG NSEC",
+             &rr_ent_nsec4_},
+            // And these are NSECs used in different tests
+            {"ns.example.org. 300 IN NSEC *.nswild.example.org. A AAAA NSEC",
+             &rr_ns_nsec_},
+            {"*.wild.example.org. 300 IN NSEC foo.wild.example.org. A NSEC",
+             &rr_wild_nsec_},
+            {NULL, NULL}
+        };
+
+        for (unsigned int i = 0; zone_data[i].text != NULL; ++i) {
+            *zone_data[i].rrset = textToRRset(zone_data[i].text);
+        }
+
+    }
+
+    ~InMemoryZoneFinderTest() {
+        // Make sure we reset the hash creator to the default
+        setNSEC3HashCreator(NULL);
+        ZoneData::destroy(mem_sgmt_, zone_data_, RRClass::IN());
+    }
+
+    // NSEC3-specific call for 'loading' data
+    void addZoneDataNSEC3(const ConstRRsetPtr rrset) {
+        assert(rrset->getType() == RRType::NSEC3());
+
+        const generic::NSEC3& nsec3_rdata =
+             dynamic_cast<const generic::NSEC3&>(
+                  rrset->getRdataIterator()->getCurrent());
+
+        NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
+        if (nsec3_data == NULL) {
+             nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
+             zone_data_->setNSEC3Data(nsec3_data);
+        } else {
+             const size_t salt_len = nsec3_data->getSaltLen();
+             const uint8_t* salt_data = nsec3_data->getSaltData();
+             const vector<uint8_t>& salt_data_2 = nsec3_rdata.getSalt();
+
+             if ((nsec3_rdata.getHashalg() != nsec3_data->hashalg) ||
+                 (nsec3_rdata.getIterations() != nsec3_data->iterations) ||
+                 (salt_data_2.size() != salt_len)) {
+                  isc_throw(isc::Unexpected,
+                            "NSEC3 with inconsistent parameters: " <<
+                            rrset->toText());
+             }
+
+             if ((salt_len > 0) &&
+                 (std::memcmp(&salt_data_2[0], salt_data, salt_len) != 0)) {
+                  isc_throw(isc::Unexpected,
+                            "NSEC3 with inconsistent parameters: " <<
+                            rrset->toText());
+             }
+        }
+
+        ZoneNode* node;
+        nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
+
+        RdataSet* rdset = RdataSet::create(mem_sgmt_, encoder_,
+                                           rrset, ConstRRsetPtr());
+        RdataSet* old_rdset = node->setData(rdset);
+        if (old_rdset != NULL) {
+             RdataSet::destroy(mem_sgmt_, class_, old_rdset);
+        }
+        zone_data_->setSigned(true);
+    }
+
+    // simplified version of 'loading' data
+    void addZoneData(const ConstRRsetPtr rrset) {
+        ZoneNode* node = NULL;
+
+        if (rrset->getType() == RRType::NSEC3()) {
+            return (addZoneDataNSEC3(rrset));
+        } else if (rrset->getType() == RRType::NSEC()) {
+            zone_data_->setSigned(true);
+        }
+
+        zone_data_->insertName(mem_sgmt_, rrset->getName(), &node);
+
+        if (rrset->getType() == RRType::NS() &&
+            rrset->getName() != zone_data_->getOriginNode()->getName()) {
+            node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
+        } else if (rrset->getType() == RRType::DNAME()) {
+            node->setFlag(DomainTreeNode<RdataSet>::FLAG_CALLBACK);
+        }
+
+        RdataSet* next_rds = node->getData();
+        RdataSet* rdataset =
+            RdataSet::create(mem_sgmt_, encoder_, rrset, rrset->getRRsig());
+        rdataset->next = next_rds;
+        node->setData(rdataset);
+
+        // find wildcard nodes in name (go through all of them in case there
+        // is a nonterminal one)
+        // Note that this method is pretty much equal to the 'real' loader;
+        // but less efficient
+        Name name(rrset->getName());
+        while (name.getLabelCount() > 1) {
+            if (name.isWildcard()) {
+                ZoneNode* wnode = NULL;
+                // add Wild node
+                zone_data_->insertName(mem_sgmt_, name.split(1), &wnode);
+                wnode->setFlag(ZoneData::WILDCARD_NODE);
+                // add wildcard name itself too
+                zone_data_->insertName(mem_sgmt_, name, &wnode);
+            }
+            name = name.split(1);
+        }
+
+        // If we've added NSEC3PARAM at zone origin, set up NSEC3
+        // specific data or check consistency with already set up
+        // parameters.
+        if (rrset->getType() == RRType::NSEC3PARAM() &&
+            rrset->getName() == origin_) {
+            // We know rrset has exactly one RDATA
+            const generic::NSEC3PARAM& param =
+                dynamic_cast<const generic::NSEC3PARAM&>
+                 (rrset->getRdataIterator()->getCurrent());
+
+            NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
+            if (nsec3_data == NULL) {
+                nsec3_data = NSEC3Data::create(mem_sgmt_, param);
+                zone_data_->setNSEC3Data(nsec3_data);
+                zone_data_->setSigned(true);
+            } else {
+                size_t salt_len = nsec3_data->getSaltLen();
+                const uint8_t* salt_data = nsec3_data->getSaltData();
+                const vector<uint8_t>& salt_data_2 = param.getSalt();
+
+                if ((param.getHashalg() != nsec3_data->hashalg) ||
+                    (param.getIterations() != nsec3_data->iterations) ||
+                    (salt_data_2.size() != salt_len)) {
+                     isc_throw(isc::Unexpected,
+                               "NSEC3PARAM with inconsistent parameters: "
+                               << rrset->toText());
+                }
+
+                if ((salt_len > 0) &&
+                    (std::memcmp(&salt_data_2[0],
+                                 salt_data, salt_len) != 0)) {
+                     isc_throw(isc::Unexpected,
+                               "NSEC3PARAM with inconsistent parameters: "
+                               << rrset->toText());
+                }
+            }
+        }
+    }
+
+    // Some data to test with
+    const RRClass class_;
+    const Name origin_;
+    // The zone finder to torture by tests
+    MemorySegmentTest mem_sgmt_;
+    memory::ZoneData* zone_data_;
+    memory::InMemoryZoneFinder zone_finder_;
+    isc::datasrc::memory::RdataEncoder encoder_;
+
+    // Placeholder for storing RRsets to be checked with rrsetsCheck()
+    vector<ConstRRsetPtr> actual_rrsets_;
+
+    /*
+     * Some RRsets to put inside the zone.
+     */
+    RRsetPtr
+        // Out of zone RRset
+        rr_out_,
+        // NS of example.org
+        rr_ns_,
+        // A of ns.example.org
+        rr_ns_a_,
+        // AAAA of ns.example.org
+        rr_ns_aaaa_,
+        // A of example.org
+        rr_a_;
+    RRsetPtr rr_ns_ns_;         // used to make rr_ns_a_ a glue.
+    RRsetPtr rr_cname_;         // CNAME in example.org (RDATA will be added)
+    RRsetPtr rr_cname_a_; // for mixed CNAME + A case
+    RRsetPtr rr_dname_;         // DNAME in example.org (RDATA will be added)
+    RRsetPtr rr_dname_a_; // for mixed DNAME + A case
+    RRsetPtr rr_dname_ns_; // for mixed DNAME + NS case
+    RRsetPtr rr_dname_apex_; // for mixed DNAME + NS case in the apex
+    RRsetPtr rr_child_ns_; // NS of a child domain (for delegation)
+    RRsetPtr rr_child_ds_; // DS of a child domain (for delegation, auth data)
+    RRsetPtr rr_child_glue_; // glue RR of the child domain
+    RRsetPtr rr_grandchild_ns_; // NS below a zone cut (unusual)
+    RRsetPtr rr_grandchild_glue_; // glue RR below a deeper zone cut
+    RRsetPtr rr_child_dname_; // A DNAME under NS
+    RRsetPtr rr_wild_;        // Wildcard record
+    RRsetPtr rr_cnamewild_;     // CNAME at a wildcard
+    RRsetPtr rr_emptywild_;
+    RRsetPtr rr_nested_emptywild_;
+    RRsetPtr rr_nswild_, rr_dnamewild_;
+    RRsetPtr rr_child_wild_;
+    RRsetPtr rr_under_wild_;
+    RRsetPtr rr_not_wild_;
+    RRsetPtr rr_not_wild_another_;
+    RRsetPtr rr_nsec3_;
+    RRsetPtr rr_nsec_;
+    RRsetPtr rr_ent_nsec2_;
+    RRsetPtr rr_ent_nsec3_;
+    RRsetPtr rr_ent_nsec4_;
+    RRsetPtr rr_ns_nsec_;
+    RRsetPtr rr_wild_nsec_;
+
+    /**
+     * \brief Test one find query to the zone finder.
+     *
+     * Asks a query to the zone finder and checks it does not throw and returns
+     * expected results. It returns nothing, it just signals failures
+     * to GTEST.
+     *
+     * \param name The name to ask for.
+     * \param rrtype The RRType to ask of.
+     * \param result The expected code of the result.
+     * \param check_answer Should a check against equality of the answer be
+     *     done?
+     * \param answer The expected rrset, if any should be returned.
+     * \param expected_flags The expected result flags returned via find().
+     *     These can be tested using isWildcard() etc.
+     * \param zone_finder Check different InMemoryZoneFinder object than
+     *     zone_finder_ (if NULL, uses zone_finder_)
+     * \param check_wild_answer Checks that the answer has the same RRs, type
+     *     class and TTL as the eqxpected answer and that the name corresponds
+     *     to the one searched. It is meant for checking answers for wildcard
+     *     queries.
+     */
+    void findTest(const Name& name, const RRType& rrtype,
+                  ZoneFinder::Result result,
+                  bool check_answer = true,
+                  const ConstRRsetPtr& answer = ConstRRsetPtr(),
+                  ZoneFinder::FindResultFlags expected_flags =
+                  ZoneFinder::RESULT_DEFAULT,
+                  memory::InMemoryZoneFinder* zone_finder = NULL,
+                  ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT,
+                  bool check_wild_answer = false)
+    {
+        SCOPED_TRACE("findTest for " + name.toText() + "/" + rrtype.toText());
+
+        if (zone_finder == NULL) {
+            zone_finder = &zone_finder_;
+        }
+        const ConstRRsetPtr answer_sig = answer ? answer->getRRsig() :
+            RRsetPtr(); // note we use the same type as of retval of getRRsig()
+        // The whole block is inside, because we need to check the result and
+        // we can't assign to FindResult
+        EXPECT_NO_THROW({
+                ZoneFinderContextPtr find_result(zone_finder->find(
+                                                     name, rrtype, options));
+                // Check it returns correct answers
+                EXPECT_EQ(result, find_result->code);
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                          find_result->isWildcard());
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+                          != 0, find_result->isNSECSigned());
+                EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+                          != 0, find_result->isNSEC3Signed());
+                if (check_answer) {
+                    if (!answer) {
+                        ASSERT_FALSE(find_result->rrset);
+                    } else {
+                        ASSERT_TRUE(find_result->rrset);
+                        ConstRRsetPtr result_rrset(
+                            convertRRset(find_result->rrset));
+                        rrsetCheck(answer, result_rrset);
+                        if (answer_sig &&
+                            (options & ZoneFinder::FIND_DNSSEC) != 0) {
+                            ASSERT_TRUE(result_rrset->getRRsig());
+                            rrsetCheck(answer_sig, result_rrset->getRRsig());
+                        } else {
+                            EXPECT_FALSE(result_rrset->getRRsig());
+                        }
+                    }
+                } else if (check_wild_answer) {
+                    ASSERT_NE(ConstRRsetPtr(), answer) <<
+                        "Wrong test, don't check for wild names if you expect "
+                        "empty answer";
+                    ASSERT_NE(ConstRRsetPtr(), find_result->rrset) <<
+                        "No answer found";
+                    // Build the expected answer using the given name and
+                    // other parameter of the base wildcard RRset.
+                    RRsetPtr wildanswer(new RRset(name, answer->getClass(),
+                                                  answer->getType(),
+                                                  answer->getTTL()));
+                    RdataIteratorPtr expectedIt(answer->getRdataIterator());
+                    for (; !expectedIt->isLast(); expectedIt->next()) {
+                        wildanswer->addRdata(expectedIt->getCurrent());
+                    }
+
+                    ConstRRsetPtr result_rrset(
+                        convertRRset(find_result->rrset));
+                    rrsetCheck(wildanswer, result_rrset);
+
+                    // Same for the RRSIG, if any.
+                    if (answer_sig) {
+                        ASSERT_TRUE(result_rrset->getRRsig());
+
+                        RRsetPtr wildsig(new RRset(name,
+                                                   answer_sig->getClass(),
+                                                   RRType::RRSIG(),
+                                                   answer_sig->getTTL()));
+                        RdataIteratorPtr expectedIt(
+                            answer_sig->getRdataIterator());
+                        for (; !expectedIt->isLast(); expectedIt->next()) {
+                            wildsig->addRdata(expectedIt->getCurrent());
+                        }
+                        rrsetCheck(wildsig, result_rrset->getRRsig());
+                    }
+                }
+            });
+    }
+    /**
+     * \brief Calls the findAll on the finder and checks the result.
+     */
+    void findAllTest(const Name& name, ZoneFinder::Result result,
+                     const vector<ConstRRsetPtr>& expected_rrsets,
+                     ZoneFinder::FindResultFlags expected_flags =
+                     ZoneFinder::RESULT_DEFAULT,
+                     memory::InMemoryZoneFinder* finder = NULL,
+                     const ConstRRsetPtr &rrset_result = ConstRRsetPtr(),
+                     ZoneFinder::FindOptions options =
+                     ZoneFinder::FIND_DEFAULT)
+    {
+        if (finder == NULL) {
+            finder = &zone_finder_;
+        }
+        std::vector<ConstRRsetPtr> target;
+        ZoneFinderContextPtr find_result(finder->findAll(name, target,
+                                                         options));
+        EXPECT_EQ(result, find_result->code);
+        if (!rrset_result) {
+            EXPECT_FALSE(find_result->rrset);
+        } else {
+            ASSERT_TRUE(find_result->rrset);
+            rrsetCheck(rrset_result, convertRRset(find_result->rrset));
+        }
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                  find_result->isWildcard());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
+                  != 0, find_result->isNSECSigned());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
+                  != 0, find_result->isNSEC3Signed());
+        // Convert all rrsets to 'full' ones before checking
+        std::vector<ConstRRsetPtr> converted_rrsets;
+        BOOST_FOREACH(ConstRRsetPtr cur_rrset, target) {
+            converted_rrsets.push_back(convertRRset(cur_rrset));
+        }
+        rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                    converted_rrsets.begin(), converted_rrsets.end());
+    }
+};
+
+/**
+ * \brief Test InMemoryZoneFinder::InMemoryZoneFinder constructor.
+ *
+ * Takes the created zone finder and checks its properties they are the same
+ * as passed parameters.
+ */
+TEST_F(InMemoryZoneFinderTest, constructor) {
+    ASSERT_EQ(origin_, zone_finder_.getOrigin());
+
+    // Some unusual (abnormal case): if we add a super domain name of the
+    // zone somehow, the label of the origin node won't be absolute.
+    // getOrigin() should still be the correct one.
+    ZoneNode *node;
+    zone_data_->insertName(mem_sgmt_, Name("org"), &node);
+    ASSERT_EQ(origin_, zone_finder_.getOrigin());
+}
+
+TEST_F(InMemoryZoneFinderTest, findCNAME) {
+    // install CNAME RR
+    addZoneData(rr_cname_);
+
+    // Find A RR of the same.  Should match the CNAME
+    findTest(rr_cname_->getName(), RRType::NS(), ZoneFinder::CNAME, true,
+             rr_cname_);
+
+    // Find the CNAME itself.  Should result in normal SUCCESS
+    findTest(rr_cname_->getName(), RRType::CNAME(), ZoneFinder::SUCCESS, true,
+             rr_cname_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findCNAMEUnderZoneCut) {
+    // There's nothing special when we find a CNAME under a zone cut
+    // (with FIND_GLUE_OK).  The behavior is different from BIND 9,
+    // so we test this case explicitly.
+    addZoneData(rr_child_ns_);
+    ConstRRsetPtr rr_cname_under_cut_ = textToRRset(
+        "cname.child.example.org. 300 IN CNAME target.child.example.org.");
+    addZoneData(rr_cname_under_cut_);
+    findTest(Name("cname.child.example.org"), RRType::AAAA(),
+             ZoneFinder::CNAME, true, rr_cname_under_cut_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+// Search under a DNAME record. It should return the DNAME
+TEST_F(InMemoryZoneFinderTest, findBelowDNAME) {
+    EXPECT_NO_THROW(addZoneData(rr_dname_));
+    findTest(Name("below.dname.example.org"), RRType::A(), ZoneFinder::DNAME,
+             true, rr_dname_);
+}
+
+// Search at the domain with DNAME. It should act as DNAME isn't there, DNAME
+// influences only the data below (see RFC 2672, section 3)
+TEST_F(InMemoryZoneFinderTest, findAtDNAME) {
+    EXPECT_NO_THROW(addZoneData(rr_dname_));
+    EXPECT_NO_THROW(addZoneData(rr_dname_a_));
+
+    const Name dname_name(rr_dname_->getName());
+    findTest(dname_name, RRType::A(), ZoneFinder::SUCCESS, true, rr_dname_a_);
+    findTest(dname_name, RRType::DNAME(), ZoneFinder::SUCCESS, true,
+             rr_dname_);
+    findTest(dname_name, RRType::TXT(), ZoneFinder::NXRRSET, true);
+}
+
+// Try searching something that is both under NS and DNAME, without and with
+// GLUE_OK mode (it should stop at the NS and DNAME respectively).
+TEST_F(InMemoryZoneFinderTest, DNAMEUnderNS) {
+    addZoneData(rr_child_ns_);
+    addZoneData(rr_child_dname_);
+
+    Name lowName("below.dname.child.example.org.");
+
+    findTest(lowName, RRType::A(), ZoneFinder::DELEGATION, true, rr_child_ns_);
+    findTest(lowName, RRType::A(), ZoneFinder::DNAME, true, rr_child_dname_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+// Test adding child zones and zone cut handling
+TEST_F(InMemoryZoneFinderTest, delegationNS) {
+    // add in-zone data
+    EXPECT_NO_THROW(addZoneData(rr_ns_));
+
+    // install a zone cut
+    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+
+    // below the zone cut
+    findTest(Name("www.child.example.org"), RRType::A(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_);
+
+    // at the zone cut
+    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+    findTest(Name("child.example.org"), RRType::NS(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+
+    // finding NS for the apex (origin) node.  This must not be confused
+    // with delegation due to the existence of an NS RR.
+    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
+
+    // unusual case of "nested delegation": the highest cut should be used.
+    EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+    findTest(Name("www.grand.child.example.org"), RRType::A(),
+             // note: !rr_grandchild_ns_
+             ZoneFinder::DELEGATION, true, rr_child_ns_);
+}
+
+TEST_F(InMemoryZoneFinderTest, delegationWithDS) {
+    // Similar setup to the previous one, but with DS RR at the delegation
+    // point.
+    addZoneData(rr_ns_);
+    addZoneData(rr_child_ns_);
+    addZoneData(rr_child_ds_);
+
+    // Normal types of query should result in delegation, but DS query
+    // should be considered in-zone (but only exactly at the delegation point).
+    findTest(Name("child.example.org"), RRType::A(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+    findTest(Name("child.example.org"), RRType::DS(), ZoneFinder::SUCCESS,
+             true, rr_child_ds_);
+    findTest(Name("grand.child.example.org"), RRType::DS(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_);
+
+    // There's nothing special for DS query at the zone apex.  It would
+    // normally result in NXRRSET.
+    findTest(Name("example.org"), RRType::DS(), ZoneFinder::NXRRSET,
+             true, ConstRRsetPtr());
+}
+
+TEST_F(InMemoryZoneFinderTest, findAny) {
+    EXPECT_NO_THROW(addZoneData(rr_a_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_));
+    EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+
+    vector<ConstRRsetPtr> expected_sets;
+
+    // origin
+    expected_sets.push_back(rr_a_);
+    expected_sets.push_back(rr_ns_);
+    findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
+
+    // out zone name
+    EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
+                             vector<ConstRRsetPtr>()),
+                 OutOfZone);
+
+    expected_sets.clear();
+    expected_sets.push_back(rr_child_glue_);
+    findAllTest(rr_child_glue_->getName(), ZoneFinder::SUCCESS, expected_sets);
+
+    // add zone cut
+    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+
+    // zone cut
+    findAllTest(rr_child_ns_->getName(), ZoneFinder::DELEGATION,
+                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+                NULL, rr_child_ns_);
+
+    // glue for this zone cut
+    findAllTest(rr_child_glue_->getName(),ZoneFinder::DELEGATION,
+                vector<ConstRRsetPtr>(), ZoneFinder::RESULT_DEFAULT,
+                NULL, rr_child_ns_);
+}
+
+TEST_F(InMemoryZoneFinderTest, glue) {
+    // install zone data:
+    // a zone cut
+    EXPECT_NO_THROW(addZoneData(rr_child_ns_));
+    // glue for this cut
+    EXPECT_NO_THROW(addZoneData(rr_child_glue_));
+    // a nested zone cut (unusual)
+    EXPECT_NO_THROW(addZoneData(rr_grandchild_ns_));
+    // glue under the deeper zone cut
+    EXPECT_NO_THROW(addZoneData(rr_grandchild_glue_));
+    // glue 'at the' zone cut
+    EXPECT_NO_THROW(addZoneData(rr_ns_a_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_ns_));
+
+    // by default glue is hidden due to the zone cut
+    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::DELEGATION,
+             true, rr_child_ns_);
+
+
+    // If we do it in the "glue OK" mode, we should find the exact match.
+    findTest(rr_child_glue_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+             rr_child_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
+
+    // glue OK + NXRRSET case
+    findTest(rr_child_glue_->getName(), RRType::AAAA(), ZoneFinder::NXRRSET,
+             true, ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
+
+    // glue OK + NXDOMAIN case
+    findTest(Name("www.child.example.org"), RRType::A(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+
+    // nested cut case.  The glue should be found.
+    findTest(rr_grandchild_glue_->getName(), RRType::AAAA(),
+             ZoneFinder::SUCCESS,
+             true, rr_grandchild_glue_, ZoneFinder::RESULT_DEFAULT, NULL,
+             ZoneFinder::FIND_GLUE_OK);
+
+    // A non-existent name in nested cut.  This should result in delegation
+    // at the highest zone cut.
+    findTest(Name("www.grand.child.example.org"), RRType::TXT(),
+             ZoneFinder::DELEGATION, true, rr_child_ns_,
+             ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+
+    // Glue at a zone cut
+    findTest(Name("ns.example.org"), RRType::A(),
+             ZoneFinder::DELEGATION, true, rr_ns_ns_);
+    findTest(Name("ns.example.org"), RRType::A(), ZoneFinder::SUCCESS,
+             true, rr_ns_a_, ZoneFinder::RESULT_DEFAULT,
+             NULL, ZoneFinder::FIND_GLUE_OK);
+}
+
+/**
+ * \brief Test searching.
+ *
+ * Check it finds or does not find correctly and does not throw exceptions.
+ */
+void
+InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags,
+                                  ZoneFinder::FindOptions find_options)
+{
+    // Fill some data inside
+    // Now put all the data we have there. It should throw nothing
+    rr_a_->addRRsig(createRdata(RRType::RRSIG(), RRClass::IN(),
+                                "A 5 3 3600 20120814220826 20120715220826 "
+                                "1234 example.com. FAKE"));
+    EXPECT_NO_THROW(addZoneData(rr_ns_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_a_));
+    EXPECT_NO_THROW(addZoneData(rr_ns_aaaa_));
+    EXPECT_NO_THROW(addZoneData(rr_a_));
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+        zone_data_->setSigned(true);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+        zone_data_->setSigned(true);
+    }
+
+    // These two should be successful
+    findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
+    findTest(rr_ns_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+             rr_ns_a_);
+
+    // Similar test for a signed RRset.  We should see the RRSIG iff
+    // FIND_DNSSEC option is specified.
+    findTest(rr_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true, rr_a_);
+    findTest(rr_a_->getName(), RRType::A(), ZoneFinder::SUCCESS, true,
+             rr_a_, ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_DNSSEC);
+
+    // These domains don't exist. (and one is out of the zone).  In an
+    // NSEC-signed zone with DNSSEC records requested, it should return the
+    // covering NSEC for the query name (the actual NSEC in the test data may
+    // not really "cover" it, but for the purpose of this test it's okay).
+    ConstRRsetPtr expected_nsec; // by default it's NULL
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+        (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+        expected_nsec = rr_nsec_;
+    }
+
+    // There's no other name between this one and the origin, so when NSEC
+    // is to be returned it should be the origin NSEC.
+    findTest(Name("nothere.example.org"), RRType::A(),
+             ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+             NULL, find_options);
+
+    // The previous name in the zone is "ns.example.org", but it doesn't
+    // have an NSEC.  It should be skipped and the origin NSEC will be
+    // returned as the "closest NSEC".
+    findTest(Name("nxdomain.example.org"), RRType::A(),
+             ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+             NULL, find_options);
+    EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
+                 OutOfZone);
+
+    // These domain exist but don't have the provided RRType.  For the latter
+    // one we now add its NSEC (which was delayed so that it wouldn't break
+    // other cases above).
+    findTest(origin_, RRType::AAAA(), ZoneFinder::NXRRSET, true,
+             expected_nsec, expected_flags, NULL, find_options);
+
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_ns_nsec_);
+        zone_data_->setSigned(true);
+        if ((find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+            expected_nsec = rr_ns_nsec_;
+        }
+    }
+    findTest(rr_ns_a_->getName(), RRType::NS(), ZoneFinder::NXRRSET, true,
+             expected_nsec, expected_flags, NULL, find_options);
+}
+
+TEST_F(InMemoryZoneFinderTest, find) {
+    findCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
+    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3SignedWithDNSSEC) {
+    // For NSEC3-signed zones, specifying the DNSSEC option shouldn't change
+    // anything (the NSEC3_SIGNED flag is always set, and no records are
+    // returned for negative cases regardless).
+    findCheck(ZoneFinder::RESULT_NSEC3_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
+    // NSEC-signed zone, without requesting DNSSEC (no NSEC should be provided)
+    findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Generalized test for Empty Nonterminal (ENT) cases with NSEC
+void
+InMemoryZoneFinderTest::findNSECENTCheck(const Name& ent_name,
+    ConstRRsetPtr expected_nsec,
+    ZoneFinder::FindResultFlags expected_flags)
+{
+    addZoneData(rr_emptywild_);
+    addZoneData(rr_under_wild_);
+
+    // Sanity check: Should result in NXRRSET
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+    // Sanity check: No NSEC added yet
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags,
+             NULL, ZoneFinder::FIND_DNSSEC);
+
+    // Now add the NSEC rrs making it a 'complete' zone (in terms of NSEC,
+    // there are no sigs)
+    addZoneData(rr_nsec_);
+    addZoneData(rr_ent_nsec2_);
+    addZoneData(rr_ent_nsec3_);
+    addZoneData(rr_ent_nsec4_);
+    zone_data_->setSigned(true);
+
+    // Should result in NXRRSET, and RESULT_NSEC_SIGNED
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(),
+             expected_flags | ZoneFinder::RESULT_NSEC_SIGNED);
+
+    // And check for the NSEC if DNSSEC_OK
+    findTest(ent_name, RRType::A(), ZoneFinder::NXRRSET, true,
+             expected_nsec, expected_flags | ZoneFinder::RESULT_NSEC_SIGNED,
+             NULL, ZoneFinder::FIND_DNSSEC);
+}
+
+TEST_F(InMemoryZoneFinderTest,findNSECEmptyNonterminal) {
+    // Non-wildcard case
+    findNSECENTCheck(Name("wild.example.org"), rr_ent_nsec3_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalWildcard) {
+    // Wildcard case, above actual wildcard
+    findNSECENTCheck(Name("foo.example.org"), rr_nsec_);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECEmptyNonterminalAtWildcard) {
+    // Wildcard case, at actual wildcard
+    findNSECENTCheck(Name("bar.foo.example.org"), rr_nsec_,
+                     ZoneFinder::RESULT_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, findNSECSignedWithDNSSEC) {
+    // NSEC-signed zone, requesting DNSSEC (NSEC should be provided)
+    findCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::FIND_DNSSEC);
+}
+
+void
+InMemoryZoneFinderTest::emptyNodeCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
+    /*
+     * The backend ZoneTree for this test should look like as follows:
+     *          example.org
+     *               |
+     *              baz (empty; easy case)
+     *            /  |  \
+     *          bar  |  x.foo ('foo' part is empty; a bit trickier)
+     *              bbb
+     *             /
+     *           aaa
+     */
+
+    // Construct the test zone
+    const char* const names[] = {
+        "bar.example.org.", "x.foo.example.org.", "aaa.baz.example.org.",
+        "bbb.baz.example.org.", NULL};
+    for (int i = 0; names[i] != NULL; ++i) {
+        ConstRRsetPtr rrset = textToRRset(string(names[i]) +
+                                          " 300 IN A 192.0.2.1");
+        addZoneData(rrset);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+        zone_data_->setSigned(true);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+        zone_data_->setSigned(true);
+    }
+
+    // empty node matching, easy case: the node for 'baz' exists with
+    // no data.
+    findTest(Name("baz.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+
+    // empty node matching, a trickier case: the node for 'foo' is part of
+    // "x.foo", which should be considered an empty node.
+    findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET, true,
+             ConstRRsetPtr(), expected_flags);
+
+    // "org" is contained in "example.org", but it shouldn't be treated as
+    // NXRRSET because it's out of zone.
+    // Note: basically we don't expect such a query to be performed (the common
+    // operation is to identify the best matching zone first then perform
+    // search it), but we shouldn't be confused even in the unexpected case.
+    EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNode) {
+    emptyNodeCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
+    emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
+    emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+/*
+ * Test that puts a (simple) wildcard into the zone and checks we can
+ * correctly find the data.
+ */
+void
+InMemoryZoneFinderTest::wildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags,
+    ZoneFinder::FindOptions find_options)
+{
+    /*
+     *            example.org.
+     *                 |
+     *             [cname]wild (not *.wild, should have wild mark)
+     *                 |
+     *                 *
+     */
+
+    // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
+    // add RRSIGs to the records.
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 ||
+        (expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        // Convenience shortcut.  The RDATA is not really validatable, but
+        // it doesn't matter for our tests.
+        const char* const rrsig_common = "5 3 3600 "
+            "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+
+        find_options = find_options | ZoneFinder::FIND_DNSSEC;
+        rr_wild_->addRRsig(textToRRset("*.wild.example.org. 300 IN RRSIG A " +
+                                       string(rrsig_common)));
+        rr_cnamewild_->addRRsig(textToRRset("*.cnamewild.example.org. 300 IN "
+                                            "RRSIG CNAME " +
+                                            string(rrsig_common)));
+    }
+    addZoneData(rr_wild_);
+    addZoneData(rr_cnamewild_);
+    // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
+    // (the content of the NSEC3 shouldn't matter)
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+    }
+
+    // Search at the parent. The parent will not have the A, but it will
+    // be in the wildcard (so check the wildcard isn't matched at the parent)
+    {
+        SCOPED_TRACE("Search at parent");
+        if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+            findTest(Name("wild.example.org"), RRType::A(),
+                     ZoneFinder::NXRRSET, true, rr_nsec_, expected_flags,
+                     NULL, find_options);
+        } else {
+            findTest(Name("wild.example.org"), RRType::A(),
+                     ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+                     expected_flags, NULL, find_options);
+        }
+    }
+
+    // For the test setup of "NSEC-signed" zone, we might expect it will
+    // be returned with a negative result, either because wildcard match is
+    // disabled by the search option or because wildcard match is canceled
+    // per protocol.
+    ConstRRsetPtr expected_nsec; // by default it's NULL
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+        (find_options & ZoneFinder::FIND_DNSSEC) != 0) {
+        expected_nsec = rr_nsec_;
+    }
+    // Explicitly converting the following to const pointers; some compilers
+    // would complain about mixed use of const and non const in ?: below.
+    const ConstRRsetPtr rr_wild = rr_wild_;
+    const ConstRRsetPtr rr_cnamewild = rr_cnamewild_;
+
+    // Search the original name of wildcard
+    {
+        SCOPED_TRACE("Search directly at *");
+        findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
+                 true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
+                 find_options);
+    }
+
+    // Below some of the test cases will normally result in a wildcard match;
+    // if NO_WILDCARD is specified, it should result in NXDOMAIN instead,
+    // and, when available and requested, the covering NSEC will be returned.
+    // The following are shortcut parameters to unify these cases.
+    const bool wild_ok = ((find_options & ZoneFinder::NO_WILDCARD) == 0);
+    const ZoneFinder::FindResultFlags wild_expected_flags =
+        wild_ok ? (ZoneFinder::RESULT_WILDCARD | expected_flags) :
+        expected_flags;
+
+    // Search "created" name.
+    {
+        SCOPED_TRACE("Search at created child");
+        findTest(Name("a.wild.example.org"), RRType::A(),
+                 wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
+                 wild_ok ? rr_wild : expected_nsec,
+                 wild_expected_flags, NULL, find_options, wild_ok);
+    }
+
+    // Search name that has CNAME.
+    {
+        SCOPED_TRACE("Matching CNAME");
+        findTest(Name("a.cnamewild.example.org"), RRType::A(),
+                 wild_ok ? ZoneFinder::CNAME : ZoneFinder::NXDOMAIN, false,
+                 wild_ok ? rr_cnamewild : expected_nsec,
+                 wild_expected_flags, NULL, find_options, wild_ok);
+    }
+
+    // Search another created name, this time little bit lower
+    {
+        SCOPED_TRACE("Search at created grand-child");
+        findTest(Name("a.b.wild.example.org"), RRType::A(),
+                 wild_ok ? ZoneFinder::SUCCESS : ZoneFinder::NXDOMAIN, false,
+                 wild_ok ? rr_wild : expected_nsec,
+                 wild_expected_flags, NULL, find_options, wild_ok);
+    }
+
+    addZoneData(rr_under_wild_);
+    {
+        SCOPED_TRACE("Search under non-wildcard");
+        findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+                 ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+                 NULL, find_options);
+    }
+
+    // Wildcard match, but no data.  We add the additional NSEC at the wildcard
+    // at this point so that it wouldn't break other tests above.  Note also
+    // that in the NO_WILDCARD case the resulting NSEC is the same.  Ideally
+    // we could use a more tricky setup so we can distinguish these cases,
+    // but for this purpose it's not bad; what we'd like to test here is that
+    // wildcard substitution doesn't happen for either case, and the
+    // NO_WILDCARD effect itself can be checked by the result code (NXDOMAIN).
+    ConstRRsetPtr expected_wild_nsec; // by default it's NULL
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_wild_nsec_);
+        expected_wild_nsec = rr_wild_nsec_;
+    }
+    {
+        SCOPED_TRACE("Search at wildcard, no data");
+        findTest(Name("a.wild.example.org"), RRType::AAAA(),
+                 wild_ok ? ZoneFinder::NXRRSET : ZoneFinder::NXDOMAIN, true,
+                 wild_ok ? expected_wild_nsec : expected_wild_nsec,
+                 wild_expected_flags, NULL, find_options);
+    }
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcard) {
+    // Normal case
+    wildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithNSEC) {
+    // Wildcard is disabled.  In practice, this is used as part of query
+    // processing for an NSEC-signed zone, so we test that case specifically.
+    wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED, ZoneFinder::NO_WILDCARD);
+}
+
+TEST_F(InMemoryZoneFinderTest, wildcardDisabledWithoutNSEC) {
+    // Similar to the previous once, but check the behavior for a non signed
+    // zone just in case.
+    wildcardCheck(ZoneFinder::RESULT_DEFAULT, ZoneFinder::NO_WILDCARD);
+}
+
+/*
+ * Test that we don't match a wildcard if we get under delegation.
+ * By 4.3.3 of RFC1034:
+ * "Wildcard RRs do not apply:
+ *   - When the query is in another zone.  That is, delegation cancels
+ *     the wildcard defaults."
+ */
+TEST_F(InMemoryZoneFinderTest, delegatedWildcard) {
+    addZoneData(rr_child_wild_);
+    addZoneData(rr_child_ns_);
+
+    {
+        SCOPED_TRACE("Looking under delegation point");
+        findTest(Name("a.child.example.org"), RRType::A(),
+                 ZoneFinder::DELEGATION, true, rr_child_ns_);
+    }
+
+    {
+        SCOPED_TRACE("Looking under delegation point in GLUE_OK mode");
+        findTest(Name("a.child.example.org"), RRType::A(),
+                 ZoneFinder::DELEGATION, true, rr_child_ns_,
+                 ZoneFinder::RESULT_DEFAULT, NULL, ZoneFinder::FIND_GLUE_OK);
+    }
+}
+
+// Tests combination of wildcard and ANY.
+void
+InMemoryZoneFinderTest::anyWildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
+    addZoneData(rr_wild_);
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+    }
+
+    vector<ConstRRsetPtr> expected_sets;
+
+    // First try directly the name (normal match)
+    {
+        SCOPED_TRACE("Asking directly for *");
+        expected_sets.push_back(rr_wild_);
+        findAllTest(Name("*.wild.example.org"), ZoneFinder::SUCCESS,
+                    expected_sets);
+    }
+
+    // Then a wildcard match
+    {
+        SCOPED_TRACE("Asking in the wild way");
+        expected_sets.clear();
+        RRsetPtr expected(new RRset(Name("a.wild.example.org"),
+                                    rr_wild_->getClass(), rr_wild_->getType(),
+                                    rr_wild_->getTTL()));
+        expected->addRdata(rr_wild_->getRdataIterator()->getCurrent());
+        expected_sets.push_back(expected);
+        findAllTest(Name("a.wild.example.org"), ZoneFinder::SUCCESS,
+                    expected_sets,
+                    ZoneFinder::RESULT_WILDCARD | expected_flags);
+    }
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcard) {
+    anyWildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC3) {
+    anyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, anyWildcardNSEC) {
+    anyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Test there's nothing in the wildcard in the middle if we load
+// wild.*.foo.example.org.
+void
+InMemoryZoneFinderTest::emptyWildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags)
+{
+    /*
+     *            example.org.
+     *                foo
+     *                 *
+     *               wild
+     */
+    addZoneData(rr_emptywild_);
+    if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
+        addZoneData(rr_nsec3_);
+    }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        addZoneData(rr_nsec_);
+    }
+
+    {
+        SCOPED_TRACE("Asking for the original record under wildcard");
+        findTest(Name("wild.*.foo.example.org"), RRType::A(),
+                 ZoneFinder::SUCCESS, true, rr_emptywild_);
+    }
+
+    {
+        SCOPED_TRACE("Asking for A record");
+        findTest(Name("a.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(),
+                 ZoneFinder::RESULT_WILDCARD | expected_flags);
+        findTest(Name("*.foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(), expected_flags);
+        findTest(Name("foo.example.org"), RRType::A(), ZoneFinder::NXRRSET,
+                 true, ConstRRsetPtr(), expected_flags);
+    }
+
+    {
+        SCOPED_TRACE("Asking for ANY record");
+        findAllTest(Name("*.foo.example.org"), ZoneFinder::NXRRSET,
+                    vector<ConstRRsetPtr>(), expected_flags);
+
+        findAllTest(Name("a.foo.example.org"), ZoneFinder::NXRRSET,
+                    vector<ConstRRsetPtr>(),
+                    ZoneFinder::RESULT_WILDCARD | expected_flags);
+    }
+
+    {
+        SCOPED_TRACE("Asking on the non-terminal");
+        findTest(Name("wild.bar.foo.example.org"), RRType::A(),
+                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
+                 ZoneFinder::RESULT_WILDCARD | expected_flags);
+    }
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcard) {
+    emptyWildcardCheck();
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC3) {
+    emptyWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+}
+
+TEST_F(InMemoryZoneFinderTest, emptyWildcardNSEC) {
+    emptyWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
+// Same as emptyWildcard, but with multiple * in the path.
+TEST_F(InMemoryZoneFinderTest, nestedEmptyWildcard) {
+    addZoneData(rr_nested_emptywild_);
+
+    {
+        SCOPED_TRACE("Asking for the original record under wildcards");
+        findTest(Name("wild.*.foo.*.bar.example.org"), RRType::A(),
+            ZoneFinder::SUCCESS, true, rr_nested_emptywild_);
+    }
+
+    {
+        SCOPED_TRACE("Matching wildcard against empty nonterminal");
+
+        const char* names[] = {
+            "baz.foo.*.bar.example.org",
+            "baz.foo.baz.bar.example.org",
+            "*.foo.baz.bar.example.org",
+            NULL
+        };
+
+        for (const char** name = names; *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+            findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET, true,
+                     ConstRRsetPtr(), ZoneFinder::RESULT_WILDCARD);
+        }
+    }
+
+    // Domains to test
+    const char* names[] = {
+        "*.foo.*.bar.example.org",
+        "foo.*.bar.example.org",
+        "*.bar.example.org",
+        "bar.example.org",
+        NULL
+    };
+
+    {
+        SCOPED_TRACE("Asking directly for A on parent nodes");
+
+        for (const char** name = names; *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+            findTest(Name(*name), RRType::A(), ZoneFinder::NXRRSET);
+        }
+    }
+
+    {
+        SCOPED_TRACE("Asking for ANY on parent nodes");
+
+        for (const char** name = names; *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+
+            findAllTest(Name(*name), ZoneFinder::NXRRSET,
+                        vector<ConstRRsetPtr>());
+        }
+    }
+}
+
+// We run this part twice from the below test, in two slightly different
+// situations
+void
+InMemoryZoneFinderTest::doCancelWildcardCheck(
+    ZoneFinder::FindResultFlags expected_flags,
+    ZoneFinder::FindOptions find_options)
+{
+    // These should be canceled
+    {
+        SCOPED_TRACE("Canceled under foo.wild.example.org");
+
+        // For an NSEC-signed zone with DNSSEC requested, the covering NSEC
+        // should be returned.  The expected NSEC is actually just the only
+        // NSEC in the test data, but in this context it doesn't matter;
+        // it's sufficient just to check any NSEC is returned (or not).
+        ConstRRsetPtr expected_nsec; // by default it's NULL
+        if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0 &&
+            (find_options & ZoneFinder::FIND_DNSSEC)) {
+            expected_nsec = rr_nsec_;
+        }
+
+        findTest(Name("aaa.foo.wild.example.org"), RRType::A(),
+                 ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+                 NULL, find_options);
+        findTest(Name("zzz.foo.wild.example.org"), RRType::A(),
+                 ZoneFinder::NXDOMAIN, true, expected_nsec, expected_flags,
+                 NULL, find_options);
+    }
+
+    // This is existing, non-wildcard domain, shouldn't wildcard at all
+    {
+        SCOPED_TRACE("Existing domain under foo.wild.example.org");
+        findTest(Name("bar.foo.wild.example.org"), RRType::A(),
+                 ZoneFinder::SUCCESS, true, rr_not_wild_);
+    }
+
+    // These should be caught by the wildcard
+    {
+        SCOPED_TRACE("Neighbor wildcards to foo.wild.example.org");
+
+        const char* names[] = {
+            "aaa.bbb.wild.example.org",
+            "aaa.zzz.wild.example.org",
+            "zzz.wild.example.org",
+            NULL
+        };
+
+        for (const char** name = names; *name != NULL; ++ name) {
+            SCOPED_TRACE(string("Node ") + *name);
+
+            findTest(Name(*name), RRType::A(), ZoneFinder::SUCCESS, false,
+                     rr_wild_,
+                     ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+                     ZoneFinder::FIND_DEFAULT, true);
+        }
+    }
+
+    // This shouldn't be wildcarded, it's an existing domain
+    {
+        SCOPED_TRACE("The foo.wild.example.org itself");
+        findTest(Name("foo.wild.example.org"), RRType::A(),
+                 ZoneFinder::NXRRSET, true, ConstRRsetPtr(), expected_flags);
+    }
+}
+
+/*
+ * This tests that if there's a name between the wildcard domain and the
+ * searched one, it will not trigger wildcard, for example, if we have
+ * *.wild.example.org and bar.foo.wild.example.org, then we know
+ * foo.wild.example.org exists and is not wildcard. Therefore, search for
+ * aaa.foo.wild.example.org should return NXDOMAIN.
+ *
+ * Tests few cases "around" the canceled wildcard match, to see something that
+ * shouldn't be canceled isn't.
+ */
+TEST_F(InMemoryZoneFinderTest, cancelWildcard) {
+    addZoneData(rr_wild_);
+    addZoneData(rr_not_wild_);
+
+    {
+        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+        doCancelWildcardCheck();
+    }
+
+    // Try putting another one under foo.wild....
+    // The result should be the same but it will be done in another way in the
+    // code, because the foo.wild.example.org will exist in the tree.
+    addZoneData(rr_not_wild_another_);
+    {
+        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+        doCancelWildcardCheck();
+    }
+}
+
+// Same tests as cancelWildcard for NSEC3-signed zone
+TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC3) {
+    addZoneData(rr_wild_);
+    addZoneData(rr_not_wild_);
+    addZoneData(rr_nsec3_);
+
+    {
+        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+    }
+    addZoneData(rr_not_wild_another_);
+    {
+        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
+    }
+}
+
+// Same tests as cancelWildcard for NSEC-signed zone.  Check both cases with
+// or without FIND_DNSSEC option.  NSEC should be returned only when the option
+// is given.
+TEST_F(InMemoryZoneFinderTest, cancelWildcardNSEC) {
+    addZoneData(rr_wild_);
+    addZoneData(rr_not_wild_);
+    addZoneData(rr_nsec_);
+
+    {
+        SCOPED_TRACE("Runnig with single entry under foo.wild.example.org");
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
+                              ZoneFinder::FIND_DNSSEC);
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+    }
+    addZoneData(rr_not_wild_another_);
+    {
+        SCOPED_TRACE("Runnig with two entries under foo.wild.example.org");
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED,
+                              ZoneFinder::FIND_DNSSEC);
+        doCancelWildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+    }
+}
+
+
+TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
+    // Set up the faked hash calculator.
+    const TestNSEC3HashCreator creator;
+    setNSEC3HashCreator(&creator);
+
+    // If the zone has nothing about NSEC3 (neither NSEC3 or NSEC3PARAM),
+    // findNSEC3() should be rejected.
+    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+                 DataSourceError);
+
+    // Only having NSEC3PARAM isn't enough
+    addZoneData(textToRRset("example.org. 300 IN NSEC3PARAM "
+                            "1 0 12 aabbccdd"));
+    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+                 DataSourceError);
+
+    // Unless NSEC3 for apex is added the result in the recursive mode
+    // is guaranteed.
+    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+        string(nsec3_common);
+    addZoneData(textToRRset(ns1_nsec3_text));
+    EXPECT_THROW(zone_finder_.findNSEC3(Name("www.example.org"), true),
+                 DataSourceError);
+}
+
+/// \brief NSEC3 specific tests fixture for the InMemoryZoneFinder class
+class InMemoryZoneFinderNSEC3Test : public InMemoryZoneFinderTest {
+public:
+    InMemoryZoneFinderNSEC3Test() {
+        // Set up the faked hash calculator.
+        setNSEC3HashCreator(&creator_);
+
+        // Add a few NSEC3 records:
+        // apex (example.org.): hash=0P..
+        // ns1.example.org:     hash=2T..
+        // w.example.org:       hash=01..
+        // zzz.example.org:     hash=R5..
+        const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(apex_nsec3_text));
+        const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(ns1_nsec3_text));
+        const string w_nsec3_text = string(w_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(w_nsec3_text));
+        const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+            string(nsec3_common);
+        addZoneData(textToRRset(zzz_nsec3_text));
+    }
+
+private:
+    const TestNSEC3HashCreator creator_;
+};
+
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3) {
+    performNSEC3Test(zone_finder_);
+}
+
+struct TestData {
+     // String for the name passed to findNSEC3() (concatenated with
+     // "example.org.")
+     const char* const name;
+     // Should recursive findNSEC3() be performed?
+     const bool recursive;
+     // The following are members of the FindNSEC3Result returned by
+     // findNSEC3(). The proofs are given as char*, which are converted
+     // to Name objects and checked against getName() on the returned
+     // ConstRRsetPtr. If any of these is NULL, then it's expected that
+     // ConstRRsetPtr() will be returned.
+     const bool matched;
+     const uint8_t closest_labels;
+     const char* const closest_proof;
+     const char* const next_proof;
+};
+
+const TestData nsec3_data[] = {
+     // ==== These are non-recursive tests.
+     {"n0", false, false, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+     {"n1", false,  true, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+     {"n2", false, false, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+     {"n3", false,  true, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+     {"n4", false, false, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+     {"n5", false,  true, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+     {"n6", false, false, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+     {"n7", false,  true, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+     {"n8", false, false, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+
+     // ==== These are recursive tests.
+     {"n0",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"},
+     {"n1",  true,  true, 4, "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H", NULL},
+     {"n2",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H"},
+     {"n3",  true,  true, 4, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM", NULL},
+     {"n4",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM"},
+     {"n5",  true,  true, 4, "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR", NULL},
+     {"n6",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR"},
+     {"n7",  true,  true, 4, "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN", NULL},
+     {"n8",  true,  true, 3, "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM",
+         "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN"}
+};
+
+const size_t data_count(sizeof(nsec3_data) / sizeof(*nsec3_data));
+
+TEST_F(InMemoryZoneFinderNSEC3Test, findNSEC3Walk) {
+    // This test basically uses nsec3_data[] declared above along with
+    // the fake hash setup to walk the NSEC3 tree. The names and fake
+    // hash calculation is specially setup so that the tree search
+    // terminates at specific locations in the tree. We findNSEC3() on
+    // each of the nsec3_data[], which is setup such that the hash
+    // results in the search terminating on either side of each node of
+    // the NSEC3 tree. This way, we check what result is returned in
+    // every search termination case in the NSEC3 tree.
+
+    const Name origin("example.org");
+    for (size_t i = 0; i < data_count; ++i) {
+        const Name name = Name(nsec3_data[i].name).concatenate(origin);
+
+        SCOPED_TRACE(name.toText() + (nsec3_data[i].recursive ?
+                                      ", recursive" :
+                                      ", non-recursive"));
+
+        const ZoneFinder::FindNSEC3Result result =
+            zone_finder_.findNSEC3(name, nsec3_data[i].recursive);
+
+        EXPECT_EQ(nsec3_data[i].matched, result.matched);
+        EXPECT_EQ(nsec3_data[i].closest_labels, result.closest_labels);
+
+        if (nsec3_data[i].closest_proof != NULL) {
+            ASSERT_TRUE(result.closest_proof);
+            EXPECT_EQ(Name(nsec3_data[i].closest_proof).concatenate(origin),
+                      result.closest_proof->getName());
+        } else {
+            EXPECT_FALSE(result.closest_proof);
+        }
+
+        if (nsec3_data[i].next_proof != NULL) {
+            ASSERT_TRUE(result.next_proof);
+            EXPECT_EQ(Name(nsec3_data[i].next_proof).concatenate(origin),
+                      result.next_proof->getName());
+        } else {
+            EXPECT_FALSE(result.next_proof);
+        }
+    }
+}
+}
diff --git a/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
new file mode 100644
index 0000000..6bdd737
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_table_segment_unittest.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <datasrc/memory/zone_table_segment.h>
+#include <gtest/gtest.h>
+
+using namespace isc::datasrc::memory;
+using namespace isc::data;
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+class ZoneTableSegmentTest : public ::testing::Test {
+protected:
+    ZoneTableSegmentTest() :
+        config_(Element::fromJSON("{}")),
+        segment_(ZoneTableSegment::create((*config_.get())))
+    {}
+
+    ~ZoneTableSegmentTest() {
+        if (segment_ != NULL) {
+            ZoneTableSegment::destroy(segment_);
+        }
+    }
+
+    void TearDown() {
+        // Catch any future leaks here.
+        const MemorySegment& mem_sgmt = segment_->getMemorySegment();
+        EXPECT_TRUE(mem_sgmt.allMemoryDeallocated());
+
+        ZoneTableSegment::destroy(segment_);
+        segment_ = NULL;
+    }
+
+    const ElementPtr config_;
+    ZoneTableSegment* segment_;
+};
+
+
+TEST_F(ZoneTableSegmentTest, create) {
+    // By default, a local zone table segment is created.
+    EXPECT_NE(static_cast<void*>(NULL), segment_);
+}
+
+// Helper function to check const and non-const methods.
+template <typename TS, typename TH, typename TT>
+void
+testGetHeader(ZoneTableSegment* segment) {
+    TH& header = static_cast<TS*>(segment)->getHeader();
+
+    // The zone table is unset.
+    TT* table = header.getTable();
+    EXPECT_EQ(static_cast<void*>(NULL), table);
+}
+
+TEST_F(ZoneTableSegmentTest, getHeader) {
+    // non-const version.
+    testGetHeader<ZoneTableSegment, ZoneTableHeader, ZoneTable>(segment_);
+
+    // const version.
+    testGetHeader<const ZoneTableSegment, const ZoneTableHeader,
+                  const ZoneTable>(segment_);
+}
+
+TEST_F(ZoneTableSegmentTest, getMemorySegment) {
+    // This doesn't do anything fun except test the API.
+    MemorySegment& mem_sgmt = segment_->getMemorySegment();
+    EXPECT_TRUE(mem_sgmt.allMemoryDeallocated());
+}
+
+} // anonymous namespace
diff --git a/src/lib/datasrc/tests/memory/zone_table_unittest.cc b/src/lib/datasrc/tests/memory/zone_table_unittest.cc
new file mode 100644
index 0000000..359df49
--- /dev/null
+++ b/src/lib/datasrc/tests/memory/zone_table_unittest.cc
@@ -0,0 +1,150 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/memory_segment_local.h>
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/result.h>
+#include <datasrc/memory/zone_data.h>
+#include <datasrc/memory/zone_table.h>
+
+#include <gtest/gtest.h>
+
+#include <new>                  // for bad_alloc
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::datasrc::memory;
+
+namespace {
+// Memory segment specified for tests.  It normally behaves like a "local"
+// memory segment.  If "throw count" is set to non 0 via setThrowCount(),
+// it continues the normal behavior up to the specified number of calls to
+// allocate(), and throws an exception at the next call.
+class TestMemorySegment : public isc::util::MemorySegmentLocal {
+public:
+    TestMemorySegment() : throw_count_(0) {}
+    virtual void* allocate(size_t size) {
+        if (throw_count_ > 0) {
+            if (--throw_count_ == 0) {
+                throw std::bad_alloc();
+            }
+        }
+        return (isc::util::MemorySegmentLocal::allocate(size));
+    }
+    void setThrowCount(size_t count) { throw_count_ = count; }
+
+private:
+    size_t throw_count_;
+};
+
+class ZoneTableTest : public ::testing::Test {
+protected:
+    ZoneTableTest() : zclass_(RRClass::IN()),
+                      zname1(Name("example.com")),
+                      zname2(Name("example.net")),
+                      zname3(Name("example")),
+                      zone_table(ZoneTable::create(mem_sgmt_, zclass_))
+    {}
+    ~ZoneTableTest() {
+        if (zone_table != NULL) {
+            ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
+        }
+    }
+    void TearDown() {
+        ZoneTable::destroy(mem_sgmt_, zone_table, zclass_);
+        zone_table = NULL;
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated()); // catch any leak here.
+    }
+    const RRClass zclass_;
+    const Name zname1, zname2, zname3;
+    TestMemorySegment mem_sgmt_;
+    ZoneTable* zone_table;
+};
+
+TEST_F(ZoneTableTest, create) {
+    // Test about creating a zone table.  Normal case covers through other
+    // tests.  We only check exception safety by letting the test memory
+    // segment throw.
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(ZoneTable::create(mem_sgmt_, zclass_), std::bad_alloc);
+    // This shouldn't cause memory leak (that would be caught in TearDown()).
+}
+
+TEST_F(ZoneTableTest, addZone) {
+    // Normal successful case.
+    const ZoneTable::AddResult result1 =
+        zone_table->addZone(mem_sgmt_, zclass_, zname1);
+    EXPECT_EQ(result::SUCCESS, result1.code);
+
+    // Duplicate add doesn't replace the existing data.
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+                                                 zname1).code);
+    EXPECT_EQ(result1.zone_data,
+              zone_table->addZone(mem_sgmt_, zclass_, zname1).zone_data);
+    // names are compared in a case insensitive manner.
+    EXPECT_EQ(result::EXIST, zone_table->addZone(mem_sgmt_, zclass_,
+                                                 Name("EXAMPLE.COM")).code);
+    // Add some more different ones.  Should just succeed.
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+
+    // Have the memory segment throw an exception in extending the internal
+    // tree.  It still shouldn't cause memory leak (which would be detected
+    // in TearDown()).
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(zone_table->addZone(mem_sgmt_, zclass_, Name("example.org")),
+                 std::bad_alloc);
+}
+
+TEST_F(ZoneTableTest, findZone) {
+    const ZoneTable::AddResult add_result1 =
+        zone_table->addZone(mem_sgmt_, zclass_, zname1);
+    EXPECT_EQ(result::SUCCESS, add_result1.code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname2).code);
+    EXPECT_EQ(result::SUCCESS,
+              zone_table->addZone(mem_sgmt_, zclass_, zname3).code);
+
+    const ZoneTable::FindResult find_result1 =
+        zone_table->findZone(Name("example.com"));
+    EXPECT_EQ(result::SUCCESS, find_result1.code);
+    EXPECT_EQ(add_result1.zone_data, find_result1.zone_data);
+
+    EXPECT_EQ(result::NOTFOUND,
+              zone_table->findZone(Name("example.org")).code);
+    EXPECT_EQ(static_cast<ZoneData*>(NULL),
+              zone_table->findZone(Name("example.org")).zone_data);
+
+    // there's no exact match.  the result should be the longest match,
+    // and the code should be PARTIALMATCH.
+    EXPECT_EQ(result::PARTIALMATCH,
+              zone_table->findZone(Name("www.example.com")).code);
+    EXPECT_EQ(add_result1.zone_data,
+              zone_table->findZone(Name("www.example.com")).zone_data);
+
+    // make sure the partial match is indeed the longest match by adding
+    // a zone with a shorter origin and query again.
+    EXPECT_EQ(result::SUCCESS, zone_table->addZone(mem_sgmt_, zclass_,
+                                                   Name("com")).code);
+    EXPECT_EQ(add_result1.zone_data,
+              zone_table->findZone(Name("www.example.com")).zone_data);
+}
+}
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index fcdca16..958c9e1 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -293,72 +293,6 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
     ++it;
 }
 
-// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
-// object.
-//
-// For apex (example.org)
-const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
-// For ns1.example.org
-const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
-// For w.example.org
-const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-// For x.y.w.example.org (lower-cased)
-const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
-// For zzz.example.org.
-const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
-
-// A simple faked NSEC3 hash calculator with a dedicated creator for it.
-//
-// This is used in some NSEC3-related tests below.
-class TestNSEC3HashCreator : public NSEC3HashCreator {
-    class TestNSEC3Hash : public NSEC3Hash {
-    private:
-        typedef map<Name, string> NSEC3HashMap;
-        typedef NSEC3HashMap::value_type NSEC3HashPair;
-        NSEC3HashMap map_;
-    public:
-        TestNSEC3Hash() {
-            // Build pre-defined hash
-            map_[Name("example.org")] = apex_hash;
-            map_[Name("www.example.org")] = "2S9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("xxx.example.org")] = "Q09MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("yyy.example.org")] = "0A9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
-            map_[Name("x.y.w.example.org")] =
-                "2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S";
-            map_[Name("y.w.example.org")] = "K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
-            map_[Name("w.example.org")] = w_hash;
-            map_[Name("zzz.example.org")] = zzz_hash;
-            map_[Name("smallest.example.org")] =
-                "00000000000000000000000000000000";
-            map_[Name("largest.example.org")] =
-                "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
-        }
-        virtual string calculate(const Name& name) const {
-            const NSEC3HashMap::const_iterator found = map_.find(name);
-            if (found != map_.end()) {
-                return (found->second);
-            }
-            isc_throw(isc::Unexpected, "unexpected name for NSEC3 test: "
-                      << name);
-        }
-        virtual bool match(const generic::NSEC3PARAM&) const {
-            return (true);
-        }
-        virtual bool match(const generic::NSEC3&) const {
-            return (true);
-        }
-    };
-
-public:
-    virtual NSEC3Hash* create(const generic::NSEC3PARAM&) const {
-        return (new TestNSEC3Hash);
-    }
-    virtual NSEC3Hash* create(const generic::NSEC3&) const {
-        return (new TestNSEC3Hash);
-    }
-};
-
 /// \brief Test fixture for the InMemoryZoneFinder class
 class InMemoryZoneFinderTest : public ::testing::Test {
     // A straightforward pair of textual RR(set) and a RRsetPtr variable
diff --git a/src/lib/datasrc/tests/query_unittest.cc b/src/lib/datasrc/tests/query_unittest.cc
deleted file mode 100644
index 7a20b86..0000000
--- a/src/lib/datasrc/tests/query_unittest.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2010  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 <gtest/gtest.h>
-
-#include <util/buffer.h>
-#include <dns/message.h>
-#include <dns/name.h>
-#include <dns/opcode.h>
-#include <dns/rrtype.h>
-#include <dns/rrclass.h>
-
-#include <datasrc/query.h>
-
-#include <dns/tests/unittest_util.h>
-
-using isc::UnitTestUtil;
-using namespace isc::dns;
-using namespace isc::datasrc;
-
-namespace {
-
-void
-createQuery(Message& m, const Name& qname, const RRClass& qclass,
-            const RRType& qtype)
-{
-    m.setOpcode(Opcode::QUERY());
-    m.setHeaderFlag(Message::HEADERFLAG_RD);
-    m.addQuestion(Question(qname, qclass, qtype));
-}
-
-QueryTaskPtr
-createTask(Message& m, const Name& name, const RRType& rrtype0, HotCache& c) {
-    RRType rrtype(rrtype0);
-    Query q(m, c, true);
-    return (QueryTaskPtr(new QueryTask(q, name, rrtype,
-                                       QueryTask::SIMPLE_QUERY)));
-}
-
-// Check the QueryTask created using a temporary RRType object will remain
-// valid.
-TEST(QueryTest, constructWithTemporary) {
-    HotCache cache;
-
-    Message m1(Message::RENDER);
-    createQuery(m1, Name("www.wild.example.com"), RRClass::IN(), RRType::A()); 
-    QueryTaskPtr task_a = createTask(m1, Name("www.wild.example.com"),
-                                        RRType::A(), cache);
-    EXPECT_EQ(RRType::A(), task_a->qtype);
-
-    Message m2(Message::RENDER);
-    createQuery(m2, Name("www.wild.example.com"), RRClass::IN(),
-                RRType::AAAA());
-    QueryTaskPtr task_aaaa = createTask(m2, Name("www.wild.example.com"),
-                                        RRType::AAAA(), cache);
-    EXPECT_EQ(RRType::AAAA(), task_aaaa->qtype);
-
-}
-
-}
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 383fdcb..0fe28d8 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -669,16 +669,16 @@ TEST_F(SQLite3Create, creationtest) {
 TEST_F(SQLite3Create, emptytest) {
     ASSERT_FALSE(isReadable(SQLITE_NEW_DBFILE));
 
-    // open one manualle
+    // open one manually
     sqlite3* db;
     ASSERT_EQ(SQLITE_OK, sqlite3_open(SQLITE_NEW_DBFILE, &db));
 
-    // empty, but not locked, so creating it now should work
+    // empty, but not locked, so creating another accessor should work
     SQLite3Accessor accessor2(SQLITE_NEW_DBFILE, "IN");
 
     sqlite3_close(db);
 
-    // should work now that we closed it
+    // should still work now that we closed it
     SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN");
 }
 
@@ -696,8 +696,10 @@ TEST_F(SQLite3Create, lockedtest) {
 
     sqlite3_exec(db, "ROLLBACK TRANSACTION", NULL, NULL, NULL);
 
-    // should work now that we closed it
+    // should work now that the transaction has been rolled back
     SQLite3Accessor accessor3(SQLITE_NEW_DBFILE, "IN");
+
+    ASSERT_EQ(SQLITE_OK, sqlite3_close(db));
 }
 
 TEST_F(SQLite3AccessorTest, clone) {
diff --git a/src/lib/datasrc/tests/sqlite3_unittest.cc b/src/lib/datasrc/tests/sqlite3_unittest.cc
deleted file mode 100644
index ac1211b..0000000
--- a/src/lib/datasrc/tests/sqlite3_unittest.cc
+++ /dev/null
@@ -1,950 +0,0 @@
-// Copyright (C) 2010  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 <stdint.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#include <sqlite3.h>
-#include <gtest/gtest.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-#include <datasrc/data_source.h>
-#include <datasrc/sqlite3_datasrc.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-using namespace isc::data;
-
-namespace {
-ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/test.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_EXAMPLE2 = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/example2.com.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_EXAMPLE_ROOT = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/test-root.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_BROKENDB = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/brokendb.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_MEMORY = Element::fromJSON(
-    "{ \"database_file\": \":memory:\"}");
-ConstElementPtr SQLITE_DBFILE_NEWSCHEMA = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/newschema.sqlite3\"}");
-ConstElementPtr SQLITE_DBFILE_OLDSCHEMA = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/oldschema.sqlite3\"}");
-
-// The following file must be non existent and must be non"creatable";
-// the sqlite3 library will try to create a new DB file if it doesn't exist,
-// so to test a failure case the create operation should also fail.
-// The "nodir", a non existent directory, is inserted for this purpose.
-ConstElementPtr SQLITE_DBFILE_NOTEXIST = Element::fromJSON(
-    "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}");
-
-const string sigdata_common(" 20100322084538 20100220084538 "
-                            "33495 example.com. FAKEFAKEFAKEFAKE");
-const string dnskey1_data(" AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4q"
-                          "NGV7WcTD0WEiuV7IjXgHE36fCmS9QsUxSSOV"
-                          "o1I/FMxI2PJVqTYHkXFBS7AzLGsQYMU7UjBZ"
-                          "SotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g0qcbW"
-                          "YF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx");
-const string dnskey2_data(" AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJ"
-                          "tbvzg62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV"
-                          "4HQZJStJaZ+fHU5AwVNT+bBZdtV+NujSikhd"
-                          "THb4FYLg2b3Cx9NyJvAVukHp/91HnWuG4T36"
-                          "CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/i"
-                          "DGd8j6bqiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq"
-                          "23TaOrVTjB7d1a/h31ODfiHAxFHrkY3t3D5J"
-                          "R9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86Acv"
-                          "RyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGf"
-                          "oIK/aKwENrsjcKZZj660b1M=");
-const string nsec3_signature("gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK"
-                             "mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L"
-                             "is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02"
-                             "xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o"
-                             "8gHSY5vYTtothcZQa4BMKhmGQEk=");
-
-const Name zone_name("example.com");
-const Name nomatch_name("example.org");
-const Name child_name("sql1.example.com");
-const Name www_name("www.example.com");
-const Name www_upper_name("WWW.EXAMPLE.COM");
-
-typedef enum {
-    NORMAL,
-    EXACT,
-    ADDRESS,
-    REFERRAL
-} FindMode;
-
-class Sqlite3DataSourceTest : public ::testing::Test {
-protected:
-    Sqlite3DataSourceTest() : rrclass(RRClass::IN()),
-                              rrclass_notmatch(RRClass::CH()),
-                              rrtype(RRType::A()), rrttl(RRTTL(3600)),
-                              find_flags(0), rrset_matched(0), rrset_count(0)
-    {
-        data_source.init(SQLITE_DBFILE_EXAMPLE);
-
-        common_a_data.push_back("192.0.2.1");
-        common_sig_data.push_back("A 5 3 3600" + sigdata_common);
-        common_aaaa_data.push_back("2001:db8::1234");
-        common_aaaa_sig_data.push_back("AAAA 5 3 3600" + sigdata_common);
-
-        www_nsec_data.push_back("example.com. A RRSIG NSEC");
-        www_nsec_sig_data.push_back("NSEC 5 3 7200" + sigdata_common);
-
-        mix_a_data.push_back("192.0.2.1");
-        mix_a_data.push_back("192.0.2.2");
-        mix_aaaa_data.push_back("2001:db8::1");
-        mix_aaaa_data.push_back("2001:db8::2");
-
-        apex_soa_data.push_back("master.example.com. admin.example.com. "
-                                "1234 3600 1800 2419200 7200");
-        apex_soa_sig_data.push_back("SOA 5 2 3600" + sigdata_common);
-        apex_ns_data.push_back("dns01.example.com.");
-        apex_ns_data.push_back("dns02.example.com.");
-        apex_ns_data.push_back("dns03.example.com.");
-        apex_ns_sig_data.push_back("NS 5 2 3600" + sigdata_common);
-        apex_mx_data.push_back("10 mail.example.com.");
-        apex_mx_data.push_back("20 mail.subzone.example.com.");
-        apex_mx_sig_data.push_back("MX 5 2 3600" + sigdata_common);
-        apex_nsec_data.push_back("cname-ext.example.com. "
-                                 "NS SOA MX RRSIG NSEC DNSKEY");
-        apex_nsec_sig_data.push_back("NSEC 5 2 7200" + sigdata_common);
-        apex_dnskey_data.push_back("256 3 5" + dnskey1_data);
-        apex_dnskey_data.push_back("257 3 5" + dnskey2_data);
-        // this one is special (using different key):
-        apex_dnskey_sig_data.push_back("DNSKEY 5 2 3600 20100322084538 "
-                                       "20100220084538 4456 example.com. "
-                                       "FAKEFAKEFAKEFAKE");
-        apex_dnskey_sig_data.push_back("DNSKEY 5 2 3600" + sigdata_common);
-
-        wild_a_data.push_back("192.0.2.255");
-        dname_data.push_back("sql1.example.com.");
-        dname_sig_data.push_back("DNAME 5 3 3600" + sigdata_common);
-        cname_data.push_back("cnametest.example.org.");
-        cname_sig_data.push_back("CNAME 5 3 3600" + sigdata_common);
-        cname_nsec_data.push_back("mail.example.com. CNAME RRSIG NSEC");
-        cname_nsec_sig_data.push_back("NSEC 5 3 7200" + sigdata_common);
-        delegation_ns_data.push_back("ns1.subzone.example.com.");
-        delegation_ns_data.push_back("ns2.subzone.example.com.");
-        delegation_ds_data.push_back("40633 5 1 "
-                                     "3E56C0EA92CF529E005A4B62979533350492 "
-                                     "F105");
-        delegation_ds_data.push_back("40633 5 2 "
-                                     "AA8D4BD330C68BFB4D785894DDCF6B689CE9"
-                                     "873C4A3801F57A5AA3FE17925B8C");
-        delegation_ds_sig_data.push_back("DS 5 3 3600" + sigdata_common);
-        child_ds_data.push_back("33313 5 1 "
-                                "FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8DE");
-        child_ds_data.push_back("33313 5 2 "
-                                "0B99B7006F496D135B01AB17EDB469B4BE9E"
-                                "1973884DEA757BC4E3015A8C3AB3");
-        child_ds_sig_data.push_back("DS 5 3 3600" + sigdata_common);
-        delegation_nsec_data.push_back("*.wild.example.com. NS DS RRSIG NSEC");
-        delegation_nsec_sig_data.push_back("NSEC 5 3 7200" + sigdata_common);
-        child_a_data.push_back("192.0.2.100");
-        child_sig_data.push_back("A 5 4 3600 20100322084536 "
-                                 "20100220084536 12447 sql1.example.com. "
-                                 "FAKEFAKEFAKEFAKE");
-        nsec3_data.push_back("1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J");
-        nsec3_sig_data.push_back("NSEC3 5 4 7200 20100410172647 "
-                                 "20100311172647 63192 sql2.example.com. "
-                                 + nsec3_signature);
-    }
-    Sqlite3DataSrc data_source;
-    // we allow these to be modified in the test
-    RRClass rrclass;
-    RRClass rrclass_notmatch;
-    RRType rrtype;
-    RRTTL rrttl;
-    RRsetList result_sets;
-    uint32_t find_flags;
-    unsigned rrset_matched;
-    unsigned rrset_count;
-
-    vector<RRType> types;
-    vector<RRTTL> ttls;
-    vector<const vector<string>* > answers;
-    vector<const vector<string>* > signatures;
-
-    vector<RRType> expected_types;
-    vector<string> common_a_data;
-    vector<string> common_sig_data;
-    vector<string> common_aaaa_data;
-    vector<string> common_aaaa_sig_data;
-    vector<string> www_nsec_data;
-    vector<string> www_nsec_sig_data;
-    vector<string> mix_a_data;
-    vector<string> mix_aaaa_data;
-    vector<string> apex_soa_data;
-    vector<string> apex_soa_sig_data;
-    vector<string> apex_ns_data;
-    vector<string> apex_ns_sig_data;
-    vector<string> apex_mx_data;
-    vector<string> apex_mx_sig_data;
-    vector<string> apex_nsec_data;
-    vector<string> apex_nsec_sig_data;
-    vector<string> apex_dnskey_data;
-    vector<string> apex_dnskey_sig_data;
-    vector<string> wild_a_data;
-    vector<string> dname_data;
-    vector<string> dname_sig_data;
-    vector<string> cname_data;
-    vector<string> cname_sig_data;
-    vector<string> cname_nsec_data;
-    vector<string> cname_nsec_sig_data;
-    vector<string> delegation_ns_data;
-    vector<string> delegation_ns_sig_data;
-    vector<string> delegation_ds_data;
-    vector<string> delegation_ds_sig_data;
-    vector<string> child_ds_data;
-    vector<string> child_ds_sig_data;
-    vector<string> delegation_nsec_data;
-    vector<string> delegation_nsec_sig_data;
-    vector<string> child_a_data;
-    vector<string> child_sig_data;
-    vector<string> nsec3_data;
-    vector<string> nsec3_sig_data;
-
-    void findReferralRRsetCommon(const Name& qname, const RRClass& qclass);
-    void findAddressRRsetCommon(const RRClass& qclass);
-};
-
-void
-checkRRset(RRsetPtr rrset, const Name& expected_name,
-           const RRClass& expected_class, const RRType& expected_type,
-           const RRTTL& expected_rrttl, const vector<string>& expected_data,
-           const vector<string>* expected_sig_data)
-{
-    EXPECT_EQ(expected_name, rrset->getName());
-    EXPECT_EQ(expected_class, rrset->getClass());
-    EXPECT_EQ(expected_type, rrset->getType());
-    EXPECT_EQ(expected_rrttl, rrset->getTTL());
-
-    RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
-    vector<string>::const_iterator data_it = expected_data.begin();
-    for (; data_it != expected_data.end(); ++data_it) {
-        EXPECT_FALSE(rdata_iterator->isLast());
-        if (rdata_iterator->isLast()) {
-            // buggy case, should stop here
-            break;
-        }
-
-        // We use text-based comparison so that we can easily identify which
-        // data causes the error.  RDATA::compare() is the most strict
-        // comparison method, but in this case text-based comparison should
-        // be okay because we generate the text data from Rdata objects
-        // rather than hand-write the expected text.
-        EXPECT_EQ(createRdata(expected_type, expected_class,
-                              *data_it)->toText(),
-                  rdata_iterator->getCurrent().toText());
-        rdata_iterator->next();
-    }
-
-    if (expected_sig_data != NULL) {
-        RRsetPtr sig_rrset = rrset->getRRsig();
-        EXPECT_FALSE(sig_rrset == NULL);
-        if (sig_rrset != NULL) { // check this to avoid possible bug.
-            // Note: we assume the TTL for RRSIG is the same as that of the
-            // RRSIG target.
-            checkRRset(sig_rrset, expected_name, expected_class,
-                       RRType::RRSIG(), expected_rrttl, *expected_sig_data,
-                       NULL);
-        }
-    } else {
-        EXPECT_TRUE(NULL == rrset->getRRsig());
-    }
-
-    EXPECT_TRUE(rdata_iterator->isLast());
-}
-
-void
-checkFind(FindMode mode, const Sqlite3DataSrc& data_source,
-          const Name& expected_name, const Name* zone_name,
-          const RRClass& qclass, const RRClass& expected_class,
-          const RRType& expected_type, const vector<RRTTL>& expected_ttls,
-          const uint32_t expected_flags, const vector<RRType>& expected_types,
-          const vector<const vector<string>* >& expected_answers,
-          const vector<const vector<string>* >& expected_signatures)
-{
-    RRsetList result_sets;
-    uint32_t find_flags;
-    unsigned int rrset_matched = 0;
-    unsigned int rrset_count = 0;
-
-    switch (mode) {
-    case NORMAL:
-        EXPECT_EQ(DataSrc::SUCCESS,
-                  data_source.findRRset(expected_name, qclass,
-                                        expected_type, result_sets, find_flags,
-                                        zone_name));
-        break;
-    case EXACT:
-        EXPECT_EQ(DataSrc::SUCCESS,
-                  data_source.findExactRRset(expected_name, qclass,
-                                             expected_type, result_sets,
-                                             find_flags, zone_name));
-        break;
-    case ADDRESS:
-        EXPECT_EQ(DataSrc::SUCCESS,
-                  data_source.findAddrs(expected_name, qclass, result_sets,
-                                        find_flags, zone_name));
-        break;
-    case REFERRAL:
-        EXPECT_EQ(DataSrc::SUCCESS,
-                  data_source.findReferral(expected_name, qclass, result_sets,
-                                           find_flags, zone_name));
-        break;
-    }
-    EXPECT_EQ(expected_flags, find_flags);
-    RRsetList::iterator it = result_sets.begin();
-    for (; it != result_sets.end(); ++it) {
-        vector<RRType>::const_iterator found_type =
-            find(expected_types.begin(), expected_types.end(),
-                 (*it)->getType());
-        // there should be a match
-        EXPECT_TRUE(found_type != expected_types.end());
-        if (found_type != expected_types.end()) {
-            unsigned int index = distance(expected_types.begin(), found_type);
-            checkRRset(*it, expected_name, expected_class, (*it)->getType(),
-                       expected_ttls[index], *expected_answers[index],
-                       expected_signatures[index]);
-            ++rrset_matched;
-        }
-        ++rrset_count;
-    }
-    EXPECT_EQ(expected_types.size(), rrset_count);
-    EXPECT_EQ(expected_types.size(), rrset_matched);
-}
-
-void
-checkFind(FindMode mode, const Sqlite3DataSrc& data_source,
-          const Name& expected_name, const Name* zone_name,
-          const RRClass& qclass, const RRClass& expected_class,
-          const RRType& expected_type, const RRTTL& expected_rrttl,
-          const uint32_t expected_flags,
-          const vector<string>& expected_data,
-          const vector<string>* expected_sig_data)
-{
-    vector<RRType> types;
-    vector<RRTTL> ttls;
-    vector<const vector<string>* > answers;
-    vector<const vector<string>* > signatures;
-
-    types.push_back(expected_type);
-    ttls.push_back(expected_rrttl);
-    answers.push_back(&expected_data);
-    signatures.push_back(expected_sig_data);
-
-    checkFind(mode, data_source, expected_name, zone_name, qclass,
-              expected_class, expected_type, ttls, expected_flags, types,
-              answers, signatures);
-}
-
-TEST_F(Sqlite3DataSourceTest, close) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-}
-
-TEST_F(Sqlite3DataSourceTest, reOpen) {
-    // Replace the data with a totally different zone.  This should succeed,
-    // and shouldn't match any names in the previously managed domains.
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE2));
-
-    DataSrcMatch match(www_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, openFail) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    EXPECT_THROW(data_source.init(SQLITE_DBFILE_NOTEXIST), Sqlite3Error);
-}
-
-TEST_F(Sqlite3DataSourceTest, doubleOpen) {
-    // An attempt of duplicate open should trigger an exception.
-    EXPECT_THROW(data_source.init(SQLITE_DBFILE_EXAMPLE), DataSourceError);
-}
-
-TEST_F(Sqlite3DataSourceTest, doubleClose) {
-    // An attempt of duplicate close should trigger an exception.
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    EXPECT_THROW(data_source.close(), DataSourceError);
-}
-
-TEST_F(Sqlite3DataSourceTest, openBrokenDB) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    // The database exists but is broken.  An exception will be thrown 
-    // in the middle of the initialization.
-    EXPECT_THROW(data_source.init(SQLITE_DBFILE_BROKENDB), Sqlite3Error);
-    // Confirming the strong exception guarantee: the data source must be
-    // in the closed state.
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE));
-}
-
-// Different schema versions, see sqlite3_accessor_unittest.
-TEST_F(Sqlite3DataSourceTest, differentSchemaVersions) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    EXPECT_THROW(data_source.init(SQLITE_DBFILE_NEWSCHEMA),
-                 IncompatibleDbVersion);
-    EXPECT_THROW(data_source.init(SQLITE_DBFILE_OLDSCHEMA),
-                 IncompatibleDbVersion);
-    // Don't bother to test the new_minor case; we should retire this stuff
-    // before it really happens.
-}
-
-// This test only confirms that on-the-fly schema creation works.
-TEST_F(Sqlite3DataSourceTest, memoryDB) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_MEMORY));
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosure) {
-    DataSrcMatch match(www_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(zone_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosureMatchRoot) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.close());
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.init(SQLITE_DBFILE_EXAMPLE_ROOT));
-
-    DataSrcMatch match(Name("org."), rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(Name("."), *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosureAtDelegation) {
-    // The search name exists both in the parent and child zones, but
-    // child has a better match.
-    DataSrcMatch match(child_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(child_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestEnclosureNoMatch) {
-    DataSrcMatch match(nomatch_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findClosestClassMismatch) {
-    DataSrcMatch match(nomatch_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-// If the query class is ANY, the result should be the same as the case where
-// the class exactly matches.
-TEST_F(Sqlite3DataSourceTest, findClosestClassAny) {
-    DataSrcMatch match(www_name, RRClass::ANY());
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(zone_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetNormal) {
-    // Without specifying the zone name, and then with the zone name
-    checkFind(NORMAL, data_source, www_name, NULL, rrclass, rrclass, rrtype,
-              rrttl, 0, common_a_data, &common_sig_data);
-
-    checkFind(NORMAL, data_source, www_name, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
-    // With a zone name that doesn't match
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(www_name, rrclass, rrtype,
-                                    result_sets, find_flags, &nomatch_name));
-    EXPECT_EQ(DataSrc::NO_SUCH_ZONE, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end()); // should be empty
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetClassMismatch) {
-    EXPECT_EQ(DataSrc::ERROR,
-              data_source.findRRset(www_name, rrclass_notmatch, rrtype,
-                                    result_sets, find_flags, NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetClassAny) {
-    checkFind(NORMAL, data_source, www_name, NULL, RRClass::ANY(), rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetClassClassAny) {
-    checkFind(NORMAL, data_source, www_name, NULL, RRClass::ANY(), rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetNormalANY) {
-    types.push_back(RRType::A());
-    types.push_back(RRType::NSEC());
-    ttls.push_back(RRTTL(3600));
-    ttls.push_back(RRTTL(7200));
-    answers.push_back(&common_a_data);
-    answers.push_back(&www_nsec_data);
-    signatures.push_back(&common_sig_data);
-    signatures.push_back(&www_nsec_sig_data);
-
-    rrtype = RRType::ANY();
-    checkFind(NORMAL, data_source, www_name, NULL, rrclass, rrclass, rrtype,
-              ttls, 0, types, answers, signatures);
-
-    checkFind(NORMAL, data_source, www_name, &zone_name, rrclass, rrclass,
-              rrtype, ttls, 0, types, answers, signatures);
-}
-
-// Case insensitive lookup
-TEST_F(Sqlite3DataSourceTest, findRRsetNormalCase) {
-    checkFind(NORMAL, data_source, www_upper_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
-    checkFind(NORMAL, data_source, www_upper_name, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(www_upper_name, rrclass, rrtype,
-                                    result_sets, find_flags, &nomatch_name));
-    EXPECT_EQ(DataSrc::NO_SUCH_ZONE, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end()); // should be empty
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetApexNS) {
-    rrtype = RRType::NS();
-    checkFind(NORMAL, data_source, zone_name, NULL, rrclass, rrclass, rrtype,
-              rrttl, DataSrc::REFERRAL, apex_ns_data, &apex_ns_sig_data);
-
-    checkFind(NORMAL, data_source, zone_name, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::REFERRAL, apex_ns_data,
-              &apex_ns_sig_data);
-
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(zone_name, rrclass, rrtype,
-                                    result_sets, find_flags, &nomatch_name));
-    EXPECT_EQ(DataSrc::NO_SUCH_ZONE, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end()); // should be empty
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetApexANY) {
-    types.push_back(RRType::SOA());
-    types.push_back(RRType::NS());
-    types.push_back(RRType::MX());
-    types.push_back(RRType::NSEC());
-    types.push_back(RRType::DNSKEY());
-    ttls.push_back(rrttl); // SOA TTL
-    ttls.push_back(rrttl); // NS TTL
-    ttls.push_back(rrttl); // MX TTL
-    ttls.push_back(RRTTL(7200)); // NSEC TTL
-    ttls.push_back(rrttl); // DNSKEY TTL
-    answers.push_back(&apex_soa_data);
-    answers.push_back(&apex_ns_data);
-    answers.push_back(&apex_mx_data);
-    answers.push_back(&apex_nsec_data);
-    answers.push_back(&apex_dnskey_data);
-    signatures.push_back(&apex_soa_sig_data);
-    signatures.push_back(&apex_ns_sig_data);
-    signatures.push_back(&apex_mx_sig_data);
-    signatures.push_back(&apex_nsec_sig_data);
-    signatures.push_back(&apex_dnskey_sig_data);
-
-    rrtype = RRType::ANY();
-
-    // there is an NS at the zone apex, so the REFERRAL flag should
-    // be set, but will ordinarily be ignored by the caller
-    checkFind(NORMAL, data_source, zone_name, NULL, rrclass, rrclass, rrtype,
-              ttls, DataSrc::REFERRAL, types, answers, signatures);
-
-    checkFind(NORMAL, data_source, zone_name, &zone_name, rrclass, rrclass,
-              rrtype, ttls, DataSrc::REFERRAL, types, answers, signatures);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetMixedANY) {
-    // ANY query for mixed order RRs
-    const Name qname("mix.example.com");
-
-    types.push_back(RRType::A());
-    types.push_back(RRType::AAAA());
-    ttls.push_back(rrttl);
-    ttls.push_back(rrttl);
-    answers.push_back(&mix_a_data);
-    answers.push_back(&mix_aaaa_data);
-    signatures.push_back(NULL);
-    signatures.push_back(NULL);
-
-    rrtype = RRType::ANY();
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, ttls, 0, types, answers, signatures);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetApexNXRRSET) {
-    rrtype = RRType::AAAA();
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(zone_name, rrclass, rrtype,
-                                    result_sets, find_flags, &zone_name));
-    // there's an NS RRset at the apex name, so the REFERRAL flag should be
-    // set, too.
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
-    // Same test, without specifying the zone name
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(zone_name, rrclass, rrtype,
-                                    result_sets, find_flags, NULL));
-    // there's an NS RRset at the apex name, so the REFERRAL flag should be
-    // set, too.
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-// Matching a wildcard node.  There's nothing special for the data source API
-// point of view, but perform minimal tests anyway.
-TEST_F(Sqlite3DataSourceTest, findRRsetWildcard) {
-    const Name qname("*.wild.example.com");
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, wild_a_data, &common_sig_data);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, wild_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetEmptyNode) {
-    // foo.bar.example.com exists, but bar.example.com doesn't have any data.
-    const Name qname("bar.example.com");
-
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, rrclass, rrtype,
-                                    result_sets, find_flags, NULL));
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, rrclass, rrtype,
-                                    result_sets, find_flags, &zone_name));
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-// There's nothing special about DNAME lookup for the data source API
-// point of view, but perform minimal tests anyway.
-TEST_F(Sqlite3DataSourceTest, findRRsetDNAME) {
-    const Name qname("dname.example.com");
-
-    rrtype = RRType::DNAME();
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, dname_data, &dname_sig_data);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, dname_data, &dname_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetCNAME) {
-    const Name qname("foo.example.com");
-
-    // This qname only has the CNAME (+ sigs).  CNAME query is not different
-    // from ordinary queries.
-    rrtype = RRType::CNAME();
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, cname_data, &cname_sig_data);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, cname_data, &cname_sig_data);
-
-    // queries for (ordinary) different RR types that match the CNAME.
-    // CNAME_FOUND flag is set, and the CNAME RR is returned instead of A
-    rrtype = RRType::A();
-    types.push_back(RRType::CNAME());
-    ttls.push_back(rrttl);
-    answers.push_back(&cname_data);
-    signatures.push_back(&cname_sig_data);
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, ttls, DataSrc::CNAME_FOUND, types, answers, signatures);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, ttls, DataSrc::CNAME_FOUND, types, answers, signatures);
-
-    // NSEC query that match the CNAME.
-    // CNAME_FOUND flag is NOT set, and the NSEC RR is returned instead of
-    // CNAME.
-    rrtype = RRType::NSEC();
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, RRTTL(7200), 0, cname_nsec_data, &cname_nsec_sig_data);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, RRTTL(7200), 0, cname_nsec_data, &cname_nsec_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetDelegation) {
-    const Name qname("www.subzone.example.com");
-
-    // query for a name under a zone cut.  From the data source API point
-    // of view this is no different than "NXDOMAIN".
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, rrclass, rrtype,
-                                    result_sets, find_flags, NULL));
-    EXPECT_EQ(DataSrc::NAME_NOT_FOUND, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetDelegationAtZoneCut) {
-    const Name qname("subzone.example.com");
-
-    // query for a name *at* a zone cut.  It matches the NS RRset for the
-    // delegation.
-
-    // For non-NS ordinary queries, "no type" should be set too, and no RRset is
-    // returned.
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, rrclass, rrtype,
-                                    result_sets, find_flags, NULL));
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, rrclass, rrtype,
-                                    result_sets, find_flags, &zone_name));
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND | DataSrc::REFERRAL, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
-    // For NS query, RRset is returned with the REFERRAL flag.  No RRSIG should
-    // be provided.
-    rrtype = RRType::NS();
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::REFERRAL, delegation_ns_data, NULL);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::REFERRAL, delegation_ns_data, NULL);
-
-    // For ANY query.  At the backend data source level, it returns all
-    // existent records with their RRSIGs, setting the referral flag.
-    rrtype = RRType::ANY();
-    types.push_back(RRType::NS());
-    types.push_back(RRType::NSEC());
-    types.push_back(RRType::DS());
-    ttls.push_back(rrttl);
-    ttls.push_back(RRTTL(7200));
-    ttls.push_back(rrttl);
-    answers.push_back(&delegation_ns_data);
-    answers.push_back(&delegation_nsec_data);
-    answers.push_back(&delegation_ds_data);
-    signatures.push_back(NULL);
-    signatures.push_back(&delegation_nsec_sig_data);
-    signatures.push_back(&delegation_ds_sig_data);
-
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, ttls, DataSrc::REFERRAL, types, answers,
-              signatures);
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, ttls, DataSrc::REFERRAL, types, answers,
-              signatures);
-
-    // For NSEC query.  At the backend data source level, it returns NSEC+
-    // RRSIG with the referral flag.
-    rrtype = RRType::NSEC();
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, RRTTL(7200), DataSrc::REFERRAL, delegation_nsec_data,
-              &delegation_nsec_sig_data);
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, RRTTL(7200), DataSrc::REFERRAL, delegation_nsec_data,
-              &delegation_nsec_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetInChildZone) {
-    const Name qname("www.sql1.example.com");
-    const Name child_zone_name("sql1.example.com");
-
-    // If we don't specify the zone, the data source should identify the
-    // best matching zone.
-    checkFind(NORMAL, data_source, qname, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, child_a_data, &child_sig_data);
-
-    // If we specify the parent zone, it's treated as NXDOMAIN because it's
-    // under a zone cut.
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, rrclass, rrtype,
-                                    result_sets, find_flags, &zone_name));
-    EXPECT_EQ(DataSrc::NAME_NOT_FOUND, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-
-    // If we specify the child zone, it should be the same as the first case.
-    checkFind(NORMAL, data_source, qname, &child_zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, child_a_data, &child_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRset) {
-    // Normal case.  No different than findRRset.
-    checkFind(EXACT, data_source, www_name, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRsetClassMismatch) {
-    EXPECT_EQ(DataSrc::ERROR,
-              data_source.findExactRRset(www_name, rrclass_notmatch, rrtype,
-                                         result_sets, find_flags, NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRsetClassAny) {
-    checkFind(EXACT, data_source, www_name, &zone_name, RRClass::ANY(), rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findRRsetNSEC3) {
-    // Simple NSEC3 tests (more should be added)
-    string hashstr("1BB7SO0452U1QHL98UISNDD9218GELR5");
-
-    const Name nsec3_zonename("sql2.example.com");
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findCoveringNSEC3(nsec3_zonename,
-                                            hashstr, result_sets));
-    RRsetList::iterator it = result_sets.begin();
-    checkRRset(*it, Name(hashstr).concatenate(nsec3_zonename), rrclass,
-               RRType::NSEC3(), RRTTL(7200), nsec3_data, &nsec3_sig_data);
-    ++it;
-    EXPECT_TRUE(it == result_sets.end());
-
-}
-
-TEST_F(Sqlite3DataSourceTest, findExactRRsetCNAME) {
-    const Name qname("foo.example.com");
-
-    // This qname only has the CNAME (+ sigs).  In this case it should be
-    // no different than findRRset for CNAME query.
-    rrtype = RRType::CNAME();
-    checkFind(NORMAL, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, rrttl, 0, cname_data, &cname_sig_data);
-
-    // queries for (ordinary) different RR types that match the CNAME.
-    // "type not found" set, but CNAME and its sig are returned (awkward,
-    // but that's how it currently works).
-    rrtype = RRType::A();
-    types.push_back(RRType::CNAME());
-    ttls.push_back(rrttl);
-    answers.push_back(&cname_data);
-    signatures.push_back(&cname_sig_data);
-    checkFind(EXACT, data_source, qname, &zone_name, rrclass, rrclass,
-              rrtype, ttls, DataSrc::TYPE_NOT_FOUND, types, answers,
-              signatures);
-}
-
-void
-Sqlite3DataSourceTest::findReferralRRsetCommon(const Name& qname,
-                                               const RRClass& qclass)
-{
-    types.push_back(RRType::NS());
-    types.push_back(RRType::DS());
-    ttls.push_back(rrttl);
-    ttls.push_back(rrttl);
-    answers.push_back(&apex_ns_data);
-    answers.push_back(&child_ds_data);
-    signatures.push_back(NULL);
-    signatures.push_back(&child_ds_sig_data);
-    // Note: zone_name matters here because we need to perform the search
-    // in the parent zone.
-    checkFind(REFERRAL, data_source, qname, &zone_name, qclass, rrclass,
-              rrtype, ttls, DataSrc::REFERRAL, types, answers, signatures);
-}
-    
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRset) {
-    // A referral lookup searches for NS, DS, or DNAME, or their sigs.
-    const Name qname("sql1.example.com");
-    findReferralRRsetCommon(qname, rrclass);
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetClassMismatch) {
-    EXPECT_EQ(DataSrc::ERROR,
-              data_source.findReferral(www_name, rrclass_notmatch, result_sets,
-                                       find_flags, NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetClassAny) {
-    const Name qname("sql1.example.com");
-    findReferralRRsetCommon(qname, RRClass::ANY());
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetDNAME) {
-    // same as above.  the DNAME case.
-    const Name qname("dname.example.com");
-    checkFind(REFERRAL, data_source, qname, &zone_name, rrclass, rrclass,
-              RRType::DNAME(), rrttl, 0, dname_data, &dname_sig_data);
-}
-
-TEST_F(Sqlite3DataSourceTest, findReferralRRsetFail) {
-    // qname has not "referral" records.  the result should be "empty".
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findReferral(www_name, rrclass,
-                                       result_sets, find_flags, &zone_name));
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-void
-Sqlite3DataSourceTest::findAddressRRsetCommon(const RRClass& qclass) {
-    // A referral lookup searches for A or AAAA, or their sigs.
-
-    // A-only case
-    checkFind(ADDRESS, data_source, www_name, &zone_name, qclass, rrclass,
-              rrtype, rrttl, 0, common_a_data, &common_sig_data);
-
-    // AAAA-only case
-    checkFind(ADDRESS, data_source, Name("ip6.example.com"), &zone_name,
-              qclass, rrclass, RRType::AAAA(), rrttl, 0, common_aaaa_data,
-              &common_aaaa_sig_data);
-
-    // Dual-stack
-    types.push_back(RRType::A());
-    ttls.push_back(rrttl);
-    answers.push_back(&common_a_data);
-    signatures.push_back(&common_sig_data);
-    types.push_back(RRType::AAAA());
-    ttls.push_back(rrttl);
-    answers.push_back(&common_aaaa_data);
-    signatures.push_back(&common_aaaa_sig_data);
-    checkFind(ADDRESS, data_source, Name("ip46.example.com"), &zone_name,
-              rrclass, rrclass, rrtype, ttls, 0, types, answers, signatures);
-
-    // The qname doesn't have no address records.
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findAddrs(Name("text.example.com"), qclass,
-                                    result_sets, find_flags, &zone_name));
-    EXPECT_EQ(DataSrc::TYPE_NOT_FOUND, find_flags);
-    EXPECT_TRUE(result_sets.begin() == result_sets.end());
-}
-
-TEST_F(Sqlite3DataSourceTest, findAddressRRset) {
-    findAddressRRsetCommon(rrclass);
-}
-
-TEST_F(Sqlite3DataSourceTest, findAddressRRsetClassMismatch) {
-    EXPECT_EQ(DataSrc::ERROR, data_source.findAddrs(www_name, rrclass_notmatch,
-                                                    result_sets, find_flags,
-                                                    NULL));
-}
-
-TEST_F(Sqlite3DataSourceTest, findAddressRRsetClassAny) {
-    findAddressRRsetCommon(RRClass::ANY());
-}
-
-}
diff --git a/src/lib/datasrc/tests/static_unittest.cc b/src/lib/datasrc/tests/static_unittest.cc
deleted file mode 100644
index 2a19ecb..0000000
--- a/src/lib/datasrc/tests/static_unittest.cc
+++ /dev/null
@@ -1,422 +0,0 @@
-// Copyright (C) 2010  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 <stdint.h>
-#include <string>
-#include <vector>
-
-#include <config.h>
-
-#include <gtest/gtest.h>
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rdata.h>
-#include <dns/rrclass.h>
-#include <dns/rrtype.h>
-#include <dns/rdataclass.h>
-#include <dns/rrsetlist.h>
-#include <cc/data.h>
-
-#include <datasrc/query.h>
-#include <datasrc/data_source.h>
-#include <datasrc/static_datasrc.h>
-
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-using namespace isc::datasrc;
-
-namespace {
-class StaticDataSourceTest : public ::testing::Test {
-protected:
-    StaticDataSourceTest() : version_name("version.bind"),
-                             authors_name("authors.bind"),
-                             nomatch_name("example.com"),
-                             rrclass(RRClass::CH()), rrtype(RRType::TXT()),
-                             rrttl(RRTTL(0)), find_flags(0), matched_rdata(0)
-    {
-        // version.bind is answered with package name+version
-        // (defined as PACKAGE_STRING in config.h)
-        version_data.push_back(PACKAGE_STRING);
-
-        // NOTE: in addition, the order of the following items matter.
-        authors_data.push_back("Chen Zhengzhang");
-        authors_data.push_back("Dmitriy Volodin");
-        authors_data.push_back("Evan Hunt");
-        authors_data.push_back("Haidong Wang");
-        authors_data.push_back("Haikuo Zhang");
-        authors_data.push_back("Han Feng");
-        authors_data.push_back("Jelte Jansen");
-        authors_data.push_back("Jeremy C. Reed");
-        authors_data.push_back("Xie Jiagui");
-        authors_data.push_back("Jin Jian");
-        authors_data.push_back("JINMEI Tatuya");
-        authors_data.push_back("Kazunori Fujiwara");
-        authors_data.push_back("Michael Graff");
-        authors_data.push_back("Michal Vaner");
-        authors_data.push_back("Mukund Sivaraman");
-        authors_data.push_back("Naoki Kambe");
-        authors_data.push_back("Shane Kerr");
-        authors_data.push_back("Shen Tingting");
-        authors_data.push_back("Stephen Morris");
-        authors_data.push_back("Yoshitaka Aharen");
-        authors_data.push_back("Zhang Likun");
-
-        version_ns_data.push_back("version.bind.");
-        authors_ns_data.push_back("authors.bind.");
-
-        version_soa_data.push_back("version.bind. hostmaster.version.bind. "
-                                   "0 28800 7200 604800 86400");
-        authors_soa_data.push_back("authors.bind. hostmaster.authors.bind. "
-                                   "0 28800 7200 604800 86400");
-    }
-    StaticDataSrc data_source;
-    const Name version_name;
-    const Name authors_name;
-    const Name nomatch_name;
-    const RRClass rrclass;
-    RRType rrtype;              // we allow this to be modified in the test
-    RRTTL rrttl;
-    RRsetList result_sets;
-    uint32_t find_flags;
-    unsigned matched_rdata;
-    vector<RRType> types;
-    vector<RRTTL> ttls;
-    vector<const vector<string>* > answers;
-    vector<string> version_data;
-    vector<string> authors_data;
-    vector<string> version_ns_data;
-    vector<string> authors_ns_data;
-    vector<string> version_soa_data;
-    vector<string> authors_soa_data;
-};
-
-void
-checkRRset(ConstRRsetPtr rrset, const Name& expected_name,
-           const RRClass& expected_class, const RRType& expected_type,
-           const RRTTL& rrttl, const vector<string>& expected_data)
-{
-    EXPECT_EQ(expected_name, rrset->getName());
-    EXPECT_EQ(expected_class, rrset->getClass());
-    EXPECT_EQ(expected_type, rrset->getType());
-    EXPECT_EQ(rrttl, rrset->getTTL());
-
-    RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
-    vector<string>::const_iterator data_it = expected_data.begin();
-    for (; data_it != expected_data.end(); ++data_it) {
-        EXPECT_FALSE(rdata_iterator->isLast());
-        if (rdata_iterator->isLast()) {
-            // buggy case, should stop here
-            break;
-        }
-        EXPECT_EQ(0, (rdata_iterator->getCurrent()).compare(
-                      *createRdata(expected_type,
-                                   expected_class,
-                                   *data_it)));
-        rdata_iterator->next();
-    }
-
-    EXPECT_TRUE(rdata_iterator->isLast());
-}
-
-void
-checkFind(const DataSrc& data_source,
-          const Name& qname, const Name* zone_name,
-          const RRClass& qclass, const RRClass& expected_class,
-          const RRType& qtype, const vector<RRTTL>& expected_ttls,
-          const uint32_t expected_flags, const vector<RRType>& expected_types,
-          const vector<const vector<string>* >& expected_answers)
-{
-    RRsetList result_sets;
-    uint32_t find_flags;
-    unsigned int rrset_matched = 0;
-    unsigned int rrset_count = 0;
-
-    EXPECT_EQ(DataSrc::SUCCESS,
-              data_source.findRRset(qname, qclass, qtype, result_sets,
-                                    find_flags, zone_name));
-    EXPECT_EQ(expected_flags, find_flags);
-
-    if ((find_flags & (DataSrc::NO_SUCH_ZONE | DataSrc::NAME_NOT_FOUND |
-                       DataSrc::TYPE_NOT_FOUND)) != 0) {
-        EXPECT_TRUE(result_sets.begin() == result_sets.end());
-        return;
-    }
-
-    RRsetList::iterator it = result_sets.begin();
-    for (; it != result_sets.end(); ++it) {
-        vector<RRType>::const_iterator found_type =
-            find(expected_types.begin(), expected_types.end(),
-                 (*it)->getType());
-        // there should be a match
-        EXPECT_TRUE(found_type != expected_types.end());
-        if (found_type != expected_types.end()) {
-            unsigned int index = distance(expected_types.begin(), found_type);
-            checkRRset(*it, qname, expected_class, (*it)->getType(),
-                       expected_ttls[index], *expected_answers[index]);
-            ++rrset_matched;
-        }
-        ++rrset_count;
-    }
-    EXPECT_EQ(expected_types.size(), rrset_count);
-    EXPECT_EQ(expected_types.size(), rrset_matched);
-}
-
-void
-checkFind(const DataSrc& data_source,
-          const Name& qname, const Name* zone_name,
-          const RRClass& qclass, const RRClass& expected_class,
-          const RRType& qtype,  // == expected RRType
-          const RRTTL& expected_ttl, const uint32_t expected_flags,
-          const vector<string>& expected_answers)
-{
-    vector<RRType> types;
-    vector<RRTTL> ttls;
-    vector<const vector<string>* > answers;
-
-    types.push_back(qtype);
-    ttls.push_back(expected_ttl);
-    answers.push_back(&expected_answers);
-
-    checkFind(data_source, qname, zone_name, qclass, expected_class, qtype,
-              ttls, expected_flags, types, answers);
-}
-
-TEST_F(StaticDataSourceTest, init) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.init());
-}
-
-TEST_F(StaticDataSourceTest, close) {
-    EXPECT_EQ(DataSrc::SUCCESS, data_source.init());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersion) {
-    DataSrcMatch match(version_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(version_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-// Class Any query should result in the same answer.
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassAny) {
-    DataSrcMatch match(version_name, RRClass::ANY());
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(version_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-// If class doesn't match the lookup should fail.
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionClassMismatch) {
-    DataSrcMatch match(version_name, RRClass::IN());
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForVersionPartial) {
-    DataSrcMatch match(Name("foo").concatenate(version_name), rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(version_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthors) {
-    DataSrcMatch match(authors_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(authors_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureForAuthorsPartial) {
-    DataSrcMatch match(Name("foo").concatenate(authors_name), rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(authors_name, *match.getEnclosingZone());
-    EXPECT_EQ(&data_source, match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findClosestEnclosureNoMatch) {
-    DataSrcMatch match(nomatch_name, rrclass);
-    data_source.findClosestEnclosure(match);
-    EXPECT_EQ(static_cast<void*>(NULL), match.getEnclosingZone());
-    EXPECT_EQ(static_cast<void*>(NULL), match.getDataSource());
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionTXT) {
-    checkFind(data_source, version_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, version_data);
-    checkFind(data_source, version_name, &version_name, rrclass, rrclass,
-              rrtype, rrttl, 0, version_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionNS) {
-    rrtype = RRType::NS();
-    checkFind(data_source, version_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, version_ns_data);
-    checkFind(data_source, version_name, &version_name, rrclass, rrclass,
-              rrtype, rrttl, 0, version_ns_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionSOA) {
-    rrtype = RRType::SOA();
-
-    checkFind(data_source, version_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, version_soa_data);
-    checkFind(data_source, version_name, &version_name, rrclass, rrclass,
-              rrtype, rrttl, 0, version_soa_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetVersionANY) {
-    rrtype = RRType::ANY();
-    
-    types.push_back(RRType::TXT());
-    types.push_back(RRType::NS());
-    types.push_back(RRType::SOA());
-    ttls.push_back(rrttl);
-    ttls.push_back(rrttl);
-    ttls.push_back(rrttl);
-    answers.push_back(&version_data);
-    answers.push_back(&version_ns_data);
-    answers.push_back(&version_soa_data);
-
-    checkFind(data_source, version_name, NULL, rrclass, rrclass, rrtype, ttls,
-              0, types, answers);
-    checkFind(data_source, version_name, &version_name, rrclass, rrclass,
-              rrtype, ttls, 0, types, answers);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsTXT) {
-    checkFind(data_source, authors_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, authors_data);
-    checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
-              rrtype, rrttl, 0, authors_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsNS) {
-    rrtype = RRType::NS();
-    checkFind(data_source, authors_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, authors_ns_data);
-    checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
-              rrtype, rrttl, 0, authors_ns_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsSOA) {
-    rrtype = RRType::SOA();
-    checkFind(data_source, authors_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, 0, authors_soa_data);
-    checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
-              rrtype, rrttl, 0, authors_soa_data);
-}
-
-TEST_F(StaticDataSourceTest, findRRsetAuthorsANY) {
-    rrtype = RRType::ANY();
-    
-    types.push_back(RRType::TXT());
-    types.push_back(RRType::NS());
-    types.push_back(RRType::SOA());
-    ttls.push_back(rrttl);
-    ttls.push_back(rrttl);
-    ttls.push_back(rrttl);
-    answers.push_back(&authors_data);
-    answers.push_back(&authors_ns_data);
-    answers.push_back(&authors_soa_data);
-
-    checkFind(data_source, authors_name, NULL, rrclass, rrclass, rrtype, ttls,
-              0, types, answers);
-    checkFind(data_source, authors_name, &authors_name, rrclass, rrclass,
-              rrtype, ttls, 0, types, answers);
-}
-// Class ANY lookup should result in the same answer.
-TEST_F(StaticDataSourceTest, findRRsetVersionClassAny) {
-    checkFind(data_source, version_name, NULL, RRClass::ANY(), rrclass,
-              rrtype, rrttl, 0, version_data);
-    checkFind(data_source, version_name, &version_name, RRClass::ANY(), rrclass,
-              rrtype, rrttl, 0, version_data);
-}
-
-// If the class doesn't match, it should simply fail.
-TEST_F(StaticDataSourceTest, findRRsetVersionClassMismatch) {
-    EXPECT_EQ(DataSrc::ERROR,
-              data_source.findRRset(version_name, RRClass::IN(), rrtype,
-                                    result_sets, find_flags, &version_name));
-}
-
-TEST_F(StaticDataSourceTest, findRRsetOutOfZone) {
-    // If the qname doesn't match any of the static zones, the result should
-    // be "no such zone", regardless of whether the zone is explicitly
-    // specified.  Other "expected" result parameters will be ignored.
-    checkFind(data_source, nomatch_name, NULL, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-    checkFind(data_source, nomatch_name, &version_name, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-    checkFind(data_source, nomatch_name, &authors_name, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-}
-
-// If a zone name is given but doesn't match any of the static zones,
-// the result should be "no such zone"
-TEST_F(StaticDataSourceTest, findRRsetZoneMismatch) {
-    const Name& short_zonename(Name("bind"));
-    checkFind(data_source, version_name, &short_zonename, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-    checkFind(data_source, authors_name, &short_zonename, rrclass, rrclass,
-              rrtype, rrttl, DataSrc::NO_SUCH_ZONE, authors_ns_data);
-}
-
-// Zone matches, but name doesn't exist in the zone
-TEST_F(StaticDataSourceTest, findRRsetNoName) {
-    checkFind(data_source, Name("foo").concatenate(version_name), NULL, rrclass,
-              rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND, authors_ns_data);
-    checkFind(data_source, Name("foo").concatenate(version_name), &version_name,
-              rrclass, rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND,
-              authors_ns_data);
-    checkFind(data_source, Name("foo").concatenate(authors_name), NULL, rrclass,
-              rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND, authors_ns_data);
-    checkFind(data_source, Name("foo").concatenate(authors_name), &authors_name,
-              rrclass, rrclass, rrtype, rrttl, DataSrc::NAME_NOT_FOUND,
-              authors_ns_data);
-}
-
-// Zone matches and qname exists, but type doesn't exist for the name.
-TEST_F(StaticDataSourceTest, findRRsetNoType) {
-    const RRType& nomatch_type = RRType::A();
-
-    checkFind(data_source, version_name, NULL, rrclass,
-              rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
-              authors_ns_data);
-    checkFind(data_source, version_name, &version_name, rrclass,
-              rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
-              authors_ns_data);
-    checkFind(data_source, authors_name, NULL, rrclass,
-              rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
-              authors_ns_data);
-    checkFind(data_source, authors_name, &authors_name, rrclass,
-              rrclass, nomatch_type, rrttl, DataSrc::TYPE_NOT_FOUND,
-              authors_ns_data);
-}
-
-// Simple tests for "unsupported" tests.
-TEST_F(StaticDataSourceTest, notImplemented) {
-    Name target_name(version_name);
-    EXPECT_EQ(DataSrc::NOT_IMPLEMENTED,
-              data_source.findPreviousName(version_name, target_name,
-                                           &version_name));
-
-    string target_hash;
-    EXPECT_EQ(DataSrc::NOT_IMPLEMENTED,
-              data_source.findCoveringNSEC3(version_name, target_hash,
-                                            result_sets));
-}
-
-}
diff --git a/src/lib/datasrc/tests/test_datasrc.cc b/src/lib/datasrc/tests/test_datasrc.cc
deleted file mode 100644
index d78e7db..0000000
--- a/src/lib/datasrc/tests/test_datasrc.cc
+++ /dev/null
@@ -1,657 +0,0 @@
-// Copyright (C) 2010  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 <config.h>
-
-#include <cassert>
-
-#include <algorithm>
-
-#include <dns/tests/unittest_util.h>
-#include <datasrc/tests/test_datasrc.h>
-
-#include <datasrc/data_source.h>
-
-#include <util/buffer.h>
-#include <dns/messagerenderer.h>
-#include <dns/name.h>
-#include <dns/rdata.h>
-#include <dns/rdataclass.h>
-#include <dns/rrclass.h>
-#include <dns/rrset.h>
-#include <dns/rrsetlist.h>
-#include <dns/rrtype.h>
-#include <dns/rrttl.h>
-
-#include <iostream>
-
-using isc::UnitTestUtil;
-using namespace std;
-using namespace isc::dns;
-using namespace isc::dns::rdata;
-
-namespace isc {
-namespace datasrc {
-
-namespace {
-
-// This is a mock data source for testing.  It can contain multiple zones.
-// The content of each zone should be configured in the form of RRData{}.
-// Each RRData element is a tuple of char strings, representing
-// "name, RRtype, RDATA".  For simplicity we use the same single TTL for
-// RRs (TEST_TTL) defined below.
-// Multiple RRs of the same pair of (name, RRtype) can be defined, but
-// they must not be interleaved with other types of pair.  For example,
-// This is okay:
-// {"example.com", "AAAA", "2001:db8::1"},
-// {"example.com", "AAAA", "2001:db8::2"},
-// ...
-// but this is invalid:
-// {"example.com", "AAAA", "2001:db8::1"},
-// {"example.com", "A", "192.0.2.1"},
-// {"example.com", "AAAA", "2001:db8::2"},
-// ...
-// If an RRset is associated with an RRSIG, the RRSIG must immediately follow
-// the RRset to be signed.  Multiple RRSIGs can follow the RRset.  RRSIG
-// records will always be attached to the most recent non-RRSIG RRset;
-// consequently, the first RR listed must not be an RRSIG record.
-//
-// Names are sorted internally, and don't have to be sorted in the data.
-//
-// A zone is defined in the form of ZoneData{}, which contains:
-// zone name (character string)
-// RRclass (character string)
-// A pointer to in-zone RRs in the form of RRData{}
-// A pointer to glue RRs in the form of RRData{}
-// Glues can be omitted, in which case a convenient constant "empty_records"
-// can be specified.
-
-// For simplicity we use the same single TTL for all test RRs.
-const uint32_t TEST_TTL = 3600;
-
-struct RRData {
-    const char* const name;
-    const char* const rrtype;
-    const char* const rdata;
-};
-
-struct ZoneData {
-    const char* const zone_name;
-    const char* const rrclass;
-    const struct RRData* records;
-    const struct RRData* glue_records;
-};
-
-//
-// zone data for example.com
-//
-const struct RRData example_com_records[] = {
-    // example.com
-    {"example.com", "NS", "dns01.example.com"},
-    {"example.com", "NS", "dns02.example.com"},
-    {"example.com", "NS", "dns03.example.com"},
-    {"example.com", "RRSIG", "NS 5 2 3600 20100322084538 20100220084538 33495 example.com. ClcrfjkQZUY5L6ZlCkU3cJHzcrEGrofKSVeeoeZ+w6yeEowFNVXs2YBo3tom53DiCrdD9rs3feVSLGW5rjsz/O6lDuomgQG+EVSnWa7GTIPBXj1BmDXXp3XxeldYmhf4UzaN5BA+RUA5E8NChNKuNNof76j2S9tilfN/kvpy4fw="},
-    {"example.com", "SOA", "master.example.com. admin.example.com. 1234 3600 1800 2419200 7200"},
-    {"example.com", "RRSIG", "SOA 5 2 3600 20100322084538 20100220084538 33495 example.com.  KUun66Qaw36osk2BJS6U1fAy3PPDkNo2QK4meGNbDBY8q8b+f2o+IXJ14YCvssGl1ORW0CcLnDRxssnk8V/Svmj5iFhO+8HC2hnVBdi2zewvdVtwRb+lWwKN7pkXXwuy6g1t9WCd/j5FCc/wgxqtZUTPb6XgZcnHrORDMOTqLs4="},
-    {"example.com", "NSEC", "cname-ext.example.com. NS SOA MX RRSIG NSEC DNSKEY"},
-    {"example.com", "RRSIG", "NSEC 5 2 7200 20100322084538 20100220084538 33495 example.com. KxuVaPPKNPJzr/q+cJPiNlkHVTQK0LVsgTbSqruXQc25lAd0wn5oKUtxL1bEAchHkfA8eLzcYCj2ZqqAv9OJubw53mfskTad7UHs4Uj2RTrIsNGMCiZGgOpvNb9JcWpQtoyXVT1uNse+Qsbeir0eyeYIufUynFU041jtNrlJMio="},
-    {"example.com", "DNSKEY", "257 3 5 AwEAAe5WFbxdCPq2jZrZhlMj7oJdff3W7syJtbvzg62tRx0gkoCDoBI9DPjlOQG0UAbj+xUV4HQZJStJaZ+fHU5AwVNT+bBZdtV+NujSikhdTHb4FYLg2b3Cx9NyJvAVukHp/91HnWuG4T36CzAFrfPwsHIrBz9BsaIQ21VRkcmj7DswfI/iDGd8j6bqiODyNZYQ+ZrLmF0KIJ2yPN3iO6Zq23TaOrVTjB7d1a/h31ODfiHAxFHrkY3t3D5JR9Nsl/7fdRmSznwtcSDgLXBoFEYmw6p86AcvRyoYNcL1SXjaKVLG5jyU3UR+LcGZT5t/0xGfoIK/aKwENrsjcKZZj660b1M="},
-    {"example.com", "DNSKEY", "256 3 5 AwEAAcOUBllYc1hf7ND9uDy+Yz1BF3sI0m4qNGV7WcTD0WEiuV7IjXgHE36fCmS9QsUxSSOVo1I/FMxI2PJVqTYHkXFBS7AzLGsQYMU7UjBZSotBJ6Imt5pXMu+lEDNy8TOUzG3xm7g0qcbWYF6qCEfvZoBtAqi5Rk7Mlrqs8agxYyMx"},
-    {"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 4456 example.com. 37FC0rcwOZVarTMjft0BMbvv8hbJU7OHNsvO7R1q6OgsLTj7QGMX3sC42JGbwUrYI/OwnZblNcv1eim0g0jX5k+sVr2OJsEubngRjVqLo54qV8rBC14tLk9PGKxxjQG0IBJU866uHxzXYBO2a1r2g93/qyTtrT7iPLu/2Ce1WRKMBPK0yf4nW2usFU/PXesXFWpZ7HLGZL73/NWv8wcezBDuU0B2PlHLjSu7k6poq6JWDC02o5SYnEBwsJ5Chi+3/NZmzKTiNP7g0H4t6QhunkEXxL3z0617mwwQt00ypXsNunnPy4Ub5Kllk1SKJl8ZkEDKkJtSvuXJhcAZsLyMQw=="},
-    {"example.com", "RRSIG", "DNSKEY 5 2 3600 20100416210049 20100317210049 33495 example.com. h3OM5r3roBsgnEQk9fcjTg5L7p3yDptDpVzDN/lgjqpaWxtlz5LsulBH3YzwYyXzT7pG7L0/qT6dcuRECc/rniECviWvmJMJZzEAMry0Of/pk/8ekuGTxABpqwAoCwM5as30sc0cfMJTS7umpJVDA4lRB2zoKGefWnJ3+pREDiY="},
-
-    // dns01.example.com
-    {"dns01.example.com", "A", "192.0.2.1"},
-    {"dns01.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. NIawlZLk8WZAjNux7oQM2mslfW52OZFFkWt++7FHu2SU98XqEeKfCMnpgtWe5T8Nr9cS8df901iEOJoWQzGTEaHYUBtEhsSjBVn7mKp3fz6473a2xxy75SUKZ0rxjNXSZ8Q5rnFmkX0HTH2Sg51mtjH6aC2pfheQnA2t193BnSg="},
-    {"dns01.example.com", "NSEC", "dns02.example.com. A RRSIG NSEC"},
-    {"dns01.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. EkyeshmMNP9xiAz6mDFDIwksTdmkF9zsFzLuVKAgK6eUk7St6tp5PSvjA8nWol0vdvvz4LK85a4ffTFEiNRyvWeYP2vOhEkyDcrwuCd8Vc3jh/8Sm1Js+nX7hJStrZGFvp2TWPpt9nKH5p3MxXvTb/YVurnue0xSeFAE17O3+I0="},
-
-    // dns02.example.com
-    {"dns02.example.com", "A", "192.0.2.2"},
-    {"dns02.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. XJtVMbUIRE0mk6Hn/Nx6k36jaxaBDPK2/IYB6vCQjJETz6gW4T6q/H/eY9/Lsw5iYPFhoBRDxT4XFj575t98kELXnJe1WhuMbRPlOhyOjxkLECaUne/sbFPOtbGFx9ohuojI0RgxxZiCFaO8wJuv6nfPuzmlLajWS6z9NZeOMIk="},
-    {"dns02.example.com", "NSEC", "dns03.example.com. A RRSIG NSEC"},
-    {"dns02.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. imBNTMB3sPU4kblcaAH6V7lCVt5xgtAybi3DA/SbLEulLaV2NE6vcoEn/AieaM4mOJicQnUDj/H+1hSEhzxU2tRM8zfVlvztxQWn6eh7ZR4mKfNDSvRUGU9ykhpwMyC7wjOt1j5bcSA/OTnLRAilslnJyOM4bSaxVEFo8YPjncY="},
-
-    // dns03.example.com
-    {"dns03.example.com", "A", "192.0.2.3"},
-    {"dns03.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. Ubrcm1H+F6m8khle7P9zU8eO+Jtuj+1Vx1MM5KAkmZPJwQe9uTcoCpQa6DXOGG9kajDTnNN1Be1gkZuJDTZJG4SmJLXLbNY3RDnxpGmWta3qs/VgDq78/YM8ropt1/s7YKyrCfGE2ff+FUB0mLObiG01ZV2gu5HJzgE7SEWLEiI="},
-    {"dns03.example.com", "NSEC", "foo.example.com. A RRSIG NSEC"},
-    {"dns03.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com.  nn829Xw5CJFnPHwI9WHeT5epQv+odtCkHnjlPFGoPTLOyiks+041UmMqtq3uiSp4d2meMSe9UuDvoROT0L6NTtQQvVqiDhTn0irTFw1uw7fO8ZTG7eyu6Ypfz0+HvfbNvd4kMoD2OTgADRXPVsCTwK+PBOIIG9YTEQfl8pCqW5g="},
-
-    // www.example.com
-    {"www.example.com", "A", "192.0.2.1"},
-    {"www.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. qyFyyV/mE8x4pdhudr5iycwhDsva31MzwO1kBR+bDKvzJg8mN8KxlPZrOlNNUhd3YRXQVwieMyxOTWRPXoxrNEDkNwimXkfe3rrHY7ibV9eNS4OIBUjb44VjCNr9CmQSzfuQ2yxO2r+YIuPYHRCjieD4xh6t9ay4IaCN/tDAJ+Q="},
-    {"www.example.com", "NSEC", "example.com. A RRSIG NSEC"},
-    {"www.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. ZLZlSVBa2oe4U+7SZASnypP2VkI5gg1/1cVGqYUvfYNIUkcVMWDgn7DZCfpmo+2vdlV/4VhAc+sjDd+X+e57XGnW8+lqZHvG6NMMhmSGmeATD3D+8lEJJGo0dxoN4rHJQyp/eT2S4nChz+D/ze+YRagYxGF7pXm9zcrw3kKZGTs="},
-
-    // *.wild.example.com
-    {"*.wild.example.com", "A", "192.0.2.2"},
-    {"*.wild.example.com", "RRSIG", "A 5 3 3600 20100322084538 20100220084538 33495 example.com. FdO+UWONgtLKFxUzzygGunw67F9y8SzsP7yOLEYVJclRR8X3Ii62L0gtQHq2y0TcKsXttRsD6XY+tM5P/pgXlTNi7Bk4Fgb0PIDPjOsfT4DrS80kWn0YbinM/4/FA1j5ru5sTTboOY5UGhvDnoA9ogNuQQYb2/3wkoH0PrA2Q/0="},
-    {"*.wild.example.com", "NSEC", "*.wild2.example.com. A RRSIG NSEC"},
-    {"*.wild.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. OoGYslRj4xjZnBuzgOqsrvkDAHWycmQzbUxCRmgWnCbXiobJK7/ynONH3jm8G3vGlU0lwpHkhNs6cUK+6Nu8W49X3MT0Xksl/brroLcXYLi3vfxnYUNMMpXdeFl6WNNfoJRo90F/f/TWXAClRrDS29qiG3G1PEJZikIxZsZ0tyM="},
-
-    // *.wild2.example.com
-    {"*.wild2.example.com", "CNAME", "www.example.com"},
-    {"*.wild2.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="},
-    {"*.wild2.example.com", "NSEC", "*.wild3.example.com. CNAME RRSIG NSEC"},
-    {"*.wild2.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
-
-    // *.wild3.example.com -- a wildcard record with a lame CNAME
-    {"*.wild3.example.com", "CNAME", "spork.example.com"},
-    {"*.wild3.example.com", "RRSIG", "CNAME 5 3 3600 20100410212307 20100311212307 33495 example.com. pGHtGdRBi4GKFSKszi6SsKvuBLDX8dFhZubU0tMojQ9SJuiFNF+WtxvdAYuUaoWP/9VLUaYmiw5u7JnzmR84DiXZPEs6DtD+UJdOZhaS7V7RTpE+tMOfVQBLpUnRWYtlTTmiBpFquzf3DdIxgUFhEPEuJJyp3LFRxJObCaq9 nvI="},
-    {"*.wild3.example.com", "NSEC", "www.example.com. CNAME RRSIG NSEC"},
-    {"*.wild3.example.com", "RRSIG", "NSEC 5 3 7200 20100410212307 20100311212307 33495 example.com. EuSzh6or8mbvwru2H7fyYeMpW6J8YZ528rabU38V/lMN0TdamghIuCneAvSNaZgwk2MSN1bWpZqB2kAipaM/ZI9/piLlTvVjjOQ8pjk0auwCEqT7Z7Qng3E92O9yVzO+WHT9QZn/fR6t60392In4IvcBGjZyjzQk8njIwbui xGA="},
-
-    // foo.example.com
-    {"foo.example.com", "CNAME", "cnametest.example.net"},
-    {"foo.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. DSqkLnsh0gCeCPVW/Q8viy9GNP+KHmFGfWqyVG1S6koBtGN/VQQ16M4PHZ9Zssmf/JcDVJNIhAChHPE2WJiaPCNGTprsaUshf1Q2vMPVnkrJKgDY8SVRYMptmT8eaT0gGri4KhqRoFpMT5OYfesybwDgfhFSQQAh6ps3bIUsy4o="},
-    {"foo.example.com", "NSEC", "mail.example.com. CNAME RRSIG NSEC"},
-    {"foo.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. RTQwlSqui6StUYye1KCSOEr1d3irndWFqHBpwP7g7n+w8EDXJ8I7lYgwzHvlQt6BLAxe5fUDi7ct8M5hXvsm7FoWPZ5wXH+2/eJUCYxIw4vezKMkMwBP6M/YkJ2CMqY8DppYf60QaLDONQAr7AcK/naSyioeI5h6eaoVitUDMso="},
-
-    // cname-int.example.com
-    {"cname-int.example.com", "CNAME", "www.example.com."},
-    {"cname-int.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. U1wjt0XY9xjTwvUmWSUcfLGMhCjfX2ylWfHrycy50x2oxcK9z94E1ejen9wDTIEBSGYgi6wpZ8RK0+02N1DWTGpDqNXd7aFRfDrWQJ/q/XJHDx0vlcmhkWhrT82LBfKxkrptOzchuSo/c0mpK+mpiIMc1VOwY+yuQ2ALfcD6EHw="},
-    {"cname-int.example.com", "NSEC", "dname.example.com. CNAME RRSIG NSEC"},
-    {"cname-int.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. rbV+gaxfrsoha59NOLF4EFyWQ+GuFCVK/8D77x1atan3HNlXBlZ1smgudKTaJ3CtlobIDt0MEdPxY1yn2Tskw/5mlP1PWf8oaP3BwGSQdn4gLI8+sMpNOPFEdXpxqxngm2F6/7fqniL1QuSAQBEdO+5UiCAgnncPmAsSJg3u1zg="},
-
-    // cname-ext.example.com
-    {"cname-ext.example.com", "CNAME", "www.sql1.example.com"},
-    {"cname-ext.example.com", "RRSIG", "CNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. bGPIuZilyygvTThK4BrdECuaBcnZUgW/0d09iN2CrNjckchQl3dtbnMNirFsVs9hShDSldRNlQpiAVMpnPgXHhReNum7jmX6yqIH6s8GKIo91zr3VL/ramlezie5w4MilDHrxXLK2pb8IHmP+ZHivQ2EtdYQZgETWBWxr5FDfwk="},
-    {"cname-ext.example.com", "NSEC", "cname-int.example.com. CNAME RRSIG NSEC"},
-    {"cname-ext.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. inWsFwSDWG7TakjwbUTzTRpXz0WifelA5Kn3ABk6BVirIPmd+yQoNj2QZBDFAQwhnLPlNws2Oo4vgMsBMyx1Fv5eHgMUuCN3DUDaLlzlPtUb42CjOUa+jZBeTV/Hd7WZrirluASE1QFDprLdSSqoPPfAKvN3pORtW7y580dMOIM="},
-
-    // dname.example.com
-    {"dname.example.com", "DNAME", "sql1.example.com."},
-    {"dname.example.com", "RRSIG", "DNAME 5 3 3600 20100322084538 20100220084538 33495 example.com. ae8U47oaiwWdurkSyzcsCAF6DxBqjukizwF7K7U6lQVMtfoUE14oiAqfj1fjH8YLDOO/Hd1twrd/u0vgjnI1Gg32YTi7cYOzwE912SV1u2B/y0awaQKWPBwOW0aI7vxelt1vMUF81xosiQD04gOIdDBTqbHKcDxum87iWbhk4Ug="},
-    {"dname.example.com", "NSEC", "dns01.example.com. DNAME RRSIG NSEC"},
-    {"dname.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. c21Fff2D8vBrLzohBnUeflkaRdUAnUxAFGp+UQ0miACDCMOFBlCS9v9g/2+orOnKfd3l4vyz55C310t8JXgXb119ofaZWj2zkdUe+X8Bax+sMS0Y5K/sUhSNvbJbozr9UYPdvjSVBiWgh3s9fsb+etKq9uFukAzGU/FuGYpO0r0="},
-
-    // subzone.example.com
-    {"subzone.example.com", "NS", "ns1.subzone.example.com"},
-    {"subzone.example.com", "NS", "ns2.subzone.example.com"},
-    {"subzone.example.com", "NSEC", "*.wild.example.com. NS DS RRSIG NSEC"},
-    {"subzone.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. Oe2kgIhsLtPJ4+lDZDxznV8/vEVoXKOBFN9lwWyebaKa19BaSXlQ+YVejmulmKDDjEucMvEfuItfn6w7bnU+DzOLk5D1lJCjwDlKz8u3xOAx16TiuQn4bgQAOiFtBQygmGGqO3BVpX+jxsmw7eH3emofy8uUqr/C4aopnwuf28g="},
-    {"subzone.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"},
-    {"subzone.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"},
-    {"subzone.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="},
-
-    // subset of child zone: sql1
-    {"sql1.example.com", "NS", "dns01.example.com"},
-    {"sql1.example.com", "NS", "dns02.example.com"},
-    {"sql1.example.com", "NS", "dns03.example.com"},
-
-    {"sql1.example.com", "DS", "33313 5 1 0FDD7A2C11AA7F55D50FBF9B7EDDA2322C541A8D"},
-    {"sql1.example.com", "DS", "33313 5 2 00B99B7006F496D135B01AB17EDB469B4BE9E1973884DEA757BC4E3015A8C3AB"},
-    {"sql1.example.com", "RRSIG", "DS 5 3 3600 20100322084538 20100220084538 33495 example.com. dIqZKvpkJN1l92SOiWgJh3KbjErIN+EfojMsm4pEdV5xQdZwj6DNNEu6Kw4rRwdvrZIu0TyqPr3jSJb7o6R7vZgZzmLfVV/ojQah7rwuYHCFcfyZ4JyK2311fMhRR1QAvMsdcjdyA1XC140Cm6AnL3cH5rh/KUks/0ec3Ca7GNQ="},
-    {"sql1.example.com", "NSEC", "subzone.example.com. NS DS RRSIG NSEC"},
-    {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084538 20100220084538 33495 example.com. k9FRdFyk/cPdkmmaoZbGZPpzIzfbFWQ3QCHd2qhJa0xAXaEOT/GBL6aFqx9SlunDu2wgES+To5fWPZGi4NzWpp6c5t27rnATN/oCEQ/UYIJKmWbqrXdst0Ps5boznk7suK2Y+km31KxaIf3fDd/T3kZCVsR0aWKRRRatPb7GfLw="},
-
-    {NULL, NULL, NULL}
-};
-
-const struct RRData example_com_glue_records[] = {
-    {"ns1.subzone.example.com", "A", "192.0.2.1"},
-    {"ns2.subzone.example.com", "A", "192.0.2.2"},
-    {NULL, NULL, NULL}
-};
-
-//
-// zone data for sql1.example.com
-//
-const struct RRData sql1_example_com_records[] = {
-    {"sql1.example.com", "NS", "dns01.example.com"},
-    {"sql1.example.com", "NS", "dns02.example.com"},
-    {"sql1.example.com", "NS", "dns03.example.com"},
-    {"sql1.example.com", "RRSIG", "NS 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. 0CL8noy0NSgoWwuKd+Dc6vyIIw2BrAEBx0IJzcSB6GlB25x/zjEd6AJG0be13HN6jOaTX8iWTuCVrEYuXg76V+M4EvTZHjEScj0az74TrDv4Vdo459paGKCX9B8NLJW1mW4fzZrrXQ8jmBEZeS91Q5rJrO+UKJEuUz3LYdTPvao="},
-    {"sql1.example.com", "SOA", "master.example.com. admin.example.com. 678 3600 1800 2419200 7200"},
-    {"sql1.example.com", "RRSIG", "SOA 5 3 3600 20100322084536 20100220084536 12447 sql1.example.com. oakulfyljL/RAKgCKXEZ3KsG8BJj5WG4JK4moWFB6c9OKem6jIk8hKP2XlUVXFuOYJlRdIM4KicmR2GAK+5jJp6z5ShssstYTXo3QosVm6oCKumuFeLFHzcjfqP1D+F9NsvHldJIBnS/4ebPkmR5OENyCZXQF5HmN2awIj4CLjE="},
-    {"sql1.example.com", "NSEC", "www.sql1.example.com. NS SOA RRSIG NSEC DNSKEY"},
-    {"sql1.example.com", "RRSIG", "NSEC 5 3 7200 20100322084536 20100220084536 12447 sql1.example.com. v71CgdTYccCiTqfRcn6HsvISQa8ruvUfCKtpwym0RW/G27xlZn8otj2IMtWwkLxti8Rqqu+PTViLaOIbeVfHBcqzAd7U59cAOYoq3ODZx6auiE3C23HAKqUavKcP7Esaajm1cbcWy6Kyie4CAZc8M7EeKxgkXMKJGqBQzF+/FOo="},
-
-    // www.sql1.example.com
-    {"www.sql1.example.com", "A", "192.0.2.2"},
-    {"www.sql1.example.com", "RRSIG", "A 5 4 3600 20100322084536 20100220084536 12447 sql1.example.com. DNdVKxB3oBsB14NPoV9WG14Y/g4zMcIXLYnFjj9vRZRZJpAvbTEipiXlayuhOxnqU827OipETQyeULZmLsqIQ1wK4Fgf+9b5aJ8D85/o4wBka00X4hZ3MwDPRb4mjuogwBTBg5NRpNSzUfbkPGiav08BFwgg+Efm9veSB05arS0="},
-    {"www.sql1.example.com", "NSEC", "sql1.example.com. A RRSIG NSEC"},
-    {"www.sql1.example.com", "RRSIG", "NSEC 5 4 7200 20100322084536 20100220084536 12447 sql1.example.com. cJMJhDx/ND7/9j3zhyXe+6eaSsU7ByYpXhJzbe+OhjFgH0VasQXq7o1QB3I293UZ+yhkjgXap+9QtPlraaNaYyTyOMQ42OoxSefJpYz9CME/FI2tsUfyrCnLFxYRNet7sMS0q+hLqxRayuEHDFDp72hHPGLJQ8a7jq4SrIonT50="},
-
-    {NULL, NULL, NULL}
-};
-
-//
-// zone data for loop.example
-//
-const struct RRData loop_example_records[] = {
-    {"loop.example", "SOA", "master.loop.example admin.loop.example. "
-     "1234 3600 1800 2419200 7200"},
-    {"loop.example", "NS", "ns.loop.example"},
-    {"one.loop.example", "CNAME", "two.loop.example"},
-    {"two.loop.example", "CNAME", "one.loop.example"},
-    {NULL, NULL, NULL}
-};
-
-//
-// zone data for nons.example
-//
-const struct RRData nons_example_records[] = {
-    {"nons.example", "SOA", "master.nons.example admin.nons.example. "
-     "1234 3600 1800 2419200 7200"},
-    {"www.nons.example", "A", "192.0.2.1"},
-    {"ns.nons.example", "A", "192.0.2.2"},
-
-    // One of the NS names is intentionally non existent in the zone it belongs
-    // to.  This delegation is used to see if we still return the NS and the
-    // existent glue.
-    // (These are not relevant to test the case for the "no NS" case.  We use
-    // this zone to minimize the number of test zones)
-    {"incompletechild.nons.example", "NS", "ns.incompletechild.nons.example"},
-    {"incompletechild.nons.example", "NS", "nx.nosoa.example"},
-
-    {NULL, NULL, NULL}
-};
-
-const struct RRData nons_example_glue_records[] = {
-    {"ns.incompletechild.nons.example", "A", "192.0.2.1"},
-    {NULL, NULL, NULL}
-};
-
-//
-// zone data for nons-dname.example
-//
-const struct RRData nonsdname_example_records[] = {
-    {"nons-dname.example", "SOA", "master.nons-dname.example "
-     "admin.nons-dname.example. 1234 3600 1800 2419200 7200"},
-    {"nons-dname.example", "DNAME", "example.org"},
-    {"www.nons-dname.example", "A", "192.0.2.1"},
-    {"ns.nons-dname.example", "A", "192.0.2.2"},
-    {NULL, NULL, NULL}
-};
-
-//
-// zone data for nosoa.example
-//
-const struct RRData nosoa_example_records[] = {
-    {"nosoa.example", "NS", "ns.nosoa.example"},
-    {"www.nosoa.example", "A", "192.0.2.1"},
-    {"ns.nosoa.example", "A", "192.0.2.2"},
-    {NULL, NULL, NULL}
-};
-
-//
-// zone data for apexcname.example.
-//
-const struct RRData apexcname_example_records[] = {
-    {"apexcname.example", "CNAME", "canonical.apexcname.example"},
-    {"canonical.apexcname.example", "SOA",
-     "master.apexcname.example "
-     "admin.apexcname.example. 1234 3600 1800 2419200 7200"},
-    {NULL, NULL, NULL}
-};
-
-
-//
-// empty data set, for convenience.
-//
-const struct RRData empty_records[] = {
-    {NULL, NULL, NULL}
-};
-
-//
-// test zones
-//
-const struct ZoneData zone_data[] = {
-    { "example.com", "IN", example_com_records, example_com_glue_records },
-    { "sql1.example.com", "IN", sql1_example_com_records, empty_records },
-    { "loop.example", "IN", loop_example_records, empty_records },
-    { "nons.example", "IN", nons_example_records, nons_example_glue_records },
-    { "nons-dname.example", "IN", nonsdname_example_records, empty_records },
-    { "nosoa.example", "IN", nosoa_example_records, empty_records },
-    { "apexcname.example", "IN", nosoa_example_records, empty_records }
-};
-const size_t NUM_ZONES = sizeof(zone_data) / sizeof(zone_data[0]);
-
-struct Zone {
-    Zone(const char* const name, const char* const class_txt) :
-        zone_name(Name(name)), rrclass(class_txt)
-    {}
-    Name zone_name;
-    RRClass rrclass;
-    vector<Name> names;
-    vector<RRsetPtr> rrsets;
-};
-vector<Zone> zones;
-}
-
-DataSrc::Result
-TestDataSrc::init(isc::data::ConstElementPtr) {
-    return (init());
-}
-
-void
-buildZone(Zone& zone, const RRData* records, const bool is_glue) {
-    RRsetPtr prev_rrset;
-    for (int i = 0; records[i].name != NULL; ++i) {
-        Name name(records[i].name);
-        RRType rrtype(records[i].rrtype);
-        RRsetPtr rrset;
-        bool new_name = false;
-
-        if (!prev_rrset || prev_rrset->getName() != name) {
-            if (!is_glue) {
-                zone.names.push_back(name);
-            }
-            new_name = true;
-        }
-
-        if (new_name || prev_rrset->getType() != rrtype) {
-            rrset = RRsetPtr(new RRset(name, zone.rrclass, rrtype,
-                                       RRTTL(TEST_TTL)));
-            if (rrtype != RRType::RRSIG()) {
-                zone.rrsets.push_back(rrset);
-            }
-        } else {
-            rrset = prev_rrset;
-        }
-        rrset->addRdata(createRdata(rrtype, zone.rrclass, records[i].rdata));
-        if (rrtype == RRType::RRSIG()) {
-            prev_rrset->addRRsig(rrset);
-        } else {
-            prev_rrset = rrset;
-        }
-    }
-}
-
-DataSrc::Result
-TestDataSrc::init() {
-    if (initialized) {
-        return (SUCCESS);
-    }
-
-    if (zones.empty()) {
-        for (int i = 0; i < NUM_ZONES; ++i) {
-            Zone zone(zone_data[i].zone_name, zone_data[i].rrclass);
-            buildZone(zone, zone_data[i].records, false);
-            buildZone(zone, zone_data[i].glue_records, true);
-            sort(zone.names.begin(), zone.names.end());
-            zones.push_back(zone);
-        }
-    }
-
-    initialized = true;
-    return (SUCCESS);
-}
-
-void
-TestDataSrc::findClosestEnclosure(DataSrcMatch& match) const {
-    const Name& qname = match.getName();
-
-    if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
-        return;
-    }
-
-    vector<Zone>::const_iterator it;
-    vector<Zone>::const_iterator best_it = zones.end();
-    unsigned int best_common_labels = 0;
-    for (it = zones.begin(); it != zones.end(); ++it) {
-        const NameComparisonResult cmp = qname.compare(it->zone_name);
-        const NameComparisonResult::NameRelation reln = cmp.getRelation();
-
-        if ((reln == NameComparisonResult::EQUAL ||
-             reln == NameComparisonResult::SUBDOMAIN) &&
-            cmp.getCommonLabels() > best_common_labels) {
-            best_it = it;
-            best_common_labels = cmp.getCommonLabels();
-        }
-    }
-
-    if (best_it != zones.end()) {
-        match.update(*this, best_it->zone_name);
-    }
-}
-
-struct ZoneNameMatch : public unary_function<Name, bool> {
-    ZoneNameMatch(const Name& name) : name_(name) {}
-    bool operator()(const Zone& zone) const {
-        return (zone.zone_name == name_);
-    }
-    const Name& name_;
-};
-
-// XXX: the main data source module can override the returned RRset.
-// That's bad and should be fixed (Trac #254), but for now we work around it.
-RRsetPtr
-copyRRset(RRsetPtr const source) {
-    RRsetPtr rrset = RRsetPtr(new RRset(source->getName(), source->getClass(),
-                                        source->getType(), source->getTTL()));
-    RdataIteratorPtr it = source->getRdataIterator();
-    for (; !it->isLast(); it->next()) {
-        rrset->addRdata(it->getCurrent());
-    }
-    if (source->getRRsig()) {
-        rrset->addRRsig(copyRRset(source->getRRsig()));
-    }
-
-    return (rrset);
-}
-
-class TestDataSrc::RRsetMatch {
-public:
-    struct MatchResult {
-        MatchResult(const bool name_found, const bool has_delegation) :
-            name_found_(name_found), has_delegation_(has_delegation)
-        {}
-        bool name_found_;
-        bool has_delegation_;
-    };
-    RRsetMatch(const Name& name, const RRType& rrtype, const Mode mode,
-               RRsetList& target, uint32_t& flags) :
-        name_(name), rrtype_(rrtype), mode_(mode), target_(target),
-        flags_(flags), name_found_(false), has_delegation_(false)
-    {}
-    void operator()(const RRsetPtr& rrset) {
-        if (rrset->getName() != name_) {
-            return;
-        }
-        name_found_ = true;
-
-        if (rrset->getType() == RRType::NS() ||
-            rrset->getType() == RRType::DNAME()) {
-            has_delegation_ = true;
-        }
-
-        if (mode_ == DELEGATION) {
-            if (rrset->getType() == RRType::NS() ||
-                rrset->getType() == RRType::DNAME() ||
-                rrset->getType() == RRType::DS()) {
-                target_.addRRset(copyRRset(rrset));
-            }
-        } else if (mode_ == ADDRESS) {
-            if (rrset->getType() == RRType::A() ||
-                rrset->getType() == RRType::AAAA()) {
-                target_.addRRset(copyRRset(rrset));
-            }
-        } else {
-            if (rrtype_ == RRType::NSEC() &&
-                rrset->getType() == RRType::CNAME()) {
-                // XXX: ignore CNAME if the qtype is NSEC.
-                // tricky, but necessary.
-                return;
-            }
-            if (rrtype_ == RRType::ANY() || rrtype_ == rrset->getType() ||
-                rrset->getType() == RRType::CNAME() ||
-                rrset->getType() == RRType::DNAME()) {
-                target_.addRRset(copyRRset(rrset));
-                if (rrset->getType() == RRType::CNAME()) {
-                    flags_ |= CNAME_FOUND;
-                }
-                if (rrset->getType() == RRType::DNAME()) {
-                    flags_ |= REFERRAL;
-                }
-            }
-        }
-        
-    }
-    MatchResult getResult() { return (MatchResult(name_found_,
-                                                  has_delegation_)); }
-    const Name& name_;
-    const RRType& rrtype_;
-    const Mode mode_;
-    RRsetList& target_;
-    uint32_t& flags_;
-    bool name_found_;
-    bool has_delegation_;
-};
-
-void
-TestDataSrc::findRecords(const Name& name, const RRType& rdtype,
-                         RRsetList& target, const Name* zonename,
-                         const Mode mode, uint32_t& flags) const
-{
-    flags = 0;
-
-    assert(zonename != NULL);
-
-    vector<Zone>::const_iterator zone = find_if(zones.begin(), zones.end(),
-                                                ZoneNameMatch(*zonename));
-    if (zone == zones.end()) {
-        return;
-    }
-
-    const RRsetMatch::MatchResult match_result =
-        for_each(zone->rrsets.begin(), zone->rrsets.end(),
-                 RRsetMatch(name, rdtype, mode, target, flags)).getResult();
-    if (match_result.has_delegation_) {
-        flags |= REFERRAL;
-    }
-    if (target.size() == 0) {
-        if (match_result.name_found_) {
-            flags |= TYPE_NOT_FOUND;
-        } else {
-            flags |= NAME_NOT_FOUND;
-        }
-    }
-}
-
-DataSrc::Result
-TestDataSrc::findRRset(const Name& qname,
-                       const RRClass& qclass,
-                       const RRType& qtype,
-                       RRsetList& target,
-                       uint32_t& flags,
-                       const Name* zonename) const
-{
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        return (ERROR);
-    }
-
-    findRecords(qname, qtype, target, zonename, NORMAL, flags);
-    return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findExactRRset(const Name& qname,
-                            const RRClass& qclass,
-                            const RRType& qtype,
-                            RRsetList& target,
-                            uint32_t& flags,
-                            const Name* zonename) const
-{
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        return (ERROR);
-    }
-
-    findRecords(qname, qtype, target, zonename, NORMAL, flags);
-    // Ignore referrals in this case
-    flags &= ~REFERRAL;
-
-    // CNAMEs don't count in this case
-    if ((flags & CNAME_FOUND) != 0) {
-        flags &= ~CNAME_FOUND;
-        flags |= TYPE_NOT_FOUND;
-    }
-
-    return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findAddrs(const Name& qname,
-                       const RRClass& qclass,
-                       RRsetList& target,
-                       uint32_t& flags,
-                       const Name* zonename) const
-{
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        return (ERROR);
-    }
-
-    findRecords(qname, RRType::ANY(), target, zonename, ADDRESS, flags);
-    return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findReferral(const Name& qname,
-                          const RRClass& qclass,
-                          RRsetList& target,
-                          uint32_t& flags,
-                          const Name* zonename) const
-{
-    if (qclass != getClass() && qclass != RRClass::ANY()) {
-        return (ERROR);
-    }
-
-    findRecords(qname, RRType::ANY(), target, zonename, DELEGATION, flags);
-    return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findPreviousName(const Name& qname,
-                              Name& target,
-                              const Name* zonename) const
-{
-    assert(zonename != NULL);
-
-    vector<Zone>::const_iterator zone = find_if(zones.begin(), zones.end(),
-                                                ZoneNameMatch(*zonename));
-    if (zone == zones.end()) {
-        return (ERROR);
-    }
-
-    if (zone->names.empty()) {
-        return (ERROR);
-    }
-
-    // if found, next_name >= qname.
-    vector<Name>::const_iterator next_name =
-        lower_bound(zone->names.begin(), zone->names.end(), qname);
-    if (next_name == zone->names.end()) {
-        // if no such name was found, the previous name is the last name.
-        target = zone->names.back();
-    } else if (*next_name == qname) {
-        target = *next_name;
-    } else if (next_name == zone->names.begin()) {
-        // if qname < first_name, the "previous name" is the last name.
-        target = zone->names.back();
-    } else {
-        // otherwise, qname and next_name share the same previous name.
-        target = *(next_name - 1);
-    }
-    return (SUCCESS);
-}
-
-DataSrc::Result
-TestDataSrc::findCoveringNSEC3(const Name&, string&, RRsetList&) const {
-    return (NOT_IMPLEMENTED);
-}
-
-}
-}
diff --git a/src/lib/datasrc/tests/test_datasrc.h b/src/lib/datasrc/tests/test_datasrc.h
deleted file mode 100644
index d0d67fc..0000000
--- a/src/lib/datasrc/tests/test_datasrc.h
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright (C) 2010  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 __TEST_DATA_SOURCE_H
-#define __TEST_DATA_SOURCE_H
-
-#include <gtest/gtest.h>
-
-#include <datasrc/data_source.h>
-
-namespace isc {
-
-namespace dns {
-class Name;
-class RRClass;
-class RRType;
-class RRsetList;
-}
-
-namespace datasrc {
-class Query;
-
-class TestDataSrc : public DataSrc {
-    ///
-    /// \name Constructors, Assignment Operator and Destructor.
-    ///
-    /// Note: The copy constructor and the assignment operator are intentionally
-    /// defined as private.
-    //@{
-private:
-    TestDataSrc(const TestDataSrc& source);
-    TestDataSrc operator=(const TestDataSrc& source); 
-public:
-    TestDataSrc() : initialized(false) {}
-    ~TestDataSrc() {}
-    //@}
-
-    void findClosestEnclosure(DataSrcMatch& match) const;
-
-    Result findRRset(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     const isc::dns::RRType& qtype,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findExactRRset(const isc::dns::Name& qname,
-                          const isc::dns::RRClass& qclass,
-                          const isc::dns::RRType& qtype,
-                          isc::dns::RRsetList& target,
-                          uint32_t& flags,
-                          const isc::dns::Name* zonename) const;
-
-    Result findAddrs(const isc::dns::Name& qname,
-                     const isc::dns::RRClass& qclass,
-                     isc::dns::RRsetList& target,
-                     uint32_t& flags,
-                     const isc::dns::Name* zonename) const;
-
-    Result findReferral(const isc::dns::Name& qname,
-                        const isc::dns::RRClass& qclass,
-                        isc::dns::RRsetList& target,
-                        uint32_t& flags,
-                        const isc::dns::Name* zonename) const;
-
-    Result findPreviousName(const isc::dns::Name& qname,
-                            isc::dns::Name& target,
-                            const isc::dns::Name* zonename) const;
-
-    Result findCoveringNSEC3(const isc::dns::Name& zonename,
-                             std::string& hash,
-                             isc::dns::RRsetList& target) const;
-
-    Result init();
-    Result init(isc::data::ConstElementPtr config);
-    Result close() { return (SUCCESS); }
-
-private:
-    bool initialized;
-    enum Mode {
-        NORMAL,
-        ADDRESS,
-        DELEGATION
-    };
-    class RRsetMatch;
-
-    void findRecords(const isc::dns::Name& name, const isc::dns::RRType& rdtype,
-                     isc::dns::RRsetList& target,
-                     const isc::dns::Name* zonename, const Mode mode,
-                     uint32_t& flags) const;
-};
-
-}
-}
-
-#endif
-
-// Local Variables: 
-// mode: c++
-// End: 
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
index e499e0d..0c1393c 100644
--- a/src/lib/datasrc/tests/testdata/contexttest.zone
+++ b/src/lib/datasrc/tests/testdata/contexttest.zone
@@ -1,9 +1,10 @@
 ;; test zone file used for ZoneFinderContext tests.
 ;; RRSIGs are (obviouslly) faked ones for testing.
 
-example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 67 3600 300 3600000 3600
+example.org. 3600 IN SOA	ns1.example.org. bugs.x.w.example.org. 74 3600 300 3600000 3600
 example.org.			      3600 IN NS	ns1.example.org.
 example.org.			      3600 IN NS	ns2.example.org.
+example.org.			      3600 IN RRSIG	NS 7 3 3600 20150420235959 20051021000000 40430 example.org. FAKEFAKEFAKE
 example.org.			      3600 IN MX	1 mx1.example.org.
 example.org.			      3600 IN MX	2 mx2.example.org.
 example.org.			      3600 IN MX	3 mx.a.example.org.
@@ -71,6 +72,9 @@ a.*.emptywild.example.org.    3600 IN AAAA	2001:db8::2
 ;; expansion
 *.wildmx.example.org. 3600 IN MX 1 mx1.example.org.
 
+;; the owner name of additional for an answer RRset (MX) has DNAME
+dnamemx.example.org. 3600 IN MX 1 dname.example.org.
+
 ;; CNAME
 alias.example.org. 3600 IN CNAME cname.example.org.
 
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index ea02543..14429ae 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -14,12 +14,14 @@
 
 #include <exceptions/exceptions.h>
 
+#include <util/memory_segment_local.h>
+
 #include <dns/masterload.h>
 #include <dns/name.h>
 #include <dns/rrclass.h>
 
 #include <datasrc/zone.h>
-#include <datasrc/memory_datasrc.h>
+#include <datasrc/memory/memory_client.h>
 #include <datasrc/database.h>
 #include <datasrc/sqlite3_accessor.h>
 
@@ -39,8 +41,10 @@
 using namespace std;
 using boost::shared_ptr;
 
+using namespace isc::util;
 using namespace isc::dns;
 using namespace isc::datasrc;
+using isc::datasrc::memory::InMemoryClient;
 using namespace isc::testutils;
 
 namespace {
@@ -54,18 +58,16 @@ typedef shared_ptr<DataSourceClient> DataSourceClientPtr;
 // This is the type used as the test parameter.  Note that this is
 // intentionally a plain old type (i.e. a function pointer), not a class;
 // otherwise it could cause initialization fiasco at the instantiation time.
-typedef DataSourceClientPtr (*ClientCreator)(RRClass, const Name&);
+typedef DataSourceClientPtr (*ClientCreator)(MemorySegment&, RRClass,
+                                             const Name&);
 
 // Creator for the in-memory client to be tested
 DataSourceClientPtr
-createInMemoryClient(RRClass zclass, const Name& zname) {
-    shared_ptr<InMemoryClient> client(new InMemoryClient);
-
-    shared_ptr<InMemoryZoneFinder> finder(
-        new InMemoryZoneFinder(zclass, zname));
-    finder->load(TEST_ZONE_FILE);
-
-    client->addZone(finder);
+createInMemoryClient(MemorySegment& mem_sgmt, RRClass zclass,
+                     const Name& zname)
+{
+    shared_ptr<InMemoryClient> client(new InMemoryClient(mem_sgmt, zclass));
+    client->load(zname, TEST_ZONE_FILE);
 
     return (client);
 }
@@ -76,7 +78,7 @@ addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
 }
 
 DataSourceClientPtr
-createSQLite3Client(RRClass zclass, const Name& zname) {
+createSQLite3Client(MemorySegment&, RRClass zclass, const Name& zname) {
     // We always begin with an empty template SQLite3 DB file and install
     // the zone data from the zone file to ensure both cases have the
     // same test data.
@@ -103,7 +105,7 @@ class ZoneFinderContextTest :
 {
 protected:
     ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
-        client_ = (*GetParam())(qclass_, qzone_);
+        client_ = (*GetParam())(mem_sgmt_, qclass_, qzone_);
         REQUESTED_A.push_back(RRType::A());
         REQUESTED_AAAA.push_back(RRType::AAAA());
         REQUESTED_BOTH.push_back(RRType::A());
@@ -114,6 +116,7 @@ protected:
         ASSERT_TRUE(finder_);
     }
 
+    MemorySegmentLocal mem_sgmt_;
     const RRClass qclass_;
     const Name qzone_;
     DataSourceClientPtr client_;
@@ -232,6 +235,18 @@ TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithDname) {
                 result_sets_.begin(), result_sets_.end());
 }
 
+TEST_P(ZoneFinderContextTest, getAdditionalOnDname) {
+    // The additional name has a DNAME as well as the additional record.
+    // The existence of DNAME shouldn't hide the additional record.
+    ZoneFinderContextPtr ctx = finder_->find(Name("dnamemx.example.org"),
+                                             RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("dname.example.org. 3600 IN A 192.0.2.12\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
 TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithEmptyName) {
     // One of NS names is at an empty non terminal node.  It shouldn't cause
     // any disruption.
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index 2330412..36a1cff 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -168,48 +168,25 @@ public:
     /// can define a derived class of the base Context and override the
     /// specific virtual method.
     ///
+    /// This base class defines these common protected methods along with
+    /// some helper pure virtual methods that would be necessary for the
+    /// common methods.  If a derived class wants to use the common version
+    /// of the protected method, it needs to provide expected result through
+    /// their implementation of the pure virtual methods.
+    ///
     /// This class object is generally expected to be associated with the
     /// ZoneFinder that originally performed the \c find() call, and expects
     /// the finder is valid throughout the lifetime of this object.  It's
     /// caller's responsibility to ensure that assumption.
     class Context {
     public:
-        /// \brief The constructor for the normal find call.
-        ///
-        /// This constructor is expected to be called from the \c find()
-        /// method when it constructs the return value.
+        /// \brief The constructor.
         ///
-        /// \param finder The ZoneFinder on which find() is called.
         /// \param options The find options specified for the find() call.
         /// \param result The result of the find() call.
-        Context(ZoneFinder& finder, FindOptions options,
-                const ResultContext& result) :
-            code(result.code), rrset(result.rrset),
-            finder_(finder), flags_(result.flags), options_(options)
-        {}
-
-        /// \brief The constructor for the normal findAll call.
-        ///
-        /// This constructor is expected to be called from the \c findAll()
-        /// method when it constructs the return value.
-        ///
-        /// It copies the vector that is to be returned to the caller of
-        /// \c findAll() for possible subsequent use.  Note that it cannot
-        /// simply hold a reference to the vector because the caller may
-        /// alter it after the \c findAll() call.
-        ///
-        /// \param finder The ZoneFinder on which findAll() is called.
-        /// \param options The find options specified for the findAll() call.
-        /// \param result The result of the findAll() call (whose rrset is
-        ///        expected to be NULL).
-        /// \param all_set Reference to the vector given by the caller of
-        ///       \c findAll(), storing the RRsets to be returned.
-        Context(ZoneFinder& finder, FindOptions options,
-                const ResultContext& result,
-                const std::vector<isc::dns::ConstRRsetPtr> &all_set) :
+        Context(FindOptions options, const ResultContext& result) :
             code(result.code), rrset(result.rrset),
-            finder_(finder), flags_(result.flags), options_(options),
-            all_set_(all_set)
+            flags_(result.flags), options_(options)
         {}
 
         /// \brief The destructor.
@@ -292,21 +269,108 @@ public:
         }
 
     protected:
+        /// \brief Return the \c ZoneFinder that created this \c Context.
+        ///
+        /// A derived class implementation can return NULL if it defines
+        /// other protected methods that require a non NULL result from
+        /// this method.  Otherwise it must return a valid, non NULL pointer
+        /// to the \c ZoneFinder object.
+        ///
+        /// When returning non NULL, the ownership of the pointed object
+        /// was not transferred to the caller; it cannot be assumed to be
+        /// valid after the originating \c Context object is destroyed.
+        /// Also, the caller must not try to delete the returned object.
+        virtual ZoneFinder* getFinder() = 0;
+
+        /// \brief Return a vector of RRsets corresponding to findAll() result.
+        ///
+        /// This method returns a set of RRsets that correspond to the
+        /// returned RRsets to a prior \c findAll() call.
+        ///
+        /// A derived class implementation can return NULL if it defines
+        /// other protected methods that require a non NULL result from
+        /// this method.  Otherwise it must return a valid, non NULL pointer
+        /// to a vector that correspond to the expected set of RRsets.
+        ///
+        /// When returning non NULL, the ownership of the pointed object
+        /// was not transferred to the caller; it cannot be assumed to be
+        /// valid after the originating \c Context object is destroyed.
+        /// Also, the caller must not try to delete the returned object.
+        virtual const std::vector<isc::dns::ConstRRsetPtr>*
+        getAllRRsets() const = 0;
+
         /// \brief Actual implementation of getAdditional().
         ///
         /// This base class defines a default implementation that can be
         /// used for any type of data sources.  A data source implementation
         /// can override it.
+        ///
+        /// The default version of this implementation requires both
+        /// \c getFinder() and \c getAllRRsets() return valid results.
         virtual void getAdditionalImpl(
             const std::vector<isc::dns::RRType>& requested_types,
             std::vector<isc::dns::ConstRRsetPtr>& result);
 
     private:
-        ZoneFinder& finder_;
         const FindResultFlags flags_;
     protected:
         const FindOptions options_;
+    };
+
+    /// \brief Generic ZoneFinder context that works for all implementations.
+    ///
+    /// This is a concrete derived class of \c ZoneFinder::Context that
+    /// only use the generic (default) versions of the protected methods
+    /// and therefore work for any data source implementation.
+    ///
+    /// A data source implementation can use this class to create a
+    /// \c Context object as a return value of \c find() or \c findAll()
+    /// method if it doesn't have to optimize specific protected methods.
+    class GenericContext : public Context {
+    public:
+        /// \brief The constructor for the normal find call.
+        ///
+        /// This constructor is expected to be called from the \c find()
+        /// method when it constructs the return value.
+        ///
+        /// \param finder The ZoneFinder on which find() is called.
+        /// \param options See the \c Context class.
+        /// \param result See the \c Context class.
+        GenericContext(ZoneFinder& finder, FindOptions options,
+                       const ResultContext& result) :
+            Context(options, result), finder_(finder)
+        {}
+
+        /// \brief The constructor for the normal findAll call.
+        ///
+        /// This constructor is expected to be called from the \c findAll()
+        /// method when it constructs the return value.
+        ///
+        /// It copies the vector that is to be returned to the caller of
+        /// \c findAll() for possible subsequent use.  Note that it cannot
+        /// simply hold a reference to the vector because the caller may
+        /// alter it after the \c findAll() call.
+        ///
+        /// \param finder The ZoneFinder on which findAll() is called.
+        /// \param options See the \c Context class.
+        /// \param result See the \c Context class.
+        /// \param all_set Reference to the vector given by the caller of
+        ///       \c findAll(), storing the RRsets to be returned.
+        GenericContext(ZoneFinder& finder, FindOptions options,
+                       const ResultContext& result,
+                       const std::vector<isc::dns::ConstRRsetPtr>& all_set) :
+            Context(options, result), finder_(finder), all_set_(all_set)
+        {}
+
+    protected:
+        virtual ZoneFinder* getFinder() { return (&finder_); }
+        virtual const std::vector<isc::dns::ConstRRsetPtr>*
+        getAllRRsets() const {
+            return (&all_set_);
+        }
+
     private:
+        ZoneFinder& finder_;
         std::vector<isc::dns::ConstRRsetPtr> all_set_;
     };
 
diff --git a/src/lib/datasrc/zone_finder_context.cc b/src/lib/datasrc/zone_finder_context.cc
index 7913d71..482eb65 100644
--- a/src/lib/datasrc/zone_finder_context.cc
+++ b/src/lib/datasrc/zone_finder_context.cc
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <exceptions/exceptions.h>
+
 #include <dns/rdata.h>
 #include <dns/rrset.h>
 #include <dns/rrtype.h>
@@ -87,13 +89,22 @@ ZoneFinder::Context::getAdditionalImpl(const vector<RRType>& requested_types,
 {
     // If rrset is non NULL, it should have been SUCCESS/DELEGATION; otherwise
     // we should have responded to type ANY query.
+    ZoneFinder* finder = getFinder();
+    if (finder == NULL) {
+        // This is a bug of the derived class implementation.
+        isc_throw(isc::Unexpected, "NULL ZoneFinder in finder Context");
+    }
     if (rrset) {
-        getAdditionalForRRset(finder_, *rrset, requested_types, result,
+        getAdditionalForRRset(*finder, *rrset, requested_types, result,
                               options_);
         return;
     }
-    BOOST_FOREACH(ConstRRsetPtr rrset_in_set, all_set_) {
-        getAdditionalForRRset(finder_, *rrset_in_set, requested_types, result,
+    const vector<ConstRRsetPtr>* all_sets = getAllRRsets();
+    if (all_sets == NULL) {     // bug of the derived class implementation.
+        isc_throw(isc::Unexpected, "All RRsets is NULL in finder Context");
+    }
+    BOOST_FOREACH(ConstRRsetPtr rrset_in_set, *getAllRRsets()) {
+        getAdditionalForRRset(*finder, *rrset_in_set, requested_types, result,
                               options_);
     }
 }
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index fbd4eb5..4c22b35 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -13,7 +13,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 CLEANFILES = *.gcno *.gcda
 
-lib_LTLIBRARIES = libb10-dhcp++.la
+lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
 libb10_dhcp___la_SOURCES  =
 libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
 libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
@@ -29,8 +29,18 @@ libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
 libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
 
+libb10_dhcpsrv_la_SOURCES  = cfgmgr.cc cfgmgr.h
+libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
+libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
+libb10_dhcpsrv_la_SOURCES += triplet.h
+libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
+libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
+libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 2:0:0
+
 EXTRA_DIST  = README
-#EXTRA_DIST += log_messages.mes
 
 libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
diff --git a/src/lib/dhcp/README b/src/lib/dhcp/README
index 6bd6384..c5e70f2 100644
--- a/src/lib/dhcp/README
+++ b/src/lib/dhcp/README
@@ -1,11 +1,9 @@
-This directory holds implementation for libdhcp++.
+This directory holds implementation for DHCP libraries:
 
+libdhcp++ - this is a generic purpose DHCP library. Please be careful
+what is put here. It is going to be shared by various servers (the "regular"
+one and the embedded as well), clients, relays and performance tools.
 
-Basic Ideas
-===========
+libdhcpsrv - Server specific code goes in here. It will be used by
+dhcp4 and dhcp6 server.
 
-
-Notes
-=====
-This work just begun. Don't expect to see much useful code here.
-We are working on it.
diff --git a/src/lib/dhcp/addr_utilities.cc b/src/lib/dhcp/addr_utilities.cc
new file mode 100644
index 0000000..ad72833
--- /dev/null
+++ b/src/lib/dhcp/addr_utilities.cc
@@ -0,0 +1,96 @@
+// 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 <string.h>
+#include <dhcp/addr_utilities.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                            uint8_t len) {
+
+    const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+    uint8_t packed[V6ADDRESS_LEN];
+
+    // First we copy the whole address as 16 bytes.
+    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
+    // part. Otherwise we need to handle the byte that has to be partially
+    // zeroed.
+    if (len % 8 != 0) {
+
+        // Get the appropriate mask. It has relevant bits (those that should
+        // stay) set and irrelevant (those that should be wiped) cleared.
+        uint8_t mask = bitMask[len % 8];
+
+        // Let's leave only whatever the mask says should not be cleared.
+        packed[len / 8] = packed[len / 8] & mask;
+
+        // Since we have just dealt with this byte, let's move the prefix length
+        // to the beginning of the next byte (len is expressed in bits).
+        len = (len / 8 + 1) * 8;
+    }
+
+    // Clear out the remaining bits.
+    for (int i = len / 8; i < sizeof(packed); ++i) {
+        packed[i] = 0x0;
+    }
+
+    // Finally, let's wrap this into nice and easy IOAddress object.
+    return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
+}
+
+isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                           uint8_t len) {
+
+    const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+    uint8_t packed[V6ADDRESS_LEN];
+
+    // First we copy the whole address as 16 bytes.
+    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
+    // with ones. Otherwise we need to handle the byte that has to be partially
+    // zeroed.
+    if (len % 8 != 0) {
+        // Get the appropriate mask. It has relevant bits (those that should
+        // stay) set and irrelevant (those that should be set to 1) cleared.
+        uint8_t mask = bitMask[len % 8];
+
+        // Let's set those irrelevant bits with 1. It would be perhaps
+        // easier to not use negation here and invert bitMask content. However,
+        // with this approach, we can use the same mask in first and last
+        // address calculations.
+        packed[len / 8] = packed[len / 8] | ~mask;
+
+        // Since we have just dealt with this byte, let's move the prefix length
+        // to the beginning of the next byte (len is expressed in bits).
+        len = (len / 8 + 1) * 8;
+    }
+
+    // Finally set remaining bits to 1.
+    for (int i = len / 8; i < sizeof(packed); ++i) {
+        packed[i] = 0xff;
+    }
+
+    // Finally, let's wrap this into nice and easy IOAddress object.
+    return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
+}
+
+};
+};
diff --git a/src/lib/dhcp/addr_utilities.h b/src/lib/dhcp/addr_utilities.h
new file mode 100644
index 0000000..15532d0
--- /dev/null
+++ b/src/lib/dhcp/addr_utilities.h
@@ -0,0 +1,53 @@
+// 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 <asiolink/io_address.h>
+
+namespace isc {
+namespace dhcp {
+
+/// This code is based on similar code from the Dibbler project. I, Tomasz Mrugalski,
+/// as a sole creator of that code hereby release it under BSD license for the benefit
+/// of the BIND10 project.
+
+/// @brief returns a first address in a given prefix
+///
+/// Example: For 2001:db8:1::deaf:beef and length /120 the function will return
+/// 2001:db8:1::dead:be00. See also @ref lastAddrInPrefix.
+///
+/// @todo It currently works for v6 only and will throw if v4 address is passed.
+///
+/// @param prefix and address that belongs to a prefix
+/// @param len prefix length
+///
+/// @return first address from a prefix
+isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                            uint8_t len);
+
+/// @brief returns a last address in a given prefix
+///
+/// Example: For 2001:db8:1::deaf:beef and length /112 the function will return
+/// 2001:db8:1::dead:ffff. See also @ref firstAddrInPrefix.
+///
+/// @todo It currently works for v6 only and will throw if v4 address is passed.
+///
+/// @param prefix and address that belongs to a prefix
+/// @param len prefix length
+///
+/// @return first address from a prefix
+isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                           uint8_t len);
+
+};
+};
diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc
new file mode 100644
index 0000000..15e3ad9
--- /dev/null
+++ b/src/lib/dhcp/cfgmgr.cc
@@ -0,0 +1,78 @@
+// 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 <asiolink/io_address.h>
+#include <dhcp/cfgmgr.h>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+
+
+
+CfgMgr&
+CfgMgr::instance() {
+    static CfgMgr cfg_mgr;
+    return (cfg_mgr);
+}
+
+Subnet6Ptr
+CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
+
+    // If there's only one subnet configured, let's just use it
+    // The idea is to keep small deployments easy. In a small network - one
+    // router that also runs DHCPv6 server. Users specifies a single pool and
+    // expects it to just work. Without this, the server would complain that it
+    // doesn't have IP address on its interfaces that matches that
+    // configuration. Such requirement makes sense in IPv4, but not in IPv6.
+    // The server does not need to have a global address (using just link-local
+    // is ok for DHCPv6 server) from the pool it serves.
+    if (subnets6_.size() == 1) {
+        return (subnets6_[0]);
+    }
+
+    // If there is more than one, we need to choose the proper one
+    for (Subnet6Collection::iterator subnet = subnets6_.begin();
+         subnet != subnets6_.end(); ++subnet) {
+        if ((*subnet)->inRange(hint)) {
+            return (*subnet);
+        }
+    }
+
+    // sorry, we don't support that subnet
+    return (Subnet6Ptr());
+}
+
+Subnet6Ptr CfgMgr::getSubnet6(OptionPtr /*interfaceId*/) {
+    /// @todo: Implement get subnet6 by interface-id (for relayed traffic)
+    isc_throw(NotImplemented, "Relayed DHCPv6 traffic is not supported yet.");
+}
+
+void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
+    /// @todo: Check that this new subnet does not cross boundaries of any
+    /// other already defined subnet.
+    subnets6_.push_back(subnet);
+}
+
+CfgMgr::CfgMgr() {
+}
+
+CfgMgr::~CfgMgr() {
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h
new file mode 100644
index 0000000..5b73f2b
--- /dev/null
+++ b/src/lib/dhcp/cfgmgr.h
@@ -0,0 +1,126 @@
+// 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 CFGMGR_H
+#define CFGMGR_H
+
+#include <string>
+#include <map>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <asiolink/io_address.h>
+#include <util/buffer.h>
+#include <dhcp/option.h>
+#include <dhcp/pool.h>
+#include <dhcp/subnet.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @brief Configuration Manager
+///
+/// This singleton class holds the whole configuration for DHCPv4 and DHCPv6
+/// servers. It currently holds information about zero or more subnets6.
+/// Each subnet may contain zero or more pools. Pool4 and Pool6 is the most
+/// basic "chunk" of configuration. It contains a range of assigneable
+/// addresses.
+///
+/// Below is a sketch of configuration inheritance (not implemented yet).
+/// Let's investigate the following configuration:
+///
+/// preferred-lifetime 500;
+/// valid-lifetime 1000;
+/// subnet6 2001:db8:1::/48 {
+///     pool6 2001::db8:1::1 - 2001::db8:1::ff;
+/// };
+/// subnet6 2001:db8:2::/48 {
+///     valid-lifetime 2000;
+///     pool6 2001::db8:2::1 - 2001::db8:2::ff;
+/// };
+/// Parameters defined in a global scope are applicable to everything until
+/// they are overwritten in a smaller scope, in this case subnet6.
+/// In the example above, the first subnet6 has preferred lifetime of 500s
+/// and a valid lifetime of 1000s. The second subnet has preferred lifetime
+/// of 500s, but valid lifetime of 2000s.
+///
+/// Parameter inheritance is likely to be implemented in configuration handling
+/// routines, so there is no storage capability in a global scope for
+/// subnet-specific parameters.
+///
+/// @todo: Implement Subnet4 support (ticket #2237)
+/// @todo: Implement option definition support
+/// @todo: Implement parameter inheritance
+class CfgMgr : public boost::noncopyable {
+public:
+
+    /// @brief returns a single instance of Configuration Manager
+    ///
+    /// CfgMgr is a singleton and this method is the only way of
+    /// accessing it.
+    static CfgMgr& instance();
+
+    /// @brief get subnet by address
+    ///
+    /// Finds a matching subnet, based on an address. This can be used
+    /// in two cases: when trying to find an appropriate lease based on
+    /// a) relay link address (that must be the address that is on link)
+    /// b) our global address on the interface the message was received on
+    ///    (for directly connected clients)
+    ///
+    /// @param hint an address that belongs to a searched subnet
+    Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
+
+    /// @brief get subnet by interface-id
+    ///
+    /// Another possibility to find a subnet is based on interface-id.
+    ///
+    /// @param interface_id content of interface-id option returned by a relay
+    /// @todo This method is not currently supported.
+    Subnet6Ptr getSubnet6(OptionPtr interface_id);
+
+    /// @brief adds a subnet6
+    void addSubnet6(const Subnet6Ptr& subnet);
+
+    /// @todo: Add subnet6 removal routines. Currently it is not possible
+    /// to remove subnets. The only case where subnet6 removal would be
+    /// needed is a dynamic server reconfiguration - a use case that is not
+    /// planned to be supported any time soon.
+protected:
+
+    /// @brief Protected constructor.
+    ///
+    /// This constructor is protected for 2 reasons. First, it forbids any
+    /// instantiations of this class (CfgMgr is a singleton). Second, it
+    /// allows derived class to instantiate it. That is useful for testing
+    /// purposes.
+    CfgMgr();
+
+    /// @brief virtual desctructor
+    virtual ~CfgMgr();
+
+    /// @brief a container for Subnet6
+    ///
+    /// That is a simple vector of pointers. It does not make much sense to
+    /// optimize access time (e.g. using a map), because typical search
+    /// pattern will use calling inRange() method on each subnet until
+    /// a match is found.
+    Subnet6Collection subnets6_;
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index e058960..002e74f 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -39,6 +39,9 @@
 namespace isc {
 namespace dhcp {
 
+/* IPv4 Broadcast address */
+#define DHCP_IPV4_BROADCAST_ADDRESS "255.255.255.255"
+
 /* BOOTP (rfc951) message types */
 enum BOOTPTypes {
     BOOTREQUEST = 1,
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index a2f807e..ecaa652 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -122,8 +122,6 @@ IfaceMgr::IfaceMgr()
      session_socket_(INVALID_SOCKET), session_callback_(NULL)
 {
 
-    cout << "IfaceMgr initialization." << endl;
-
     try {
         // required for sending/receiving packets
         // let's keep it in front, just in case someone
@@ -134,13 +132,7 @@ IfaceMgr::IfaceMgr()
         detectIfaces();
 
     } catch (const std::exception& ex) {
-        cout << "IfaceMgr creation failed:" << ex.what() << endl;
-
-        // TODO Uncomment this (or call LOG_FATAL) once
-        // interface detection is implemented. Otherwise
-        // it is not possible to run tests in a portable
-        // way (see detectIfaces() method).
-        throw;
+        isc_throw(IfaceDetectError, ex.what());
     }
 }
 
@@ -166,51 +158,34 @@ void IfaceMgr::stubDetectIfaces() {
     // is faked by detecting loopback interface (lo or lo0). It will eventually
     // be removed once we have actual implementations for all supported systems.
 
-    cout << "Interface detection is not implemented on this Operating System yet. "
-         << endl;
-
-    try {
-        if (if_nametoindex("lo") > 0) {
-            ifaceName = "lo";
-            // this is Linux-like OS
-        } else if (if_nametoindex("lo0") > 0) {
-            ifaceName = "lo0";
-            // this is BSD-like OS
-        } else {
-            // we give up. What OS is this, anyway? Solaris? Hurd?
-            isc_throw(NotImplemented,
-                      "Interface detection on this OS is not supported.");
-        }
-
-        Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
-        iface.flag_up_ = true;
-        iface.flag_running_ = true;
-
-        // Note that we claim that this is not a loopback. iface_mgr tries to open a
-        // socket on all interaces that are up, running and not loopback. As this is
-        // the only interface we were able to detect, let's pretend this is a normal
-        // interface.
-        iface.flag_loopback_ = false;
-        iface.flag_multicast_ = true;
-        iface.flag_broadcast_ = true;
-        iface.setHWType(HWTYPE_ETHERNET);
-
-        iface.addAddress(IOAddress(v4addr));
-        iface.addAddress(IOAddress(v6addr));
-        addInterface(iface);
-
-        cout << "Detected interface " << ifaceName << "/" << v4addr << "/"
-             << v6addr << endl;
-    } catch (const std::exception& ex) {
-        // TODO: deallocate whatever memory we used
-        // not that important, since this function is going to be
-        // thrown away as soon as we get proper interface detection
-        // implemented
-
-        // TODO Do LOG_FATAL here
-        std::cerr << "Interface detection failed." << std::endl;
-        throw;
+    if (if_nametoindex("lo") > 0) {
+        ifaceName = "lo";
+        // this is Linux-like OS
+    } else if (if_nametoindex("lo0") > 0) {
+        ifaceName = "lo0";
+        // this is BSD-like OS
+    } else {
+        // we give up. What OS is this, anyway? Solaris? Hurd?
+        isc_throw(NotImplemented,
+                  "Interface detection on this OS is not supported.");
     }
+
+    Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
+    iface.flag_up_ = true;
+    iface.flag_running_ = true;
+
+    // Note that we claim that this is not a loopback. iface_mgr tries to open a
+    // socket on all interaces that are up, running and not loopback. As this is
+    // the only interface we were able to detect, let's pretend this is a normal
+    // interface.
+    iface.flag_loopback_ = false;
+    iface.flag_multicast_ = true;
+    iface.flag_broadcast_ = true;
+    iface.setHWType(HWTYPE_ETHERNET);
+
+    iface.addAddress(IOAddress(v4addr));
+    iface.addAddress(IOAddress(v6addr));
+    addInterface(iface);
 }
 
 bool IfaceMgr::openSockets4(const uint16_t port) {
@@ -221,13 +196,9 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
          iface != ifaces_.end();
          ++iface) {
 
-        cout << "Trying opening socket on interface " << iface->getFullName() << endl;
-
         if (iface->flag_loopback_ ||
             !iface->flag_up_ ||
             !iface->flag_running_) {
-            cout << "Interface " << iface->getFullName()
-                 << " not suitable: is loopback, is down or not running" << endl;
             continue;
         }
 
@@ -243,15 +214,13 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
 
             sock = openSocket(iface->getName(), *addr, port);
             if (sock < 0) {
-                cout << "Failed to open unicast socket." << endl;
-                return (false);
+                isc_throw(SocketConfigError, "failed to open unicast socket");
             }
 
             count++;
         }
     }
     return (count > 0);
-
 }
 
 bool IfaceMgr::openSockets6(const uint16_t port) {
@@ -280,8 +249,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
 
             sock = openSocket(iface->getName(), *addr, port);
             if (sock < 0) {
-                cout << "Failed to open unicast socket." << endl;
-                return (false);
+                isc_throw(SocketConfigError, "failed to open unicast socket");
             }
 
             // Binding socket to unicast address and then joining multicast group
@@ -290,7 +258,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
             if ( !joinMulticast(sock, iface->getName(),
                                 string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS))) {
                 close(sock);
-                isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+                isc_throw(SocketConfigError, "Failed to join "
+                          << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                           << " multicast group.");
             }
 
@@ -305,7 +274,7 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    port);
             if (sock2 < 0) {
-                isc_throw(Unexpected, "Failed to open multicast socket on "
+                isc_throw(SocketConfigError, "Failed to open multicast socket on "
                           << " interface " << iface->getFullName());
                 iface->delSocket(sock); // delete previously opened socket
             }
@@ -418,7 +387,7 @@ int IfaceMgr::openSocketFromIface(const std::string& ifname,
                 family_name = "AF_INET6";
             }
             // We did not find address on the interface.
-            isc_throw(BadValue, "There is no address for interface: "
+            isc_throw(SocketConfigError, "There is no address for interface: "
                       << ifname << ", port: " << port << ", address "
                       " family: " << family_name);
         }
@@ -460,9 +429,13 @@ int IfaceMgr::openSocketFromAddress(const IOAddress& addr,
 
 int IfaceMgr::openSocketFromRemoteAddress(const IOAddress& remote_addr,
                                           const uint16_t port) {
-    // Get local address to be used to connect to remote location.
-    IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
-    return openSocketFromAddress(local_address, port);
+    try {
+        // Get local address to be used to connect to remote location.
+        IOAddress local_address(getLocalAddress(remote_addr, port).getAddress());
+        return openSocketFromAddress(local_address, port);
+    } catch (const Exception& e) {
+        isc_throw(SocketConfigError, e.what());
+    }
 }
 
 isc::asiolink::IOAddress
@@ -483,7 +456,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
     // If remote address is broadcast address we have to
     // allow this on the socket.
     if (remote_addr.getAddress().is_v4() &&
-        (remote_addr == IOAddress("255.255.255.255"))) {
+        (remote_addr == IOAddress(DHCP_IPV4_BROADCAST_ADDRESS))) {
         // Socket has to be open prior to setting the broadcast
         // option. Otherwise set_option will complain about
         // bad file descriptor.
@@ -506,7 +479,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
     sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
     if (err_code) {
         sock.close();
-        isc_throw(Unexpected,"failed to connect to remote endpoint.");
+        isc_throw(Unexpected, "failed to connect to remote endpoint.");
     }
 
     // Once we are connected socket object holds local endpoint.
@@ -523,9 +496,6 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
 
 int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 
-    cout << "Creating UDP4 socket on " << iface.getFullName()
-         << " " << addr.toText() << "/port=" << port << endl;
-
     struct sockaddr_in addr4;
     memset(&addr4, 0, sizeof(sockaddr));
     addr4.sin_family = AF_INET;
@@ -537,12 +507,12 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 
     int sock = socket(AF_INET, SOCK_DGRAM, 0);
     if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create UDP6 socket.");
+        isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
     }
 
     if (bind(sock, (struct sockaddr *)&addr4, sizeof(addr4)) < 0) {
         close(sock);
-        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+        isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
                   << "/port=" << port);
     }
 
@@ -552,13 +522,10 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
     int flag = 1;
     if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &flag, sizeof(flag)) != 0) {
         close(sock);
-        isc_throw(Unexpected, "setsockopt: IP_PKTINFO: failed.");
+        isc_throw(SocketConfigError, "setsockopt: IP_PKTINFO: failed.");
     }
 #endif
 
-    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
-        addr.toText() << "/port=" << port << endl;
-
     SocketInfo info(sock, addr, port);
     iface.addSocket(info);
 
@@ -567,9 +534,6 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 
 int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
 
-    cout << "Creating UDP6 socket on " << iface.getFullName()
-         << " " << addr.toText() << "/port=" << port << endl;
-
     struct sockaddr_in6 addr6;
     memset(&addr6, 0, sizeof(addr6));
     addr6.sin6_family = AF_INET6;
@@ -590,7 +554,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
     // make a socket
     int sock = socket(AF_INET6, SOCK_DGRAM, 0);
     if (sock < 0) {
-        isc_throw(Unexpected, "Failed to create UDP6 socket.");
+        isc_throw(SocketConfigError, "Failed to create UDP6 socket.");
     }
 
     // Set the REUSEADDR option so that we don't fail to start if
@@ -599,12 +563,12 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                    (char *)&flag, sizeof(flag)) < 0) {
         close(sock);
-        isc_throw(Unexpected, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
+        isc_throw(SocketConfigError, "Can't set SO_REUSEADDR option on dhcpv6 socket.");
     }
 
     if (bind(sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
         close(sock);
-        isc_throw(Unexpected, "Failed to bind socket " << sock << " to " << addr.toText()
+        isc_throw(SocketConfigError, "Failed to bind socket " << sock << " to " << addr.toText()
                   << "/port=" << port);
     }
 #ifdef IPV6_RECVPKTINFO
@@ -612,14 +576,14 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
-        isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
+        isc_throw(SocketConfigError, "setsockopt: IPV6_RECVPKTINFO failed.");
     }
 #else
     // RFC2292 - an old way
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
-        isc_throw(Unexpected, "setsockopt: IPV6_PKTINFO: failed.");
+        isc_throw(SocketConfigError, "setsockopt: IPV6_PKTINFO: failed.");
     }
 #endif
 
@@ -632,14 +596,11 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
         if ( !joinMulticast( sock, iface.getName(),
                          string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
             close(sock);
-            isc_throw(Unexpected, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
+            isc_throw(SocketConfigError, "Failed to join " << ALL_DHCP_RELAY_AGENTS_AND_SERVERS
                       << " multicast group.");
         }
     }
 
-    cout << "Created socket " << sock << " on " << iface.getName() << "/" <<
-        addr.toText() << "/port=" << port << endl;
-
     SocketInfo info(sock, addr, port);
     iface.addSocket(info);
 
@@ -654,20 +615,15 @@ const std::string & mcast) {
 
     if (inet_pton(AF_INET6, mcast.c_str(),
                   &mreq.ipv6mr_multiaddr) <= 0) {
-        cout << "Failed to convert " << ifname
-             << " to IPv6 multicast address." << endl;
         return (false);
     }
 
     mreq.ipv6mr_interface = if_nametoindex(ifname.c_str());
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
                    &mreq, sizeof(mreq)) < 0) {
-        cout << "Failed to join " << mcast << " multicast group." << endl;
         return (false);
     }
 
-    cout << "Joined multicast " << mcast << " group." << endl;
-
     return (true);
 }
 
@@ -746,13 +702,8 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
 
     result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
-        isc_throw(Unexpected, "Pkt6 send failed: sendmsg() returned " << result);
+        isc_throw(SocketWriteError, "Pkt6 send failed: sendmsg() returned " << result);
     }
-    cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
-         << " on " << iface->getFullName() << " interface: "
-         << " dst=[" << pkt->getRemoteAddr().toText() << "]:" << pkt->getRemotePort()
-         << ", src=" << pkt->getLocalAddr().toText() << "]:" << pkt->getLocalPort()
-         << endl;
 
     return (result);
 }
@@ -797,24 +748,13 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
     // call OS-specific routines (like setting interface index)
     os_send4(m, control_buf_, control_buf_len_, pkt);
 
-    cout << "Trying to send " << pkt->getBuffer().getLength() << " bytes to "
-         << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
-         << " over socket " << getSocket(*pkt) << " on interface "
-         << getIface(pkt->getIface())->getFullName() << endl;
-
     pkt->updateTimestamp();
 
     int result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
-        isc_throw(Unexpected, "Pkt4 send failed.");
+        isc_throw(SocketWriteError, "pkt4 send failed");
     }
 
-    cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
-         << " on " << iface->getFullName() << " interface: "
-         << " dst=" << pkt->getRemoteAddr().toText() << ":" << pkt->getRemotePort()
-         << ", src=" << pkt->getLocalAddr().toText() << ":" << pkt->getLocalPort()
-         << endl;
-
     return (result);
 }
 
@@ -869,27 +809,18 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     select_timeout.tv_sec = timeout_sec;
     select_timeout.tv_usec = timeout_usec;
 
-    cout << "Trying to receive data on sockets: " << names.str()
-         << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
-         << timeout_usec << " seconds." << endl;
     int result = select(maxfd + 1, &sockets, NULL, NULL, &select_timeout);
-    cout << "select returned " << result << endl;
 
     if (result == 0) {
         // nothing received and timeout has been reached
         return (Pkt4Ptr()); // NULL
     } else if (result < 0) {
-        cout << "Socket read error: " << strerror(errno) << endl;
-
-        /// @todo: perhaps throw here?
-        return (Pkt4Ptr()); // NULL
+        isc_throw(SocketReadError, strerror(errno));
     }
 
     // Let's find out which socket has the data
     if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
         // something received over session socket
-        cout << "BIND10 command or config available over session socket." << endl;
-
         if (session_callback_) {
             // in theory we could call io_service.run_one() here, instead of
             // implementing callback mechanism, but that would introduce
@@ -918,14 +849,9 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     }
 
     if (!candidate) {
-        cout << "Received data over unknown socket." << endl;
-        return (Pkt4Ptr()); // NULL
+        isc_throw(SocketReadError, "received data over unknown socket");
     }
 
-    cout << "Trying to receive over UDP4 socket " << candidate->sockfd_ << " bound to "
-         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
-         << iface->getFullName() << endl;
-
     // Now we have a socket, let's get some data from it!
     struct sockaddr_in from_addr;
     uint8_t buf[RCVBUFSIZE];
@@ -958,8 +884,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
 
     result = recvmsg(candidate->sockfd_, &m, 0);
     if (result < 0) {
-        cout << "Failed to receive UDP4 data." << endl;
-        return (Pkt4Ptr()); // NULL
+        isc_throw(SocketReadError, "failed to receive UDP4 data");
     }
 
     // We have all data let's create Pkt4 object.
@@ -982,15 +907,9 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
     pkt->setLocalPort(candidate->port_);
 
     if (!os_receive4(m, pkt)) {
-        cout << "Unable to find pktinfo" << endl;
-        return (boost::shared_ptr<Pkt4>()); // NULL
+        isc_throw(SocketReadError, "unable to find pktinfo");
     }
 
-    cout << "Received " << result << " bytes from " << from.toText()
-         << "/port=" << from_port
-         << " sent to " << pkt->getLocalAddr().toText() << " over interface "
-         << iface->getFullName() << endl;
-
     return (pkt);
 }
 
@@ -1039,10 +958,6 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
         names << session_socket_ << "(session)";
     }
 
-    cout << "Trying to receive data on sockets: " << names.str()
-         << ". Timeout is " << timeout_sec << "." << setw(6) << setfill('0')
-         << timeout_usec << " seconds." << endl;
-
     struct timeval select_timeout;
     select_timeout.tv_sec = timeout_sec;
     select_timeout.tv_usec = timeout_usec;
@@ -1053,17 +968,12 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
         // nothing received and timeout has been reached
         return (Pkt6Ptr()); // NULL
     } else if (result < 0) {
-        cout << "Socket read error: " << strerror(errno) << endl;
-
-        /// @todo: perhaps throw here?
-        return (Pkt6Ptr()); // NULL
+        isc_throw(SocketReadError, strerror(errno));
     }
 
     // Let's find out which socket has the data
     if ((session_socket_ != INVALID_SOCKET) && (FD_ISSET(session_socket_, &sockets))) {
         // something received over session socket
-        cout << "BIND10 command or config available over session socket." << endl;
-
         if (session_callback_) {
             // in theory we could call io_service.run_one() here, instead of
             // implementing callback mechanism, but that would introduce
@@ -1092,14 +1002,9 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     }
 
     if (!candidate) {
-        cout << "Received data over unknown socket." << endl;
-        return (Pkt6Ptr()); // NULL
+        isc_throw(SocketReadError, "received data over unknown socket");
     }
 
-    cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
-         << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
-         << iface->getFullName() << endl;
-
     // Now we have a socket, let's get some data from it!
     uint8_t buf[RCVBUFSIZE];
     memset(&control_buf_[0], 0, control_buf_len_);
@@ -1163,12 +1068,10 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
             cmsg = CMSG_NXTHDR(&m, cmsg);
         }
         if (!found_pktinfo) {
-            cout << "Unable to find pktinfo" << endl;
-            return (Pkt6Ptr()); // NULL
+            isc_throw(SocketReadError, "unable to find pktinfo");
         }
     } else {
-        cout << "Failed to receive data." << endl;
-        return (Pkt6Ptr()); // NULL
+        isc_throw(SocketReadError, "failed to receive data");
     }
 
     // Let's create a packet.
@@ -1176,8 +1079,7 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     try {
         pkt = Pkt6Ptr(new Pkt6(buf, result));
     } catch (const std::exception& ex) {
-        cout << "Failed to create new packet." << endl;
-        return (Pkt6Ptr()); // NULL
+        isc_throw(SocketReadError, "failed to create new packet");
     }
 
     pkt->updateTimestamp();
@@ -1193,18 +1095,10 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
     if (received) {
         pkt->setIface(received->getName());
     } else {
-        cout << "Received packet over unknown interface (ifindex="
-             << pkt->getIndex() << ")." << endl;
-        return (boost::shared_ptr<Pkt6>()); // NULL
+        isc_throw(SocketReadError, "received packet over unknown interface"
+                  << "(ifindex=" << pkt->getIndex() << ")");
     }
 
-    /// @todo: Move this to LOG_DEBUG
-    cout << "Received " << pkt->getBuffer().getLength() << " bytes over "
-         << pkt->getIface() << "/" << pkt->getIndex() << " interface: "
-         << " src=" << pkt->getRemoteAddr().toText()
-         << ", dst=" << pkt->getLocalAddr().toText()
-         << endl;
-
     return (pkt);
 }
 
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 98ce672..a7b9a78 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -28,6 +28,38 @@
 namespace isc {
 
 namespace dhcp {
+
+/// @brief IfaceMgr exception thrown thrown when interface detection fails.
+class IfaceDetectError : public Exception {
+public:
+    IfaceDetectError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when socket opening
+/// or configuration failed.
+class SocketConfigError : public Exception {
+public:
+    SocketConfigError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when error occured during
+/// reading data from socket.
+class SocketReadError : public Exception {
+public:
+    SocketReadError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief IfaceMgr exception thrown thrown when error occured during
+/// sedning data through socket.
+class SocketWriteError : public Exception {
+public:
+    SocketWriteError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
 /// @brief handles network interfaces, transmission and reception
 ///
 /// IfaceMgr is an interface manager class that detects available network
@@ -340,6 +372,8 @@ public:
     ///
     /// @param pkt packet to be sent
     ///
+    /// @throw isc::BadValue if invalid interface specified in the packet.
+    /// @throw isc::dhcp::SocketWriteError if sendmsg() failed to send packet.
     /// @return true if sending was successful
     bool send(const Pkt6Ptr& pkt);
 
@@ -351,6 +385,8 @@ public:
     ///
     /// @param pkt a packet to be sent
     ///
+    /// @throw isc::BadValue if invalid interface specified in the packet.
+    /// @throw isc::dhcp::SocketWriteError if sendmsg() failed to send packet.
     /// @return true if sending was successful
     bool send(const Pkt4Ptr& pkt);
 
@@ -369,6 +405,7 @@ public:
     /// (in microseconds)
     ///
     /// @throw isc::BadValue if timeout_usec is greater than one million
+    /// @throw isc::dhcp::SocketReadError if error occured when receiving a packet.
     /// @return Pkt6 object representing received packet (or NULL)
     Pkt6Ptr receive6(uint32_t timeout_sec, uint32_t timeout_usec = 0);
 
@@ -383,6 +420,7 @@ public:
     /// (in microseconds)
     ///
     /// @throw isc::BadValue if timeout_usec is greater than one million
+    /// @throw isc::dhcp::SocketReadError if error occured when receiving a packet.
     /// @return Pkt4 object representing received packet (or NULL)
     Pkt4Ptr receive4(uint32_t timeout_sec, uint32_t timeout_usec = 0);
 
@@ -460,6 +498,7 @@ public:
     ///
     /// @param port specifies port number (usually DHCP6_SERVER_PORT)
     ///
+    /// @throw SocketOpenFailure if tried and failed to open socket.
     /// @return true if any sockets were open
     bool openSockets6(const uint16_t port = DHCP6_SERVER_PORT);
 
@@ -472,6 +511,7 @@ public:
     ///
     /// @param port specifies port number (usually DHCP4_SERVER_PORT)
     ///
+    /// @throw SocketOpenFailure if tried and failed to open socket.
     /// @return true if any sockets were open
     bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT);
 
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index 3c62d72..189fe90 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -417,8 +417,6 @@ namespace dhcp {
 /// Uses the socket-based netlink protocol to retrieve the list of interfaces
 /// from the Linux kernel.
 void IfaceMgr::detectIfaces() {
-    cout << "Linux: detecting interfaces." << endl;
-
     // Copies of netlink messages about links will be stored here.
     Netlink::NetlinkMessages link_info;
 
@@ -495,8 +493,6 @@ void IfaceMgr::detectIfaces() {
 
     nl.release_list(link_info);
     nl.release_list(addr_info);
-
-    printIfaces();
 }
 
 /// @brief sets flag_*_ fields.
@@ -558,10 +554,7 @@ bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) {
             // broadcast. This will return broadcast address, not
             // the address we are bound to.
 
-            // IOAddress tmp(htonl(pktinfo->ipi_spec_dst.s_addr));
-            // cout << "The other addr is: " << tmp.toText() << endl;
-
-            // Perhaps we should uncomment this:
+            // XXX: Perhaps we should uncomment this:
             // to_addr = pktinfo->ipi_spec_dst;
         }
         cmsg = CMSG_NXTHDR(&m, cmsg);
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 22cd47b..12e1bbd 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -72,26 +72,23 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
         offset += 2;
 
         if (offset + opt_len > end) {
-            cout << "Option " << opt_type << " truncated." << endl;
+            // @todo: consider throwing exception here.
             return (offset);
         }
         OptionPtr opt;
         switch (opt_type) {
         case D6O_IA_NA:
         case D6O_IA_PD:
-            // cout << "Creating Option6IA" << endl;
             opt = OptionPtr(new Option6IA(opt_type,
                                           buf.begin() + offset,
                                           buf.begin() + offset + opt_len));
             break;
         case D6O_IAADDR:
-            // cout << "Creating Option6IAAddr" << endl;
             opt = OptionPtr(new Option6IAAddr(opt_type,
                                               buf.begin() + offset,
                                               buf.begin() + offset + opt_len));
             break;
         default:
-            // cout << "Creating Option" << endl;
             opt = OptionPtr(new Option(Option::V6,
                                        opt_type,
                                        buf.begin() + offset,
@@ -151,15 +148,9 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
 
 void LibDHCP::packOptions6(isc::util::OutputBuffer &buf,
                            const isc::dhcp::Option::OptionCollection& options) {
-    try {
-        for (Option::OptionCollection::const_iterator it = options.begin();
-             it != options.end(); ++it) {
-            it->second->pack(buf);
-        }
-    }
-    catch (const Exception&) {
-        cout << "Packet build failed (Option build failed)." << endl;
-        throw;
+    for (Option::OptionCollection::const_iterator it = options.begin();
+         it != options.end(); ++it) {
+        it->second->pack(buf);
     }
 }
 
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 2c3f1eb..405277d 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -197,8 +197,6 @@ void Pkt4::check() {
 }
 
 void Pkt4::repack() {
-    cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
-
     bufferOut_.writeData(&data_[0], data_.size());
 }
 
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index e869c7b..330c97f 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -100,7 +100,7 @@ Pkt6::packUDP() {
         LibDHCP::packOptions6(bufferOut_, options_);
     }
     catch (const Exception& e) {
-        cout << "Packet build failed:" << e.what() << endl;
+        /// @todo: throw exception here once we turn this function to void.
         return (false);
     }
     return (true);
@@ -129,8 +129,8 @@ Pkt6::unpack() {
 bool
 Pkt6::unpackUDP() {
     if (data_.size() < 4) {
-        std::cout << "DHCPv6 packet truncated. Only " << data_.size()
-                  << " bytes. Need at least 4." << std::endl;
+        // @todo: throw exception here informing that packet is truncated
+        // once we turn this function to void.
         return (false);
     }
     msg_type_ = data_[0];
@@ -143,7 +143,7 @@ Pkt6::unpackUDP() {
 
         LibDHCP::unpackOptions6(opt_buffer, options_);
     } catch (const Exception& e) {
-        cout << "Packet parsing failed:" << e.what() << endl;
+        // @todo: throw exception here once we turn this function to void.
         return (false);
     }
     return (true);
@@ -197,8 +197,6 @@ Pkt6::delOption(uint16_t type) {
 }
 
 void Pkt6::repack() {
-    cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
-
     bufferOut_.writeData(&data_[0], data_.size());
 }
 
diff --git a/src/lib/dhcp/pool.cc b/src/lib/dhcp/pool.cc
new file mode 100644
index 0000000..da8a2e3
--- /dev/null
+++ b/src/lib/dhcp/pool.cc
@@ -0,0 +1,87 @@
+// 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 <asiolink/io_address.h>
+#include <dhcp/addr_utilities.h>
+#include <dhcp/pool.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+Pool::Pool(const isc::asiolink::IOAddress& first,
+           const isc::asiolink::IOAddress& last)
+    :id_(getNextID()), first_(first), last_(last) {
+}
+
+bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
+    return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
+}
+
+Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+             const isc::asiolink::IOAddress& last)
+    :Pool(first, last), type_(type), prefix_len_(0) {
+
+    // check if specified address boundaries are sane
+    if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
+        isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
+    }
+
+    if (last < first) {
+        isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
+        // This check is a bit strict. If we decide that it is too strict,
+        // we need to comment it and uncomment lines below.
+        // On one hand, letting the user specify 2001::f - 2001::1 is nice, but
+        // on the other hand, 2001::1 may be a typo and the user really meant
+        // 2001::1:0 (or 1 followed by some hex digit), so a at least a warning
+        // would be useful.
+
+        // first_  = last;
+        // last_ = first;
+    }
+
+
+    // TYPE_PD is not supported by this constructor. first-last style
+    // parameters are for IA and TA only. There is another dedicated
+    // constructor for that (it uses prefix/length)
+    if ((type != TYPE_IA) && (type != TYPE_TA)) {
+        isc_throw(BadValue, "Invalid Pool6 type specified");
+    }
+}
+
+Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
+             uint8_t prefix_len)
+    :Pool(prefix, IOAddress("::")),
+     type_(type), prefix_len_(prefix_len) {
+
+    // check if the prefix is sane
+    if (prefix.getFamily() != AF_INET6) {
+        isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
+    }
+
+    // check if the prefix length is sane
+    if (prefix_len == 0 || prefix_len > 128) {
+        isc_throw(BadValue, "Invalid prefix length");
+    }
+
+    /// @todo: We should probably implement checks against weird addresses
+    /// here, like ::, starting with fe80, starting with ff etc. .
+
+    // Let's now calculate the last address in defined pool
+    last_ = lastAddrInPrefix(prefix, prefix_len);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/lib/dhcp/pool.h b/src/lib/dhcp/pool.h
new file mode 100644
index 0000000..8f6fd86
--- /dev/null
+++ b/src/lib/dhcp/pool.h
@@ -0,0 +1,155 @@
+// 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 POOL_H
+#define POOL_H
+
+#include <vector>
+#include <asiolink/io_address.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief base class for Pool4 and Pool6
+///
+/// Stores information about pool of IPv4 or IPv6 addresses.
+/// That is a basic component of a configuration.
+class Pool {
+
+public:
+
+    /// @brief returns Pool-id
+    ///
+    /// @return pool-id value
+    /// Pool-id is an unique value that can be used to identify a pool.
+    uint32_t getId() const {
+        return (id_);
+    }
+
+    /// @brief Returns the first address in a pool.
+    ///
+    /// @return first address in a pool
+    const isc::asiolink::IOAddress& getFirstAddress() const {
+        return (first_);
+    }
+
+    /// @brief Returns the last address in a pool.
+    /// @return last address in a pool
+    const isc::asiolink::IOAddress& getLastAddress() const {
+        return (last_);
+    }
+
+    /// @brief Checks if a given address is in the range.
+    ///
+    /// @return true, if the address is in pool
+    bool inRange(const isc::asiolink::IOAddress& addr) const;
+
+protected:
+
+    /// @brief protected constructor
+    ///
+    /// This constructor is protected to prevent anyone from instantiating
+    /// Pool class directly. Instances of Pool4 and Pool6 should be created
+    /// instead.
+    Pool(const isc::asiolink::IOAddress& first,
+         const isc::asiolink::IOAddress& last);
+
+    /// @brief returns the next unique Pool-ID
+    ///
+    /// @return the next unique Pool-ID
+    static uint32_t getNextID() {
+        static uint32_t id = 0;
+        return (id++);
+    }
+
+    /// @brief pool-id
+    ///
+    /// This ID is used to identify this specific pool.
+    uint32_t id_;
+
+    /// @brief The first address in a pool
+    isc::asiolink::IOAddress first_;
+
+    /// @brief The last address in a pool
+    isc::asiolink::IOAddress last_;
+
+    /// @brief Comments field
+    ///
+    /// @todo: This field is currently not used.
+    std::string comments_;
+};
+
+/// @brief Pool information for IPv6 addresses and prefixes
+///
+/// It holds information about pool6, i.e. a range of IPv6 address space that
+/// is configured for DHCP allocation.
+class Pool6 : public Pool {
+public:
+
+    /// @brief specifies Pool type
+    ///
+    /// Currently there are 3 pool types defined in DHCPv6:
+    /// - Non-temporary addresses (conveyed in IA_NA)
+    /// - Temporary addresses (conveyed in IA_TA)
+    /// - Delegated Prefixes (conveyed in IA_PD)
+    /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
+    /// support for it is not planned for now.
+    typedef enum {
+        TYPE_IA,
+        TYPE_TA,
+        TYPE_PD
+    }  Pool6Type;
+
+    /// @brief the constructor for Pool6 "min-max" style definition
+    ///
+    /// @param first the first address in a pool
+    /// @param last the last address in a pool
+    Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+          const isc::asiolink::IOAddress& last);
+
+    /// @brief the constructor for Pool6 "prefix/len" style definition
+    ///
+    /// @param prefix specifies prefix of the pool
+    /// @param prefix_len specifies length of the prefix of the pool
+    Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
+          uint8_t prefix_len);
+
+    /// @brief returns pool type
+    ///
+    /// @return pool type
+    Pool6Type getType() const {
+        return (type_);
+    }
+
+private:
+    /// @brief defines a pool type
+    Pool6Type type_;
+
+    /// @brief prefix length
+    /// used by TYPE_PD only (zeroed for other types)
+    uint8_t prefix_len_;
+};
+
+/// @brief a pointer an IPv6 Pool
+typedef boost::shared_ptr<Pool6> Pool6Ptr;
+
+/// @brief a container for IPv6 Pools
+typedef std::vector<Pool6Ptr> Pool6Collection;
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
+
+#endif // POOL_H
diff --git a/src/lib/dhcp/subnet.cc b/src/lib/dhcp/subnet.cc
new file mode 100644
index 0000000..a999295
--- /dev/null
+++ b/src/lib/dhcp/subnet.cc
@@ -0,0 +1,91 @@
+// 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 <dhcp/addr_utilities.h>
+#include <asiolink/io_address.h>
+#include <dhcp/subnet.h>
+#include <dhcp/pool.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
+               const Triplet<uint32_t>& t1,
+               const Triplet<uint32_t>& t2,
+               const Triplet<uint32_t>& valid_lifetime)
+    :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
+     t2_(t2), valid_(valid_lifetime) {
+    if ( (prefix.getFamily() == AF_INET6 && len > 128) ||
+         (prefix.getFamily() == AF_INET && len > 32) ) {
+        isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
+    }
+}
+
+bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
+    IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
+    IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
+
+    return ((first <= addr) && (addr <= last));
+}
+
+Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
+                 const Triplet<uint32_t>& t1,
+                 const Triplet<uint32_t>& t2,
+                 const Triplet<uint32_t>& preferred_lifetime,
+                 const Triplet<uint32_t>& valid_lifetime)
+    :Subnet(prefix, length, t1, t2, valid_lifetime),
+     preferred_(preferred_lifetime){
+    if (prefix.getFamily() != AF_INET6) {
+        isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText()
+                  << " specified in subnet6");
+    }
+}
+
+void Subnet6::addPool6(const Pool6Ptr& pool) {
+    IOAddress first_addr = pool->getFirstAddress();
+    IOAddress last_addr = pool->getLastAddress();
+
+    if (!inRange(first_addr) || !inRange(last_addr)) {
+        isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText()
+                  << " does not belong in this (" << prefix_ << "/" << prefix_len_
+                  << ") subnet6");
+    }
+
+    /// @todo: Check that pools do not overlap
+
+    pools_.push_back(pool);
+}
+
+Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
+    Pool6Ptr candidate;
+    for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++pool) {
+
+        // if we won't find anything better, then let's just use the first pool
+        if (!candidate) {
+            candidate = *pool;
+        }
+
+        // if the client provided a pool and there's a pool that hint is valid in,
+        // then let's use that pool
+        if ((*pool)->inRange(hint)) {
+            return (*pool);
+        }
+    }
+    return (candidate);
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/subnet.h b/src/lib/dhcp/subnet.h
new file mode 100644
index 0000000..aa59010
--- /dev/null
+++ b/src/lib/dhcp/subnet.h
@@ -0,0 +1,161 @@
+// 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 SUBNET_H
+#define SUBNET_H
+
+#include <boost/shared_ptr.hpp>
+#include <asiolink/io_address.h>
+#include <dhcp/pool.h>
+#include <dhcp/triplet.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief a base class for Subnet4 and Subnet6
+///
+/// This class presents a common base for IPv4 and IPv6 subnets.
+/// In a physical sense, a subnet defines a single network link with all devices
+/// attached to it. In most cases all devices attached to a single link can
+/// share the same parameters. Therefore Subnet holds several values that are
+/// typically shared by all hosts: renew timer (T1), rebind timer (T2) and
+/// leased addresses lifetime (valid-lifetime).
+///
+/// @todo: Implement support for options here
+class Subnet {
+public:
+    /// @brief checks if specified address is in range
+    bool inRange(const isc::asiolink::IOAddress& addr) const;
+
+    /// @brief return valid-lifetime for addresses in that prefix
+    Triplet<uint32_t> getValid() const {
+        return (valid_);
+    }
+
+    /// @brief returns T1 (renew timer), expressed in seconds
+    Triplet<uint32_t> getT1() const {
+        return (t1_);
+    }
+
+    /// @brief returns T2 (rebind timer), expressed in seconds
+    Triplet<uint32_t> getT2() const {
+        return (t2_);
+    }
+
+protected:
+    /// @brief protected constructor
+    //
+    /// By making the constructor protected, we make sure that noone will
+    /// ever instantiate that class. Pool4 and Pool6 should be used instead.
+    Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
+           const Triplet<uint32_t>& t1,
+           const Triplet<uint32_t>& t2,
+           const Triplet<uint32_t>& valid_lifetime);
+
+    /// @brief returns the next unique Subnet-ID
+    ///
+    /// @return the next unique Subnet-ID
+    static uint32_t getNextID() {
+        static uint32_t id = 0;
+        return (id++);
+    }
+
+    /// @brief subnet-id
+    ///
+    /// Subnet-id is a unique value that can be used to find or identify
+    /// a Subnet4 or Subnet6.
+    uint32_t id_;
+
+    /// @brief a prefix of the subnet
+    isc::asiolink::IOAddress prefix_;
+
+    /// @brief a prefix length of the subnet
+    uint8_t prefix_len_;
+
+    /// @brief a tripet (min/default/max) holding allowed renew timer values
+    Triplet<uint32_t> t1_;
+
+    /// @brief a tripet (min/default/max) holding allowed rebind timer values
+    Triplet<uint32_t> t2_;
+
+    /// @brief a tripet (min/default/max) holding allowed valid lifetime values
+    Triplet<uint32_t> valid_;
+};
+
+/// @brief A configuration holder for IPv6 subnet.
+///
+/// This class represents an IPv6 subnet.
+class Subnet6 : public Subnet {
+public:
+
+    /// @brief Constructor with all parameters
+    ///
+    /// @param prefix Subnet6 prefix
+    /// @param length prefix length
+    /// @param t1 renewal timer (in seconds)
+    /// @param t2 rebind timer (in seconds)
+    /// @param preferred_lifetime preferred lifetime of leases (in seconds)
+    /// @param valid_lifetime preferred lifetime of leases (in seconds)
+    Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
+            const Triplet<uint32_t>& t1,
+            const Triplet<uint32_t>& t2,
+            const Triplet<uint32_t>& preferred_lifetime,
+            const Triplet<uint32_t>& valid_lifetime);
+
+    /// @brief Returns preverred lifetime (in seconds)
+    ///
+    /// @return a triplet with preferred lifetime
+    Triplet<uint32_t> getPreferred() const {
+        return (preferred_);
+    }
+
+    /// @brief Returns a pool that specified address belongs to
+    ///
+    /// @param hint address that the returned pool should cover (optional)
+    /// @return Pointer to found pool6 (or NULL)
+    Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint =
+                      isc::asiolink::IOAddress("::"));
+
+    /// @brief Adds a new pool.
+    /// @param pool pool to be added
+    void addPool6(const Pool6Ptr& pool);
+
+    /// @brief returns all pools
+    ///
+    /// The reference is only valid as long as the object that
+    /// returned it.
+    ///
+    /// @return a collection of all pools
+    const Pool6Collection& getPools() const {
+        return pools_;
+    }
+
+protected:
+    /// @brief collection of pools in that list
+    Pool6Collection pools_;
+
+    /// @brief a triplet with preferred lifetime (in seconds)
+    Triplet<uint32_t> preferred_;
+};
+
+/// @brief A pointer to a Subnet6 object
+typedef boost::shared_ptr<Subnet6> Subnet6Ptr;
+
+/// @brief A collection of Subnet6 objects
+typedef std::vector<Subnet6Ptr> Subnet6Collection;
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
+#endif // SUBNET_T
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 9e00ab0..9fd3492 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -24,7 +24,7 @@ TESTS_ENVIRONMENT = \
 
 TESTS =
 if HAVE_GTEST
-TESTS += libdhcp++_unittests
+TESTS += libdhcp++_unittests libdhcpsrv_unittests
 libdhcp___unittests_SOURCES  = run_unittests.cc
 libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
 libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
@@ -38,20 +38,37 @@ libdhcp___unittests_SOURCES += pkt4_unittest.cc
 
 libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 libdhcp___unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
 libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
 
+libdhcpsrv_unittests_SOURCES  = run_unittests.cc
+libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc triplet_unittest.cc
+libdhcpsrv_unittests_SOURCES += pool_unittest.cc subnet_unittest.cc
+libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
+
+libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+libdhcpsrv_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcpsrv_unittests_LDADD  = $(GTEST_LDADD)
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+
+
 if USE_CLANGPP
 # This is to workaround unused variables tcout and tcerr in
 # log4cplus's streams.h and unused parameters from some of the
 # Boost headers.
 libdhcp___unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
+libdhcpsrv_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
 endif
+
 libdhcp___unittests_LDADD  = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 libdhcp___unittests_LDADD += $(GTEST_LDADD)
 endif
 
diff --git a/src/lib/dhcp/tests/addr_utilities_unittest.cc b/src/lib/dhcp/tests/addr_utilities_unittest.cc
new file mode 100644
index 0000000..8382827
--- /dev/null
+++ b/src/lib/dhcp/tests/addr_utilities_unittest.cc
@@ -0,0 +1,93 @@
+
+// Copyright (C) 2010  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 <config.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include <dhcp/addr_utilities.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+TEST(Pool6Test, lastAddrInPrefix) {
+    IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
+
+    // Prefixes rounded to nibbles are easy...
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:1234:ffff",
+              lastAddrInPrefix(addr1, 112).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:123f:ffff",
+              lastAddrInPrefix(addr1, 108).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:12ff:ffff",
+              lastAddrInPrefix(addr1, 104).toText());
+    EXPECT_EQ("2001:db8:1:1234:ffff:ffff:ffff:ffff",
+              lastAddrInPrefix(addr1, 64).toText());
+
+    IOAddress addr2("2001::");
+
+    // These are tricker, though, as they are done in 1 bit increments
+
+    // the last address in 2001::/127 pool should be 2001::1
+    EXPECT_EQ("2001::1", lastAddrInPrefix(addr2, 127).toText());
+
+    EXPECT_EQ("2001::3", lastAddrInPrefix(addr2, 126).toText());
+    EXPECT_EQ("2001::7", lastAddrInPrefix(addr2, 125).toText());
+    EXPECT_EQ("2001::f", lastAddrInPrefix(addr2, 124).toText());
+    EXPECT_EQ("2001::1f", lastAddrInPrefix(addr2, 123).toText());
+    EXPECT_EQ("2001::3f", lastAddrInPrefix(addr2, 122).toText());
+    EXPECT_EQ("2001::7f", lastAddrInPrefix(addr2, 121).toText());
+    EXPECT_EQ("2001::ff", lastAddrInPrefix(addr2, 120).toText());
+
+    // Let's check extreme cases
+    IOAddress anyAddr("::");
+    EXPECT_EQ("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+              lastAddrInPrefix(anyAddr, 1).toText());
+    EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+              lastAddrInPrefix(anyAddr, 0).toText());
+    EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
+}
+
+TEST(Pool6Test, firstAddrInPrefix) {
+    IOAddress addr1("2001:db8:1:1234:5678:1234:abcd:beef");
+
+    // Prefixes rounded to nibbles are easy...
+    EXPECT_EQ("2001:db8:1:1234:5678:1234::",
+              firstAddrInPrefix(addr1, 96).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:1230::",
+              firstAddrInPrefix(addr1, 92).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:1200::",
+              firstAddrInPrefix(addr1, 88).toText());
+    EXPECT_EQ("2001:db8:1:1234::",
+              firstAddrInPrefix(addr1, 64).toText());
+
+    IOAddress addr2("2001::ffff");
+
+    // These are tricker, though, as they are done in 1 bit increments
+
+    // the first address in 2001::/127 pool should be 2001::1
+    EXPECT_EQ("2001::fffe", firstAddrInPrefix(addr2, 127).toText());
+
+    EXPECT_EQ("2001::fffc", firstAddrInPrefix(addr2, 126).toText());
+    EXPECT_EQ("2001::fff8", firstAddrInPrefix(addr2, 125).toText());
+    EXPECT_EQ("2001::fff0", firstAddrInPrefix(addr2, 124).toText());
+    EXPECT_EQ("2001::ffe0", firstAddrInPrefix(addr2, 123).toText());
+    EXPECT_EQ("2001::ffc0", firstAddrInPrefix(addr2, 122).toText());
+    EXPECT_EQ("2001::ff80", firstAddrInPrefix(addr2, 121).toText());
+    EXPECT_EQ("2001::ff00", firstAddrInPrefix(addr2, 120).toText());
+}
diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc
new file mode 100644
index 0000000..a11acbf
--- /dev/null
+++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc
@@ -0,0 +1,63 @@
+// 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 <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <dhcp/cfgmgr.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc;
+
+// don't import the entire boost namespace.  It will unexpectedly hide uint8_t
+// for some systems.
+using boost::scoped_ptr;
+
+namespace {
+
+// This test verifies if the configuration manager is able to hold and return
+// valid leases
+TEST(CfgMgrTest, subnet6) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+
+    ASSERT_TRUE(&cfg_mgr != 0);
+
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+
+    // there shouldn't be any subnet configured at this stage
+    EXPECT_EQ( Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("2000::1")));
+
+    cfg_mgr.addSubnet6(subnet1);
+
+    // Now we have only one subnet, any request will be served from it
+    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2001:db8::1")));
+
+    cfg_mgr.addSubnet6(subnet2);
+    cfg_mgr.addSubnet6(subnet3);
+
+    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123")));
+    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
+    EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1")));
+
+}
+
+} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index 4fe7e23..462910b 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -82,10 +82,8 @@ TEST_F(IfaceMgrTest, loDetect) {
     // it will go away as soon as proper interface detection
     // is implemented
     if (if_nametoindex("lo") > 0) {
-        cout << "This is Linux, using lo as loopback." << endl;
         snprintf(LOOPBACK, BUF_SIZE - 1, "lo");
     } else if (if_nametoindex("lo0") > 0) {
-        cout << "This is BSD, using lo0 as loopback." << endl;
         snprintf(LOOPBACK, BUF_SIZE - 1, "lo0");
     } else {
         cout << "Failed to detect loopback interface. Neither "
@@ -421,7 +419,7 @@ TEST_F(IfaceMgrTest, sockets6) {
     // testing socket operation in a portable way is tricky
     // without interface detection implemented
 
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
 
     IOAddress loAddr("::1");
 
@@ -441,10 +439,25 @@ TEST_F(IfaceMgrTest, sockets6) {
     // removed code for binding socket twice to the same address/port
     // as it caused problems on some platforms (e.g. Mac OS X)
 
-    close(socket1);
-    close(socket2);
+    // Close sockets here because the following tests will want to
+    // open sockets on the same ports.
+    ifacemgr->closeSockets();
 
-    delete ifacemgr;
+    // Use address that is not assigned to LOOPBACK iface.
+    IOAddress invalidAddr("::2");
+    EXPECT_THROW(
+        ifacemgr->openSocket(LOOPBACK, invalidAddr, 10547),
+        SocketConfigError
+    );
+
+    // Use non-existing interface name.
+    EXPECT_THROW(
+        ifacemgr->openSocket("non_existing_interface", loAddr, 10548),
+        BadValue
+    );
+
+    // Do not call closeSockets() because it is called by IfaceMgr's
+    // virtual destructor.
 }
 
 TEST_F(IfaceMgrTest, socketsFromIface) {
@@ -468,6 +481,18 @@ TEST_F(IfaceMgrTest, socketsFromIface) {
     EXPECT_GT(socket2, 0);
     close(socket2);
 
+    // Close sockets here because the following tests will want to
+    // open sockets on the same ports.
+    ifacemgr->closeSockets();
+
+    // Use invalid interface name.
+    EXPECT_THROW(
+        ifacemgr->openSocketFromIface("non_existing_interface", PORT1, AF_INET),
+        BadValue
+    );
+
+    // Do not call closeSockets() because it is called by IfaceMgr's
+    // virtual destructor.
 }
 
 
@@ -482,7 +507,6 @@ TEST_F(IfaceMgrTest, socketsFromAddress) {
     );
     // socket descriptor must be positive integer
     EXPECT_GT(socket1, 0);
-    close(socket1);
 
     // Open v4 socket on loopback interface and bind to different port
     int socket2 = 0;
@@ -492,7 +516,19 @@ TEST_F(IfaceMgrTest, socketsFromAddress) {
     );
     // socket descriptor must be positive integer
     EXPECT_GT(socket2, 0);
-    close(socket2);
+
+    // Close sockets here because the following tests will want to
+    // open sockets on the same ports.
+    ifacemgr->closeSockets();
+
+    // Use non-existing address.
+    IOAddress invalidAddr("1.2.3.4");
+    EXPECT_THROW(
+        ifacemgr->openSocketFromAddress(invalidAddr, PORT1), BadValue
+    );
+
+    // Do not call closeSockets() because it is called by IfaceMgr's
+    // virtual destructor.
 }
 
 TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
@@ -507,7 +543,6 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
         socket1 = ifacemgr->openSocketFromRemoteAddress(loAddr6, PORT1);
     );
     EXPECT_GT(socket1, 0);
-    close(socket1);
 
     // Open v4 socket to connect to remote address.
     int socket2 = 0;
@@ -516,7 +551,10 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
         socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2);
     );
     EXPECT_GT(socket2, 0);
-    close(socket2);
+
+    // Close sockets here because the following tests will want to
+    // open sockets on the same ports.
+    ifacemgr->closeSockets();
 
     // The following test is currently disabled for OSes other than
     // Linux because interface detection is not implemented on them.
@@ -530,8 +568,10 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
         socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2);
     );
     EXPECT_GT(socket3, 0);
-    close(socket3);
 #endif
+
+    // Do not call closeSockets() because it is called by IfaceMgr's
+    // virtual destructor.
 }
 
 // TODO: disabled due to other naming on various systems
@@ -570,7 +610,7 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     // testing socket operation in a portable way is tricky
     // without interface detection implemented
 
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
 
     // let's assume that every supported OS have lo interface
     IOAddress loAddr("::1");
@@ -619,7 +659,11 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     // we should accept both values as source ports.
     EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
 
-    delete ifacemgr;
+    // try to send/receive data over the closed socket. Closed socket's descriptor is
+    // still being hold by IfaceMgr which will try to use it to receive data.
+    close(socket1);
+    EXPECT_THROW(ifacemgr->receive6(10), SocketReadError);
+    EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
 }
 
 TEST_F(IfaceMgrTest, sendReceive4) {
@@ -627,7 +671,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
     // testing socket operation in a portable way is tricky
     // without interface detection implemented
 
-    NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
+    boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
 
     // let's assume that every supported OS have lo interface
     IOAddress loAddr("127.0.0.1");
@@ -675,8 +719,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
 
-    rcvPkt = ifacemgr->receive4(10);
-
+    ASSERT_NO_THROW(rcvPkt = ifacemgr->receive4(10));
     ASSERT_TRUE(rcvPkt); // received our own packet
 
     ASSERT_NO_THROW(
@@ -710,7 +753,11 @@ TEST_F(IfaceMgrTest, sendReceive4) {
     // assume the one or the other will always be choosen for sending data. We should
     // skip checking source port of sent address.
 
-    delete ifacemgr;
+    // try to receive data over the closed socket. Closed socket's descriptor is
+    // still being hold by IfaceMgr which will try to use it to receive data.
+    close(socket1);
+    EXPECT_THROW(ifacemgr->receive4(10), SocketReadError);
+    EXPECT_THROW(ifacemgr->send(sendPkt), SocketWriteError);
 }
 
 
@@ -1049,7 +1096,7 @@ void parse_ifconfig(const std::string& textFile, IfaceMgr::IfaceCollection& ifac
             addr = addr.substr(0, addr.find_first_of(" "));
             IOAddress a(addr);
             iface->addAddress(a);
-        } else if(line.find("Metric")) {
+        } else if(line.find("Metric") != string::npos) {
             // flags
             if (line.find("UP") != string::npos) {
                 iface->flag_up_ = true;
@@ -1214,7 +1261,7 @@ TEST_F(IfaceMgrTest, controlSession) {
     EXPECT_NO_THROW(ifacemgr->set_session_socket(pipefd[0], my_callback));
 
     Pkt4Ptr pkt4;
-    pkt4 = ifacemgr->receive4(1);
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
 
     // Our callback should not be called this time (there was no data)
     EXPECT_FALSE(callback_ok);
@@ -1226,7 +1273,7 @@ TEST_F(IfaceMgrTest, controlSession) {
     EXPECT_EQ(38, write(pipefd[1], "Hi, this is a message sent over a pipe", 38));
 
     // ... and repeat
-    pkt4 = ifacemgr->receive4(1);
+    ASSERT_NO_THROW(pkt4 = ifacemgr->receive4(1));
 
     // IfaceMgr should not process control socket data as incoming packets
     EXPECT_FALSE(pkt4);
diff --git a/src/lib/dhcp/tests/pool_unittest.cc b/src/lib/dhcp/tests/pool_unittest.cc
new file mode 100644
index 0000000..61d4c4a
--- /dev/null
+++ b/src/lib/dhcp/tests/pool_unittest.cc
@@ -0,0 +1,109 @@
+// 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 <config.h>
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include <gtest/gtest.h>
+#include <dhcp/pool.h>
+#include <asiolink/io_address.h>
+
+using boost::scoped_ptr;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+
+TEST(Pool6Test, constructor_first_last) {
+
+    // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
+    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
+                IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"));
+
+    EXPECT_EQ(Pool6::TYPE_IA, pool1.getType());
+    EXPECT_EQ(IOAddress("2001:db8:1::"), pool1.getFirstAddress());
+    EXPECT_EQ(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"),
+              pool1.getLastAddress());
+
+    // This is Pool6, IPv4 addresses do not belong here
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
+                       IOAddress("192.168.0.5")), BadValue);
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
+                       IOAddress("2001:db8::1")), BadValue);
+
+    // Should throw. Range should be 2001:db8::1 - 2001:db8::2, not
+    // the other way around.
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::2"),
+                       IOAddress("2001:db8::1")), BadValue);
+}
+
+TEST(Pool6Test, constructor_prefix_len) {
+
+    // let's construct 2001:db8:1::/96 pool
+    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"), 96);
+
+    EXPECT_EQ(Pool6::TYPE_IA, pool1.getType());
+    EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
+    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());
+
+    // No such thing as /130 prefix
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 130),
+                 BadValue);
+
+    // /0 prefix does not make sense
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 0),
+                 BadValue);
+
+    // This is Pool6, IPv4 addresses do not belong here
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
+                 BadValue);
+}
+
+TEST(Pool6Test, in_range) {
+   Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::1"),
+               IOAddress("2001:db8:1::f"));
+
+   EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::")));
+   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::1")));
+   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::7")));
+   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::f")));
+   EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::10")));
+   EXPECT_FALSE(pool1.inRange(IOAddress("::")));
+}
+
+// This test creates 100 pools and verifies that their IDs are unique.
+TEST(Pool6Test, unique_id) {
+
+    const int num_pools = 100;
+    std::vector<Pool6Ptr> pools;
+
+    for (int i = 0; i < num_pools; ++i) {
+        pools.push_back(Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
+                                           IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"))));
+    }
+
+    for (int i = 0; i < num_pools; ++i) {
+        for (int j = i + 1; j < num_pools; ++j) {
+            if (pools[i]->getId() == pools[j]->getId()) {
+                FAIL() << "Pool-ids must be unique";
+            }
+        }
+    }
+
+}
+
+}; // end of anonymous namespace
+
diff --git a/src/lib/dhcp/tests/run_unittests.cc b/src/lib/dhcp/tests/run_unittests.cc
index db27f76..0126645 100644
--- a/src/lib/dhcp/tests/run_unittests.cc
+++ b/src/lib/dhcp/tests/run_unittests.cc
@@ -13,7 +13,6 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <gtest/gtest.h>
-
 #include <log/logger_support.h>
 
 int
diff --git a/src/lib/dhcp/tests/subnet_unittest.cc b/src/lib/dhcp/tests/subnet_unittest.cc
new file mode 100644
index 0000000..6afebb8
--- /dev/null
+++ b/src/lib/dhcp/tests/subnet_unittest.cc
@@ -0,0 +1,112 @@
+// 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 <config.h>
+#include <dhcp/subnet.h>
+#include <exceptions/exceptions.h>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+#include <asiolink/io_address.h>
+
+// don't import the entire boost namespace.  It will unexpectedly hide uint8_t
+// for some systems.
+using boost::scoped_ptr;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+
+TEST(Subnet6Test, constructor) {
+
+    EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64,
+                                    1, 2, 3, 4));
+
+    EXPECT_THROW(Subnet6 subnet2(IOAddress("2001:db8:1::"), 129, 1, 2, 3, 4),
+                BadValue); // invalid prefix length
+    EXPECT_THROW(Subnet6 subnet3(IOAddress("192.168.0.0"), 32, 1, 2, 3, 4),
+                BadValue); // IPv4 addresses are not allowed in Subnet6
+}
+
+TEST(Subnet6Test, in_range) {
+    Subnet6 subnet(IOAddress("2001:db8:1::"), 64, 1000, 2000, 3000, 4000);
+
+    EXPECT_EQ(1000, subnet.getT1());
+    EXPECT_EQ(2000, subnet.getT2());
+    EXPECT_EQ(3000, subnet.getPreferred());
+    EXPECT_EQ(4000, subnet.getValid());
+
+
+    EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
+    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
+    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
+    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff")));
+    EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:1:1::")));
+    EXPECT_FALSE(subnet.inRange(IOAddress("::")));
+}
+
+TEST(Subnet6Test, Pool6InSubnet6) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+    Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:2::"), 64));
+    Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:3::"), 64));
+
+    subnet->addPool6(pool1);
+
+    // If there's only one pool, get that pool
+    Pool6Ptr mypool = subnet->getPool6();
+    EXPECT_EQ(mypool, pool1);
+
+
+    subnet->addPool6(pool2);
+    subnet->addPool6(pool3);
+
+    // If there are more than one pool and we didn't provide hint, we
+    // should get the first pool
+    mypool = subnet->getPool6();
+
+    EXPECT_EQ(mypool, pool1);
+
+    // If we provide a hint, we should get a pool that this hint belongs to
+    mypool = subnet->getPool6(IOAddress("2001:db8:1:3::dead:beef"));
+
+    EXPECT_EQ(mypool, pool3);
+
+}
+
+TEST(Subnet6Test, Subnet6_Pool6_checks) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    // this one is in subnet
+    Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+    subnet->addPool6(pool1);
+
+    // this one is larger than the subnet!
+    Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 48));
+
+    EXPECT_THROW(subnet->addPool6(pool2), BadValue);
+
+
+    // this one is totally out of blue
+    Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16));
+    EXPECT_THROW(subnet->addPool6(pool3), BadValue);
+
+}
+
+
+};
diff --git a/src/lib/dhcp/tests/triplet_unittest.cc b/src/lib/dhcp/tests/triplet_unittest.cc
new file mode 100644
index 0000000..727eb8a
--- /dev/null
+++ b/src/lib/dhcp/tests/triplet_unittest.cc
@@ -0,0 +1,104 @@
+// 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 <config.h>
+#include <stdint.h>
+#include <gtest/gtest.h>
+#include <dhcp/triplet.h>
+#include <exceptions/exceptions.h>
+
+using namespace isc::dhcp;
+using namespace isc;
+
+namespace {
+
+// constructor validation
+TEST(TripletTest, constructor) {
+
+    const uint32_t min = 10;
+    const uint32_t value = 20;
+    const uint32_t max = 30;
+
+    Triplet<uint32_t> x(min, value, max);
+
+    EXPECT_EQ(min, x.getMin());
+    EXPECT_EQ(value, x.get());
+    EXPECT_EQ(max, x.getMax());
+
+    // requested values below min should return allowed min value
+    EXPECT_EQ(min, x.get(min - 5));
+
+    EXPECT_EQ(min, x.get(min));
+
+    // requesting a value from within the range (min < x < max) should
+    // return the requested value
+    EXPECT_EQ(17, x.get(17));
+
+    EXPECT_EQ(max, x.get(max));
+
+    EXPECT_EQ(max, x.get(max + 5));
+
+    // this will be boring. It is expected to return 42 no matter what
+    Triplet<uint32_t> y(42);
+
+    EXPECT_EQ(42, y.getMin()); // min, default and max are equal to 42
+    EXPECT_EQ(42, y.get());    // it returns ...
+    EXPECT_EQ(42, y.getMax()); // the exact value...
+
+    // requested values below or above are ignore
+    EXPECT_EQ(42, y.get(5));   // all...
+    EXPECT_EQ(42, y.get(42));  // the...
+    EXPECT_EQ(42, y.get(80));  // time!
+}
+
+// Triplets must be easy to use.
+// Simple to/from int conversions must be done on the fly.
+TEST(TripletTest, operator) {
+
+    uint32_t x = 47;
+
+    Triplet<uint32_t> foo(1,2,3);
+    Triplet<uint32_t> bar(4,5,6);
+
+    foo = bar;
+
+    EXPECT_EQ(4, foo.getMin());
+    EXPECT_EQ(5, foo.get());
+    EXPECT_EQ(6, foo.getMax());
+
+    // assignment operator: uint32_t => triplet
+    Triplet<uint32_t> y(0);
+    y = x;
+
+    EXPECT_EQ(x, y.get());
+
+    // let's try the other way around: triplet => uint32_t
+    uint32_t z = 0;
+    z = y;
+
+    EXPECT_EQ(x, z);
+}
+
+// check if specified values are sane
+TEST(TripletTest, sanity_check) {
+
+    // min is larger than default
+    EXPECT_THROW(Triplet<uint32_t>(6,5,5), BadValue);
+
+    // max is smaller than default
+    EXPECT_THROW(Triplet<uint32_t>(5,5,4), BadValue);
+
+}
+
+}; // end of anonymous namespace
diff --git a/src/lib/dhcp/triplet.h b/src/lib/dhcp/triplet.h
new file mode 100644
index 0000000..d45f003
--- /dev/null
+++ b/src/lib/dhcp/triplet.h
@@ -0,0 +1,110 @@
+// 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>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief this template specifies a parameter value
+///
+/// This template class is used to store configuration parameters, like lifetime or T1.
+/// It defines 3 parameters: min, default, and max value. There are 2 constructors:
+/// - simple (just one value that sets all parameters)
+/// - extended (that sets default value and two thresholds)
+/// It will be used with integer types. It provides necessary operators, so
+/// it can be assigned to a plain integer or integer assigned to a Triplet.
+/// See TripletTest.operator test for details on an easy Triplet usage.
+template <class T>
+class Triplet {
+public:
+
+    /// @brief base type to Triple conversion
+    ///
+    /// Typically: uint32_t to Triplet assignment. It is very convenient
+    /// to be able to simply write Triplet<uint32_t> x = 7;
+    Triplet<T> operator=(T other) {
+        min_ = other;
+        default_ = other;
+        max_ = other;
+        return *this;
+    }
+
+    /// @brief triplet to base type conversion
+    ///
+    /// Typically: Triplet to uint32_t assignment. It is very convenient
+    /// to be able to simply write uint32_t z = x; (where x is a Triplet)
+    operator T() const {
+        return (default_);
+    }
+
+    /// @brief sets a fixed value
+    ///
+    /// This constructor assigns a fixed (i.e. no range, just a single value)
+    /// value.
+    Triplet(T value)
+        :min_(value), default_(value), max_(value) {
+    }
+
+    /// @brief sets the default value and thresholds
+    ///
+    /// @throw BadValue if min <= def <= max rule is violated
+    Triplet(T min, T def, T max)
+        :min_(min), default_(def), max_(max) {
+        if ( (min_ > def) || (def > max_) ) {
+            isc_throw(BadValue, "Invalid triplet values.");
+        }
+    }
+
+    /// @brief returns a minimum allowed value
+    T getMin() const { return min_;}
+
+    /// @brief returns the default value
+    T get() const { return default_;}
+
+    /// @brief returns value with a hint
+    ///
+    /// DHCP protocol treats any values sent by a client as hints.
+    /// This is a method that implements that. We can assign any value
+    /// from configured range that client asks.
+    T get(T hint) const {
+        if (hint <= min_) {
+            return (min_);
+        }
+
+        if (hint >= max_) {
+            return (max_);
+        }
+
+        return (hint);
+    }
+
+    /// @brief returns a maximum allowed value
+    T getMax() const { return max_; }
+
+protected:
+
+    /// @brief the minimum value
+    T min_;
+
+    /// @brief the default value
+    T default_;
+
+    /// @brief the maximum value
+    T max_;
+};
+
+
+} // namespace isc::dhcp
+} // namespace isc
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
index 6376498..abf3192 100644
--- a/src/lib/dns/benchmarks/message_renderer_bench.cc
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -39,7 +39,7 @@ public:
         renderer_(NULL),
         names_(names)
     {}
-    MessageRendererBenchMark() {
+    ~MessageRendererBenchMark() {
         delete renderer_;
     }
     unsigned int run() {
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
index 38ee2ac..65576ee 100644
--- a/src/lib/dns/benchmarks/rdatarender_bench.cc
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -42,9 +42,10 @@ template <typename T>
 class RdataRenderBenchMark {
 public:
     RdataRenderBenchMark(const vector<T>& dataset) :
-        dataset_(dataset)
+        dataset_(dataset),
+        renderer_(NULL)
     {}
-    RdataRenderBenchMark() {
+    ~RdataRenderBenchMark() {
         delete renderer_;
     }
     unsigned int run() {
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
index ed23f26..f5f6a95 100644
--- a/src/lib/dns/labelsequence.cc
+++ b/src/lib/dns/labelsequence.cc
@@ -45,8 +45,7 @@ LabelSequence::LabelSequence(const void* buf) {
     // Check the integrity on the offsets and the name data
     const uint8_t* dp = data_;
     for (size_t cur_offset = 0; cur_offset < offsets_len; ++cur_offset) {
-        if (offsets_[cur_offset] > Name::MAX_LABELLEN ||
-            dp - data_ != offsets_[cur_offset]) {
+        if (dp - data_ != offsets_[cur_offset] || *dp > Name::MAX_LABELLEN) {
             isc_throw(BadValue,
                       "Broken offset or name data in serialized "
                       "LabelSequence data");
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
index 1cb1ea8..dba0e7b 100644
--- a/src/lib/dns/message.cc
+++ b/src/lib/dns/message.cc
@@ -254,6 +254,13 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
     const size_t orig_msg_len_limit = renderer.getLengthLimit();
     const AbstractMessageRenderer::CompressMode orig_compress_mode =
         renderer.getCompressMode();
+
+    // We are going to skip soon, so we need to clear the renderer
+    // But we'll leave the length limit  and the compress mode intact
+    // (or shortened in case of TSIG)
+    renderer.clear();
+    renderer.setCompressMode(orig_compress_mode);
+
     if (tsig_len > 0) {
         if (tsig_len > orig_msg_len_limit) {
             isc_throw(InvalidParameter, "Failed to render DNS message: "
@@ -261,6 +268,8 @@ MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
                       orig_msg_len_limit << ")");
         }
         renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+    } else {
+        renderer.setLengthLimit(orig_msg_len_limit);
     }
 
     // reserve room for the header
diff --git a/src/lib/dns/message.h b/src/lib/dns/message.h
index 3b80357..85754ac 100644
--- a/src/lib/dns/message.h
+++ b/src/lib/dns/message.h
@@ -556,6 +556,10 @@ public:
     /// \c Rcode must have been set beforehand; otherwise, an exception of
     /// class \c InvalidMessageOperation will be thrown.
     ///
+    /// \note The renderer's internal buffers and data are automatically
+    /// cleared, keeping the length limit and the compression mode intact.
+    /// In case truncation is triggered, the renderer is cleared completely.
+    ///
     /// \param renderer DNS message rendering context that encapsulates the
     /// output buffer and name compression information.
     void toWire(AbstractMessageRenderer& renderer);
@@ -581,6 +585,10 @@ public:
     /// it should mean a bug either in the TSIG context or in the renderer
     /// implementation.
     ///
+    /// \note The renderer's internal buffers and data are automatically
+    /// cleared, keeping the length limit and the compression mode intact.
+    /// In case truncation is triggered, the renderer is cleared completely.
+    ///
     /// \param renderer See the other version
     /// \param tsig_ctx A TSIG context that is to be used for signing the
     /// message
diff --git a/src/lib/dns/nsec3hash.cc b/src/lib/dns/nsec3hash.cc
index 159dff3..8fe3cf1 100644
--- a/src/lib/dns/nsec3hash.cc
+++ b/src/lib/dns/nsec3hash.cc
@@ -16,6 +16,7 @@
 
 #include <cassert>
 #include <cstring>
+#include <cstdlib>
 #include <string>
 #include <vector>
 
@@ -57,17 +58,31 @@ private:
 
 public:
     NSEC3HashRFC5155(uint8_t algorithm, uint16_t iterations,
-                     const vector<uint8_t>& salt) :
+                     const uint8_t* salt_data, size_t salt_length) :
         algorithm_(algorithm), iterations_(iterations),
-        salt_(salt), digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
+        salt_data_(NULL), salt_length_(salt_length),
+        digest_(SHA1_HASHSIZE), obuf_(Name::MAX_WIRE)
     {
         if (algorithm_ != NSEC3_HASH_SHA1) {
             isc_throw(UnknownNSEC3HashAlgorithm, "Unknown NSEC3 algorithm: " <<
                       static_cast<unsigned int>(algorithm_));
         }
+
+        if (salt_length > 0) {
+            salt_data_ = static_cast<uint8_t*>(std::malloc(salt_length));
+            if (salt_data_ == NULL) {
+                throw std::bad_alloc();
+            }
+            std::memcpy(salt_data_, salt_data, salt_length);
+        }
+
         SHA1Reset(&sha1_ctx_);
     }
 
+    virtual ~NSEC3HashRFC5155() {
+        std::free(salt_data_);
+    }
+
     virtual std::string calculate(const Name& name) const;
 
     virtual bool match(const generic::NSEC3& nsec3) const;
@@ -78,7 +93,8 @@ public:
 private:
     const uint8_t algorithm_;
     const uint16_t iterations_;
-    const vector<uint8_t> salt_;
+    uint8_t* salt_data_;
+    const size_t salt_length_;
 
     // The following members are placeholder of work place and don't hold
     // any state over multiple calls so can be mutable without breaking
@@ -108,15 +124,14 @@ NSEC3HashRFC5155::calculate(const Name& name) const {
     name_copy.downcase();
     name_copy.toWire(obuf_);
 
-    const uint8_t saltlen = salt_.size();
-    const uint8_t* const salt = (saltlen > 0) ? &salt_[0] : NULL;
     uint8_t* const digest = &digest_[0];
     assert(digest_.size() == SHA1_HASHSIZE);
 
     iterateSHA1(&sha1_ctx_, static_cast<const uint8_t*>(obuf_.getData()),
-                obuf_.getLength(), salt, saltlen, digest);
+                obuf_.getLength(), salt_data_, salt_length_, digest);
     for (unsigned int n = 0; n < iterations_; ++n) {
-        iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE, salt, saltlen, digest);
+        iterateSHA1(&sha1_ctx_, digest, SHA1_HASHSIZE,
+                    salt_data_, salt_length_, digest);
     }
 
     return (encodeBase32Hex(digest_));
@@ -127,8 +142,9 @@ NSEC3HashRFC5155::match(uint8_t algorithm, uint16_t iterations,
                         const vector<uint8_t>& salt) const
 {
     return (algorithm_ == algorithm && iterations_ == iterations &&
-            salt_.size() == salt.size() &&
-            (salt_.empty() || memcmp(&salt_[0], &salt[0], salt_.size()) == 0));
+            salt_length_ == salt.size() &&
+            ((salt_length_ == 0) ||
+             memcmp(salt_data_, &salt[0], salt_length_) == 0));
 }
 
 bool
@@ -175,15 +191,35 @@ NSEC3Hash::create(const generic::NSEC3& nsec3) {
 }
 
 NSEC3Hash*
+NSEC3Hash::create(uint8_t algorithm, uint16_t iterations,
+                  const uint8_t* salt_data, size_t salt_length) {
+    return (getNSEC3HashCreator()->create(algorithm, iterations,
+                                          salt_data, salt_length));
+}
+
+NSEC3Hash*
 DefaultNSEC3HashCreator::create(const generic::NSEC3PARAM& param) const {
+    const vector<uint8_t>& salt = param.getSalt();
     return (new NSEC3HashRFC5155(param.getHashalg(), param.getIterations(),
-                                 param.getSalt()));
+                                 salt.empty() ? NULL : &salt[0],
+                                 salt.size()));
 }
 
 NSEC3Hash*
 DefaultNSEC3HashCreator::create(const generic::NSEC3& nsec3) const {
+    const vector<uint8_t>& salt = nsec3.getSalt();
     return (new NSEC3HashRFC5155(nsec3.getHashalg(), nsec3.getIterations(),
-                                 nsec3.getSalt()));
+                                 salt.empty() ? NULL : &salt[0],
+                                 salt.size()));
+}
+
+NSEC3Hash*
+DefaultNSEC3HashCreator::create(uint8_t algorithm, uint16_t iterations,
+                                const uint8_t* salt_data,
+                                size_t salt_length) const
+{
+    return (new NSEC3HashRFC5155(algorithm, iterations,
+                                 salt_data, salt_length));
 }
 
 void
diff --git a/src/lib/dns/nsec3hash.h b/src/lib/dns/nsec3hash.h
index 2056708..e082ee8 100644
--- a/src/lib/dns/nsec3hash.h
+++ b/src/lib/dns/nsec3hash.h
@@ -16,7 +16,8 @@
 #define __NSEC3HASH_H 1
 
 #include <string>
-
+#include <vector>
+#include <stdint.h>
 #include <exceptions/exceptions.h>
 
 namespace isc {
@@ -115,6 +116,16 @@ public:
     /// for hash calculation from an NSEC3 RDATA object.
     static NSEC3Hash* create(const rdata::generic::NSEC3& nsec3);
 
+    /// \brief Factory method of NSECHash from args.
+    ///
+    /// \param algorithm the NSEC3 algorithm to use; currently only 1
+    ///                  (SHA-1) is supported
+    /// \param iterations the number of iterations
+    /// \param salt_data the salt data as a byte array
+    /// \param salt_data_length the length of the salt data
+    static NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+                             const uint8_t* salt_data, size_t salt_length);
+
     /// \brief The destructor.
     virtual ~NSEC3Hash() {}
 
@@ -167,7 +178,7 @@ public:
 /// would be an experimental extension for a newer hash algorithm or
 /// implementation.
 ///
-/// The two main methods named \c create() correspond to the static factory
+/// The three main methods named \c create() correspond to the static factory
 /// methods of \c NSEC3Hash of the same name.
 ///
 /// By default, the library uses the \c DefaultNSEC3HashCreator creator.
@@ -210,6 +221,22 @@ public:
     /// <code>NSEC3Hash::create(const rdata::generic::NSEC3& param)</code>
     virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3)
         const = 0;
+
+    /// \brief Factory method of NSECHash from args.
+    ///
+    /// See
+    /// <code>NSEC3Hash::create(uint8_t algorithm, uint16_t iterations,
+    ///                         const uint8_t* salt_data,
+    ///                         size_t salt_length)</code>
+    ///
+    /// \param algorithm the NSEC3 algorithm to use; currently only 1
+    ///                  (SHA-1) is supported
+    /// \param iterations the number of iterations
+    /// \param salt_data the salt data as a byte array
+    /// \param salt_data_length the length of the salt data
+    virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+                              const uint8_t* salt_data, size_t salt_length)
+        const = 0;
 };
 
 /// \brief The default NSEC3Hash creator.
@@ -225,6 +252,9 @@ class DefaultNSEC3HashCreator : public NSEC3HashCreator {
 public:
     virtual NSEC3Hash* create(const rdata::generic::NSEC3PARAM& param) const;
     virtual NSEC3Hash* create(const rdata::generic::NSEC3& nsec3) const;
+    virtual NSEC3Hash* create(uint8_t algorithm, uint16_t iterations,
+                              const uint8_t* salt_data,
+                              size_t salt_length) const;
 };
 
 /// \brief The registrar of \c NSEC3HashCreator.
diff --git a/src/lib/dns/python/edns_python.cc b/src/lib/dns/python/edns_python.cc
index 8f0f1a4..e9d54c1 100644
--- a/src/lib/dns/python/edns_python.cc
+++ b/src/lib/dns/python/edns_python.cc
@@ -269,7 +269,6 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
     const PyObject* rrtype;
     const PyObject* rrttl;
     const PyObject* rdata;
-    s_EDNS* edns_obj = NULL;
 
     assert(null_self == NULL);
 
@@ -277,7 +276,7 @@ EDNS_createFromRR(const s_EDNS* null_self, PyObject* args) {
                          &rrclass_type, &rrclass, &rrtype_type, &rrtype,
                          &rrttl_type, &rrttl, &rdata_type, &rdata)) {
         uint8_t extended_rcode;
-        edns_obj = PyObject_New(s_EDNS, &edns_type);
+        s_EDNS* edns_obj = PyObject_New(s_EDNS, &edns_type);
         if (edns_obj == NULL) {
             return (NULL);
         }
diff --git a/src/lib/dns/python/tests/message_python_test.py b/src/lib/dns/python/tests/message_python_test.py
index 6f32b11..b9c0d5c 100644
--- a/src/lib/dns/python/tests/message_python_test.py
+++ b/src/lib/dns/python/tests/message_python_test.py
@@ -453,7 +453,7 @@ class MessageTest(unittest.TestCase):
 
     def test_to_text(self):
         message_render = create_message()
-        
+
         msg_str =\
 """;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4149
 ;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
@@ -484,7 +484,7 @@ test.example.com. 3600 IN A 192.0.2.2
                           Message.from_wire, self.p, bytes())
 
         test_name = Name("test.example.com");
-        
+
         message_parse = Message(0)
         factoryFromFile(message_parse, "message_fromWire1")
         self.assertEqual(0x1035, message_parse.get_qid())
@@ -493,7 +493,7 @@ test.example.com. 3600 IN A 192.0.2.2
         self.assertTrue(message_parse.get_header_flag(Message.HEADERFLAG_QR))
         self.assertTrue(message_parse.get_header_flag(Message.HEADERFLAG_RD))
         self.assertTrue(message_parse.get_header_flag(Message.HEADERFLAG_AA))
-    
+
         #QuestionPtr q = *message_parse.beginQuestion()
         q = message_parse.get_question()[0]
         self.assertEqual(test_name, q.get_name())
@@ -503,7 +503,7 @@ test.example.com. 3600 IN A 192.0.2.2
         self.assertEqual(2, message_parse.get_rr_count(Message.SECTION_ANSWER))
         self.assertEqual(0, message_parse.get_rr_count(Message.SECTION_AUTHORITY))
         self.assertEqual(0, message_parse.get_rr_count(Message.SECTION_ADDITIONAL))
-    
+
         #RRsetPtr rrset = *message_parse.beginSection(Message.SECTION_ANSWER)
         rrset = message_parse.get_section(Message.SECTION_ANSWER)[0]
         self.assertEqual(test_name, rrset.get_name())
@@ -569,12 +569,12 @@ test.example.com. 3600 IN A 192.0.2.2
         message_parse = Message(Message.PARSE)
         factoryFromFile(message_parse, "message_fromWire10.wire")
         self.assertEqual(Rcode.BADVERS(), message_parse.get_rcode())
-    
+
         # Maximum extended Rcode
         message_parse.clear(Message.PARSE)
         factoryFromFile(message_parse, "message_fromWire11.wire")
         self.assertEqual(0xfff, message_parse.get_rcode().get_code())
-    
+
     def test_BadEDNS0(self):
         message_parse = Message(Message.PARSE)
         # OPT RR in the answer section
@@ -596,7 +596,7 @@ test.example.com. 3600 IN A 192.0.2.2
                           factoryFromFile,
                           message_parse,
                           "message_fromWire6")
-                          
+
         # Compressed owner name of OPT RR points to a root name.
         # Not necessarily bogus, but very unusual and mostly pathological.
         # We accept it, but is it okay?
diff --git a/src/lib/dns/python/tests/tsig_python_test.py b/src/lib/dns/python/tests/tsig_python_test.py
index 7e5515d..4d99175 100644
--- a/src/lib/dns/python/tests/tsig_python_test.py
+++ b/src/lib/dns/python/tests/tsig_python_test.py
@@ -122,15 +122,23 @@ class TSIGContextTest(unittest.TestCase):
         # And there should be no error code.
         self.assertEqual(TSIGError(Rcode.NOERROR()), self.tsig_ctx.get_error())
 
+        # No message signed yet
+        self.assertRaises(TSIGContextError, self.tsig_ctx.last_had_signature)
+
     # Note: intentionally use camelCase so that we can easily copy-paste
     # corresponding C++ tests.
     def commonVerifyChecks(self, ctx, record, data, expected_error,
                            expected_new_state=\
-                               TSIGContext.STATE_VERIFIED_RESPONSE):
+                               TSIGContext.STATE_VERIFIED_RESPONSE,
+                           last_should_throw=False):
         self.assertEqual(expected_error, ctx.verify(record, data))
         self.assertEqual(expected_error, ctx.get_error())
         self.assertEqual(expected_new_state, ctx.get_state())
-
+        if last_should_throw:
+            self.assertRaises(TSIGContextError, ctx.last_had_signature)
+        else:
+            self.assertEqual(record is not None,
+                             ctx.last_had_signature())
     def test_from_keyring(self):
         # Construct a TSIG context with an empty key ring.  Key shouldn't be
         # found, and the BAD_KEY error should be recorded.
@@ -354,7 +362,7 @@ class TSIGContextTest(unittest.TestCase):
 
         tsig = self.createMessageAndSign(self.qid, self.test_name,
                                          self.tsig_ctx, 0, RRType.SOA())
-                                         
+
         fix_current_time(0x4da8b9d6 + 301)
         self.assertEqual(TSIGError.BAD_TIME,
                          self.tsig_verify_ctx.verify(tsig, DUMMY_DATA))
@@ -454,7 +462,8 @@ class TSIGContextTest(unittest.TestCase):
         self.createMessageAndSign(self.qid, self.test_name, self.tsig_ctx)
 
         self.commonVerifyChecks(self.tsig_ctx, None, DUMMY_DATA,
-                           TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST)
+                           TSIGError.FORMERR, TSIGContext.STATE_SENT_REQUEST,
+                           True)
 
         self.createMessageFromFile("tsig_verify5.wire")
         self.commonVerifyChecks(self.tsig_ctx, self.message.get_tsig_record(),
diff --git a/src/lib/dns/python/tsig_python.cc b/src/lib/dns/python/tsig_python.cc
index 0764e33..abb7733 100644
--- a/src/lib/dns/python/tsig_python.cc
+++ b/src/lib/dns/python/tsig_python.cc
@@ -66,6 +66,7 @@ PyObject* TSIGContext_getState(s_TSIGContext* self);
 PyObject* TSIGContext_getError(s_TSIGContext* self);
 PyObject* TSIGContext_sign(s_TSIGContext* self, PyObject* args);
 PyObject* TSIGContext_verify(s_TSIGContext* self, PyObject* args);
+PyObject* TSIGContext_lastHadSignature(s_TSIGContext* self);
 
 // These are the functions we export
 // For a minimal support, we don't need them.
@@ -89,6 +90,9 @@ PyMethodDef TSIGContext_methods[] = {
     { "verify",
       reinterpret_cast<PyCFunction>(TSIGContext_verify), METH_VARARGS,
       "Verify a DNS message." },
+    { "last_had_signature",
+      reinterpret_cast<PyCFunction>(TSIGContext_lastHadSignature), METH_NOARGS,
+      "Return True if the last verified message contained a signature" },
     { NULL, NULL, 0, NULL }
 };
 
@@ -234,6 +238,26 @@ TSIGContext_verify(s_TSIGContext* self, PyObject* args) {
 
     return (NULL);
 }
+
+PyObject*
+TSIGContext_lastHadSignature(s_TSIGContext* self) {
+    try {
+        long result = self->cppobj->lastHadSignature();
+        return (PyBool_FromLong(result));
+    } catch (const TSIGContextError& ex) {
+        PyErr_SetString(po_TSIGContextError, ex.what());
+    } catch (const exception& ex) {
+        const string ex_what =
+            "Unexpected failure in TSIG lastHadSignature: " +
+            string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in TSIG lastHadSignature");
+    }
+
+    return (NULL);
+}
 } // end of unnamed namespace
 
 namespace isc {
diff --git a/src/lib/dns/rdata/generic/afsdb_18.cc b/src/lib/dns/rdata/generic/afsdb_18.cc
index 6afc4de..ec76ee0 100644
--- a/src/lib/dns/rdata/generic/afsdb_18.cc
+++ b/src/lib/dns/rdata/generic/afsdb_18.cc
@@ -57,7 +57,6 @@ AFSDB::AFSDB(const std::string& afsdb_str) :
     try {
         const uint32_t subtype = tokenToNum<int32_t, 16>(getToken(iss));
         const Name servername(getToken(iss));
-        string server;
 
         if (!iss.eof()) {
             isc_throw(InvalidRdataText, "Unexpected input for AFSDB"
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
index b7463b2..62cbcec 100644
--- a/src/lib/dns/tests/labelsequence_unittest.cc
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <util/buffer.h>
+
 #include <dns/labelsequence.h>
 #include <dns/name.h>
 #include <exceptions/exceptions.h>
@@ -772,6 +774,37 @@ TEST_F(LabelSequenceTest, serialize) {
         1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' };
     expected.push_back(DataPair(sizeof(expected_data5), expected_data5));
 
+    // Labels containing a longest possible label
+    const Name name_longlabel(std::string(63, 'x')); // 63 'x's
+    LabelSequence ls_longlabel(name_longlabel);
+    actual_labelseqs.push_back(ls_longlabel);
+    vector<uint8_t> expected_data6;
+    expected_data6.push_back(2); // 2 labels
+    expected_data6.push_back(0); // 1st offset
+    expected_data6.push_back(64); // 2nd offset
+    expected_data6.push_back(63); // 1st label length
+    expected_data6.insert(expected_data6.end(), 63, 'x'); // 1st label: 63 'x's
+    expected_data6.push_back(0); // 2nd label: trailing 0
+    expected.push_back(DataPair(expected_data6.size(), &expected_data6[0]));
+
+    // Max number of labels and longest possible name
+    EXPECT_EQ(Name::MAX_WIRE, n_maxlabel.getLength());
+    LabelSequence ls_maxlabel(n_maxlabel);
+    actual_labelseqs.push_back(ls_maxlabel);
+    vector<uint8_t> expected_data7;
+    expected_data7.push_back(Name::MAX_LABELS); // number of labels
+    for (size_t i = 0; i < Name::MAX_LABELS; ++i) {
+        expected_data7.push_back(i * 2); // each label has length and 1 byte
+    }
+    // Copy wire data of the name
+    isc::util::OutputBuffer ob(0);
+    n_maxlabel.toWire(ob);
+    expected_data7.insert(expected_data7.end(),
+                          static_cast<const uint8_t*>(ob.getData()),
+                          static_cast<const uint8_t*>(ob.getData()) +
+                          ob.getLength());
+    expected.push_back(DataPair(expected_data7.size(), &expected_data7[0]));
+
     // For each data set, serialize the labels and compare the data to the
     // expected one.
     vector<DataPair>::const_iterator it = expected.begin();
@@ -827,8 +860,12 @@ TEST_F(LabelSequenceTest, badDeserialize) {
     const uint8_t toomany_offsets[] = { Name::MAX_LABELS + 1 };
     EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue);
 
-    // exceed MAX_LABEL_LEN
-    const uint8_t offsets_toolonglabel[] = { 2, 0, 64 };
+    // (second) offset does not match actual label length
+    const uint8_t offsets_wrongoffset[] = { 2, 0, 64, 1 };
+    EXPECT_THROW(LabelSequence ls(offsets_wrongoffset), isc::BadValue);
+
+    // offset matches, but exceeds MAX_LABEL_LEN
+    const uint8_t offsets_toolonglabel[] = { 2, 0, 64, 64 };
     EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue);
 
     // Inconsistent data: an offset is lower than the previous offset
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index 33e677f..835aa48 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -534,7 +534,7 @@ TEST_F(MessageTest, appendSection) {
         RRClass::IN(), RRType::A()));
     EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name,
         RRClass::IN(), RRType::AAAA()));
-    
+
 }
 
 TEST_F(MessageTest, parseHeader) {
@@ -1091,7 +1091,7 @@ TEST_F(MessageTest, toWireWithoutRcode) {
 TEST_F(MessageTest, toText) {
     // Check toText() output for a typical DNS response with records in
     // all sections
-    
+
     factoryFromFile(message_parse, "message_toText1.wire");
     {
         SCOPED_TRACE("Message toText test (basic case)");
diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc
index e607c74..4ef0c7b 100644
--- a/src/lib/dns/tests/nsec3hash_unittest.cc
+++ b/src/lib/dns/tests/nsec3hash_unittest.cc
@@ -20,11 +20,14 @@
 
 #include <dns/nsec3hash.h>
 #include <dns/rdataclass.h>
+#include <util/encode/hex.h>
 
 using boost::scoped_ptr;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
+using namespace isc::util;
+using namespace isc::util::encode;
 
 namespace {
 typedef scoped_ptr<NSEC3Hash> NSEC3HashPtr;
@@ -39,7 +42,10 @@ protected:
         test_hash_nsec3(NSEC3Hash::create(generic::NSEC3
                                           ("1 0 12 aabbccdd " +
                                            string(nsec3_common))))
-    {}
+    {
+        const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
+        test_hash_args.reset(NSEC3Hash::create(1, 12, salt, sizeof(salt)));
+    }
 
     ~NSEC3HashTest() {
         // Make sure we reset the hash creator to the default
@@ -53,6 +59,9 @@ protected:
 
     // Similar to test_hash, but created from NSEC3 RR.
     NSEC3HashPtr test_hash_nsec3;
+
+    // Similar to test_hash, but created from passed args.
+    NSEC3HashPtr test_hash_args;
 };
 
 TEST_F(NSEC3HashTest, unknownAlgorithm) {
@@ -65,6 +74,10 @@ TEST_F(NSEC3HashTest, unknownAlgorithm) {
                          generic::NSEC3("2 0 12 aabbccdd " +
                                         string(nsec3_common)))),
                      UnknownNSEC3HashAlgorithm);
+
+    const uint8_t salt[] = {0xaa, 0xbb, 0xcc, 0xdd};
+    EXPECT_THROW(NSEC3HashPtr(NSEC3Hash::create(2, 12, salt, sizeof(salt))),
+                 UnknownNSEC3HashAlgorithm);
 }
 
 // Common checks for NSEC3 hash calculation
@@ -90,6 +103,10 @@ TEST_F(NSEC3HashTest, calculate) {
         SCOPED_TRACE("calculate check with NSEC3 based hash");
         calculateCheck(*test_hash_nsec3);
     }
+    {
+        SCOPED_TRACE("calculate check with args based hash");
+        calculateCheck(*test_hash_args);
+    }
 
     // Some boundary cases: 0-iteration and empty salt.  Borrowed from the
     // .com zone data.
@@ -177,6 +194,11 @@ public:
         }
         return (new TestNSEC3Hash);
     }
+    virtual NSEC3Hash* create(uint8_t, uint16_t,
+                              const uint8_t*, size_t) const {
+        isc_throw(isc::Unexpected,
+                  "This method is not implemented here.");
+    }
 private:
     DefaultNSEC3HashCreator default_creator_;
 };
diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc
index d2bec5c..c155b53 100644
--- a/src/lib/dns/tests/rrparamregistry_unittest.cc
+++ b/src/lib/dns/tests/rrparamregistry_unittest.cc
@@ -37,6 +37,7 @@ protected:
     {
         ostringstream oss1;
         oss1 << test_class_code;
+        // cppcheck-suppress useInitializationList
         test_class_unknown_str = "CLASS" + oss1.str();
 
         ostringstream oss2;
@@ -60,7 +61,7 @@ protected:
 
     // we assume class/type numbers are officially unassigned.  If not we'll
     // need to update the test cases.
-    static const uint16_t test_class_code = 65533; 
+    static const uint16_t test_class_code = 65533;
     static const uint16_t test_type_code = 65534;
     static const string test_class_str;
     static const string test_type_str;
@@ -77,7 +78,7 @@ TEST_F(RRParamRegistryTest, addRemove) {
 
     // the first removal attempt should succeed
     EXPECT_TRUE(RRParamRegistry::getRegistry().removeType(test_type_code));
-    // then toText() should treat it as an "unknown" 
+    // then toText() should treat it as an "unknown"
     EXPECT_EQ(test_type_unknown_str, RRType(test_type_code).toText());
     // attempt of removing non-existent mapping should result in 'false'
     EXPECT_FALSE(RRParamRegistry::getRegistry().removeType(test_type_code));
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index ac503e5..458a6e0 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -66,6 +66,22 @@ testGetTime() {
     return (NOW);
 }
 
+// Thin wrapper around TSIGContext to allow access to the
+// update method.
+class TestTSIGContext : public TSIGContext {
+public:
+    TestTSIGContext(const TSIGKey& key) :
+        TSIGContext(key)
+    {}
+    TestTSIGContext(const Name& key_name, const Name& algorithm_name,
+                    const TSIGKeyRing& keyring) :
+        TSIGContext(key_name, algorithm_name, keyring)
+    {}
+    void update(const void* const data, size_t len) {
+        TSIGContext::update(data, len);
+    }
+};
+
 class TSIGTest : public ::testing::Test {
 protected:
     TSIGTest() :
@@ -83,9 +99,10 @@ protected:
         isc::util::detail::gettimeFunction = NULL;
 
         decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret);
-        tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
-                                               TSIGKey::HMACMD5_NAME(),
-                                               &secret[0], secret.size())));
+        tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                                   TSIGKey::HMACMD5_NAME(),
+                                                   &secret[0],
+                                                   secret.size())));
         tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name,
                                                       TSIGKey::HMACMD5_NAME(),
                                                       &secret[0],
@@ -116,7 +133,7 @@ protected:
     static const unsigned int AA_FLAG = 0x2;
     static const unsigned int RD_FLAG = 0x4;
 
-    boost::scoped_ptr<TSIGContext> tsig_ctx;
+    boost::scoped_ptr<TestTSIGContext> tsig_ctx;
     boost::scoped_ptr<TSIGContext> tsig_verify_ctx;
     TSIGKeyRing keyring;
     const uint16_t qid;
@@ -166,16 +183,20 @@ TSIGTest::createMessageAndSign(uint16_t id, const Name& qname,
         message.addRRset(Message::SECTION_ANSWER, answer_rrset);
     }
     renderer.clear();
-    message.toWire(renderer);
 
     TSIGContext::State expected_new_state =
         (ctx->getState() == TSIGContext::INIT) ?
         TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE;
-    ConstTSIGRecordPtr tsig = ctx->sign(id, renderer.getData(),
-                                        renderer.getLength());
+
+    message.toWire(renderer, *ctx);
+
+    message.clear(Message::PARSE);
+    InputBuffer buffer(renderer.getData(), renderer.getLength());
+    message.fromWire(buffer);
+
     EXPECT_EQ(expected_new_state, ctx->getState());
 
-    return (tsig);
+    return (ConstTSIGRecordPtr(new TSIGRecord(*message.getTSIGRecord())));
 }
 
 void
@@ -218,11 +239,17 @@ void
 commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record,
                    const void* data, size_t data_len, TSIGError expected_error,
                    TSIGContext::State expected_new_state =
-                   TSIGContext::VERIFIED_RESPONSE)
+                   TSIGContext::VERIFIED_RESPONSE,
+                   bool last_should_throw = false)
 {
     EXPECT_EQ(expected_error, ctx.verify(record, data, data_len));
     EXPECT_EQ(expected_error, ctx.getError());
     EXPECT_EQ(expected_new_state, ctx.getState());
+    if (last_should_throw) {
+        EXPECT_THROW(ctx.lastHadSignature(), TSIGContextError);
+    } else {
+        EXPECT_EQ(record != NULL, ctx.lastHadSignature());
+    }
 }
 
 TEST_F(TSIGTest, initialState) {
@@ -231,6 +258,9 @@ TEST_F(TSIGTest, initialState) {
 
     // And there should be no error code.
     EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError());
+
+    // Nothing verified yet
+    EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
 }
 
 TEST_F(TSIGTest, constructFromKeyRing) {
@@ -354,10 +384,17 @@ TEST_F(TSIGTest, verifyBadData) {
                                   12 + dummy_record.getLength() - 1),
                  InvalidParameter);
 
+    // Still nothing verified
+    EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
+
     // And the data must not be NULL.
     EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL,
                                   12 + dummy_record.getLength()),
                  InvalidParameter);
+
+    // Still nothing verified
+    EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError);
+
 }
 
 #ifdef ENABLE_CUSTOM_OPERATOR_NEW
@@ -726,8 +763,8 @@ TEST_F(TSIGTest, badsigResponse) {
 TEST_F(TSIGTest, badkeyResponse) {
     // A similar test as badsigResponse but for BADKEY
     isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
-    tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
-                                   keyring));
+    tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+                                       keyring));
     {
         SCOPED_TRACE("Verify resulting in BADKEY");
         commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0],
@@ -806,7 +843,7 @@ TEST_F(TSIGTest, nosigThenValidate) {
         SCOPED_TRACE("Verify a response without TSIG that should exist");
         commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0],
                            dummy_data.size(), TSIGError::FORMERR(),
-                           TSIGContext::SENT_REQUEST);
+                           TSIGContext::SENT_REQUEST, true);
     }
 
     createMessageFromFile("tsig_verify5.wire");
@@ -936,45 +973,47 @@ TEST_F(TSIGTest, getTSIGLength) {
     EXPECT_EQ(85, tsig_ctx->getTSIGLength());
 
     // hmac-sha1: n2=11, x=20
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
-                                           &dummy_data[0], 20)));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACSHA1_NAME(),
+                                               &dummy_data[0], 20)));
     EXPECT_EQ(74, tsig_ctx->getTSIGLength());
 
     // hmac-sha256: n2=13, x=32
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
-                                           TSIGKey::HMACSHA256_NAME(),
-                                           &dummy_data[0], 32)));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACSHA256_NAME(),
+                                               &dummy_data[0], 32)));
     EXPECT_EQ(88, tsig_ctx->getTSIGLength());
 
     // hmac-sha224: n2=13, x=28
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
-                                           TSIGKey::HMACSHA224_NAME(),
-                                           &dummy_data[0], 28)));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACSHA224_NAME(),
+                                               &dummy_data[0], 28)));
     EXPECT_EQ(84, tsig_ctx->getTSIGLength());
 
     // hmac-sha384: n2=13, x=48
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
-                                           TSIGKey::HMACSHA384_NAME(),
-                                           &dummy_data[0], 48)));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACSHA384_NAME(),
+                                               &dummy_data[0], 48)));
     EXPECT_EQ(104, tsig_ctx->getTSIGLength());
 
     // hmac-sha512: n2=13, x=64
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
-                                           TSIGKey::HMACSHA512_NAME(),
-                                           &dummy_data[0], 64)));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACSHA512_NAME(),
+                                               &dummy_data[0], 64)));
     EXPECT_EQ(120, tsig_ctx->getTSIGLength());
 
     // bad key case: n1=len(badkey.example.com)=20, n2=26, x=0
-    tsig_ctx.reset(new TSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
-                                   keyring));
+    tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(),
+                                       keyring));
     EXPECT_EQ(72, tsig_ctx->getTSIGLength());
 
     // bad sig case: n1=17, n2=26, x=0
     isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
     createMessageFromFile("message_toWire2.wire");
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
-                                           &dummy_data[0],
-                                           dummy_data.size())));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACMD5_NAME(),
+                                               &dummy_data[0],
+                                               dummy_data.size())));
     {
         SCOPED_TRACE("Verify resulting in BADSIG");
         commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
@@ -985,9 +1024,10 @@ TEST_F(TSIGTest, getTSIGLength) {
 
     // bad time case: n1=17, n2=26, x=16, y=6
     isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>;
-    tsig_ctx.reset(new TSIGContext(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(),
-                                           &dummy_data[0],
-                                           dummy_data.size())));
+    tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name,
+                                               TSIGKey::HMACMD5_NAME(),
+                                               &dummy_data[0],
+                                               dummy_data.size())));
     {
         SCOPED_TRACE("Verify resulting in BADTIME");
         commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(),
@@ -998,4 +1038,114 @@ TEST_F(TSIGTest, getTSIGLength) {
     EXPECT_EQ(91, tsig_ctx->getTSIGLength());
 }
 
+// Verify a stream of multiple messages. Some of them have a signature omitted.
+//
+// We have two contexts, one that signs, another that verifies.
+TEST_F(TSIGTest, verifyMulti) {
+    isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>;
+
+    // First, send query from the verify one to the normal one, so
+    // we initialize something like AXFR
+    {
+        SCOPED_TRACE("Query");
+        ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+                                                       tsig_verify_ctx.get());
+        commonVerifyChecks(*tsig_ctx, tsig.get(),
+                           renderer.getData(), renderer.getLength(),
+                           TSIGError(Rcode::NOERROR()),
+                           TSIGContext::RECEIVED_REQUEST);
+    }
+
+    {
+        SCOPED_TRACE("First message");
+        ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+                                                       tsig_ctx.get());
+        commonVerifyChecks(*tsig_verify_ctx, tsig.get(),
+                           renderer.getData(), renderer.getLength(),
+                           TSIGError(Rcode::NOERROR()),
+                           TSIGContext::VERIFIED_RESPONSE);
+        EXPECT_TRUE(tsig_verify_ctx->lastHadSignature());
+    }
+
+    {
+        SCOPED_TRACE("Second message");
+        ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+                                                       tsig_ctx.get());
+        commonVerifyChecks(*tsig_verify_ctx, tsig.get(),
+                           renderer.getData(), renderer.getLength(),
+                           TSIGError(Rcode::NOERROR()),
+                           TSIGContext::VERIFIED_RESPONSE);
+        EXPECT_TRUE(tsig_verify_ctx->lastHadSignature());
+    }
+
+    {
+        SCOPED_TRACE("Third message. Unsigned.");
+        // Another message does not carry the TSIG on it. But it should
+        // be OK, it's in the middle of stream.
+        message.clear(Message::RENDER);
+        message.setQid(1234);
+        message.setOpcode(Opcode::QUERY());
+        message.setRcode(Rcode::NOERROR());
+        RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(),
+                                        test_ttl));
+        answer_rrset->addRdata(createRdata(RRType::A(), test_class,
+                                           "192.0.2.1"));
+        message.addRRset(Message::SECTION_ANSWER, answer_rrset);
+        message.toWire(renderer);
+        // Update the internal state. We abuse the knowledge of
+        // internals here a little bit to generate correct test data
+        tsig_ctx->update(renderer.getData(), renderer.getLength());
+
+        commonVerifyChecks(*tsig_verify_ctx, NULL,
+                           renderer.getData(), renderer.getLength(),
+                           TSIGError(Rcode::NOERROR()),
+                           TSIGContext::VERIFIED_RESPONSE);
+
+        EXPECT_FALSE(tsig_verify_ctx->lastHadSignature());
+    }
+
+    {
+        SCOPED_TRACE("Fourth message. Signed again.");
+        ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name,
+                                                       tsig_ctx.get());
+        commonVerifyChecks(*tsig_verify_ctx, tsig.get(),
+                           renderer.getData(), renderer.getLength(),
+                           TSIGError(Rcode::NOERROR()),
+                           TSIGContext::VERIFIED_RESPONSE);
+        EXPECT_TRUE(tsig_verify_ctx->lastHadSignature());
+    }
+
+    {
+        SCOPED_TRACE("Filling in bunch of unsigned messages");
+        for (size_t i = 0; i < 100; ++i) {
+            SCOPED_TRACE(i);
+            // Another message does not carry the TSIG on it. But it should
+            // be OK, it's in the middle of stream.
+            message.clear(Message::RENDER);
+            message.setQid(1234);
+            message.setOpcode(Opcode::QUERY());
+            message.setRcode(Rcode::NOERROR());
+            RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(),
+                                            test_ttl));
+            answer_rrset->addRdata(createRdata(RRType::A(), test_class,
+                                               "192.0.2.1"));
+            message.addRRset(Message::SECTION_ANSWER, answer_rrset);
+            message.toWire(renderer);
+            // Update the internal state. We abuse the knowledge of
+            // internals here a little bit to generate correct test data
+            tsig_ctx->update(renderer.getData(), renderer.getLength());
+
+            // 99 unsigned messages is OK. But the 100th must be signed, according
+            // to the RFC2845, section 4.4
+            commonVerifyChecks(*tsig_verify_ctx, NULL,
+                               renderer.getData(), renderer.getLength(),
+                               i == 99 ? TSIGError::FORMERR() :
+                                   TSIGError(Rcode::NOERROR()),
+                               TSIGContext::VERIFIED_RESPONSE);
+
+            EXPECT_FALSE(tsig_verify_ctx->lastHadSignature());
+        }
+    }
+}
+
 } // end namespace
diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc
index 1bda021..d7ffcf8 100644
--- a/src/lib/dns/tsig.cc
+++ b/src/lib/dns/tsig.cc
@@ -61,7 +61,8 @@ struct TSIGContext::TSIGContextImpl {
     TSIGContextImpl(const TSIGKey& key,
                     TSIGError error = TSIGError::NOERROR()) :
         state_(INIT), key_(key), error_(error),
-        previous_timesigned_(0), digest_len_(0)
+        previous_timesigned_(0), digest_len_(0),
+        last_sig_dist_(-1)
     {
         if (error == TSIGError::NOERROR()) {
             // In normal (NOERROR) case, the key should be valid, and we
@@ -137,7 +138,7 @@ struct TSIGContext::TSIGContextImpl {
     // performance bottleneck, we could have this class a buffer as a member
     // variable and reuse it throughout the object's lifetime.  Right now,
     // we prefer keeping the scope for local things as small as possible.
-    void digestPreviousMAC(HMACPtr hmac) const;
+    void digestPreviousMAC(HMACPtr hmac);
     void digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, uint32_t rrttl,
                              uint64_t time_signed, uint16_t fudge,
                              uint16_t error, uint16_t otherlen,
@@ -152,14 +153,25 @@ struct TSIGContext::TSIGContextImpl {
     uint64_t previous_timesigned_; // only meaningful for response with BADTIME
     size_t digest_len_;
     HMACPtr hmac_;
+    // This is the distance from the last verified signed message. Value of 0
+    // means the last message was signed. Special value -1 means there was no
+    // signed message yet.
+    int last_sig_dist_;
 };
 
 void
-TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) const {
+TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) {
     // We should have ensured the digest size fits 16 bits within this class
     // implementation.
     assert(previous_digest_.size() <= 0xffff);
 
+    if (previous_digest_.empty()) {
+        // The previous digest was already used. We're in the middle of
+        // TCP stream somewhere and we already pushed some unsigned message
+        // into the HMAC state.
+        return;
+    }
+
     OutputBuffer buffer(sizeof(uint16_t) + previous_digest_.size());
     const uint16_t previous_digest_len(previous_digest_.size());
     buffer.writeUint16(previous_digest_len);
@@ -414,11 +426,21 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
                   "TSIG verify attempt after sending a response");
     }
 
-    // This case happens when we sent a signed request and have received an
-    // unsigned response.  According to RFC2845 Section 4.6 this case should be
-    // considered a "format error" (although the specific error code
-    // wouldn't matter much for the caller).
     if (record == NULL) {
+        if (impl_->last_sig_dist_ >= 0 && impl_->last_sig_dist_ < 99) {
+            // It is not signed, but in the middle of TCP stream. We just
+            // update the HMAC state and consider this message OK.
+            update(data, data_len);
+            // This one is not signed, the last signed is one message further
+            // now.
+            impl_->last_sig_dist_++;
+            // No digest to return now. Just say it's OK.
+            return (impl_->postVerifyUpdate(TSIGError::NOERROR(), NULL, 0));
+        }
+        // This case happens when we sent a signed request and have received an
+        // unsigned response.  According to RFC2845 Section 4.6 this case should be
+        // considered a "format error" (although the specific error code
+        // wouldn't matter much for the caller).
         return (impl_->postVerifyUpdate(TSIGError::FORMERR(), NULL, 0));
     }
 
@@ -433,6 +455,9 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
         isc_throw(InvalidParameter, "TSIG verify: empty data is invalid");
     }
 
+    // This message is signed and we won't throw any more.
+    impl_->last_sig_dist_ = 0;
+
     // Check key: whether we first verify it with a known key or we verify
     // it using the consistent key in the context.  If the check fails we are
     // done with BADKEY.
@@ -520,5 +545,24 @@ TSIGContext::verify(const TSIGRecord* const record, const void* const data,
     return (impl_->postVerifyUpdate(TSIGError::BAD_SIG(), NULL, 0));
 }
 
+bool
+TSIGContext::lastHadSignature() const {
+    if (impl_->last_sig_dist_ == -1) {
+        isc_throw(TSIGContextError, "No message was verified yet");
+    }
+    return (impl_->last_sig_dist_ == 0);
+}
+
+void
+TSIGContext::update(const void* const data, size_t len) {
+    HMACPtr hmac(impl_->createHMAC());
+    // Use the previous digest and never use it again
+    impl_->digestPreviousMAC(hmac);
+    impl_->previous_digest_.clear();
+    // Push the message there
+    hmac->update(data, len);
+    impl_->hmac_ = hmac;
+}
+
 } // namespace dns
 } // namespace isc
diff --git a/src/lib/dns/tsig.h b/src/lib/dns/tsig.h
index 028d295..9ccc580 100644
--- a/src/lib/dns/tsig.h
+++ b/src/lib/dns/tsig.h
@@ -339,7 +339,6 @@ public:
     /// returns (without an exception being thrown), the internal state of
     /// the \c TSIGContext won't be modified.
     ///
-    /// \todo Support intermediate TCP DNS messages without TSIG (RFC2845 4.4)
     /// \todo Signature truncation support based on RFC4635
     ///
     /// \exception TSIGContextError Context already signed a response.
@@ -353,6 +352,19 @@ public:
     TSIGError verify(const TSIGRecord* const record, const void* const data,
                      const size_t data_len);
 
+    /// \brief Check whether the last verified message was signed.
+    ///
+    /// RFC2845 allows for some of the messages not to be signed. However,
+    /// the last message must be signed and the class has no knowledge if a
+    /// given message is the last one, therefore it can't check directly.
+    ///
+    /// It is up to the caller to check if the last verified message was signed
+    /// after all are verified by calling this function.
+    ///
+    /// \return If the last message was signed or not.
+    /// \exception TSIGContextError if no message was verified yet.
+    bool lastHadSignature() const;
+
     /// Return the expected length of TSIG RR after \c sign()
     ///
     /// This method returns the length of the TSIG RR that would be
@@ -401,6 +413,19 @@ public:
     static const uint16_t DEFAULT_FUDGE = 300;
     //@}
 
+protected:
+    /// \brief Update internal HMAC state by more data.
+    ///
+    /// This is used mostly internaly, when we need to verify a message without
+    /// TSIG signature in the middle of signed TCP stream. However, it is also
+    /// used in tests, so it's protected instead of private, to allow tests
+    /// in.
+    ///
+    /// It doesn't contain sanity checks, and it is not tested directly. But
+    /// we may want to add these one day to allow generating the skipped TSIG
+    /// messages too. Until then, do not use this method.
+    void update(const void* const data, size_t len);
+
 private:
     struct TSIGContextImpl;
     TSIGContextImpl* impl_;
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 86c5f20..ef62b2f 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -109,11 +109,13 @@ currentTime() {
     // Get a text representation of the current time.
     time_t curtime;
     time(&curtime);
-    char* buffer = ctime(&curtime);
+    struct tm* timeinfo;
+    timeinfo = localtime(&curtime);
 
-    // Convert to string and strip out the trailing newline
-    string current_time = buffer;
-    return (isc::util::str::trim(current_time));
+    char buffer[80];
+    strftime(buffer, 80, "%a %b %d %Y %H:%M", timeinfo);
+
+    return (std::string(buffer));
 }
 
 
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
index 6f95e5d..08c9084 100644
--- a/src/lib/log/tests/logger_example.cc
+++ b/src/lib/log/tests/logger_example.cc
@@ -118,7 +118,6 @@ int main(int argc, char** argv) {
     LoggerSpecification cur_spec(ROOT_NAME);// Current specification
     OutputOption        cur_opt;            // Current output option
     vector<LoggerSpecification> loggers;    // Set of logger specifications
-    vector<OutputOption>        options;    // Output options for logger
     std::string                 severity;   // Severity set for logger
 
     // Initialize logging system - set the root logger name.
diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py
index d225bee..84fead2 100644
--- a/src/lib/python/isc/acl/tests/dns_test.py
+++ b/src/lib/python/isc/acl/tests/dns_test.py
@@ -198,10 +198,6 @@ class RequestACLTest(unittest.TestCase):
         self.assertRaises(LoaderError, REQUEST_LOADER.load,
                           [{"action": "ACCEPT", "from": 4}])
         self.assertRaises(LoaderError, REQUEST_LOADER.load,
-                          '[{"action": "ACCEPT", "from": []}]')
-        self.assertRaises(LoaderError, REQUEST_LOADER.load,
-                          [{"action": "ACCEPT", "from": []}])
-        self.assertRaises(LoaderError, REQUEST_LOADER.load,
                           '[{"action": "ACCEPT", "key": 1}]')
         self.assertRaises(LoaderError, REQUEST_LOADER.load,
                           [{"action": "ACCEPT", "key": 1}])
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index a95316d..8464a01 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -575,7 +575,7 @@ class UIModuleCCSession(MultiConfigData):
         # for type any, we determine the 'type' by what value is set
         # (which would be either list or dict)
         cur_value, _ = self.get_value(identifier)
-        type_any = module_spec['item_type'] == 'any'
+        type_any = isc.config.config_data.spec_part_is_any(module_spec)
 
         # the specified element must be a list or a named_set
         if 'list_item_spec' in module_spec or\
@@ -603,7 +603,7 @@ class UIModuleCCSession(MultiConfigData):
             self._add_value_to_named_set(identifier, item_name,
                                          item_value)
         else:
-            raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named set")
+            raise isc.cc.data.DataTypeError(str(identifier) + " is not a list or a named set")
 
     def _remove_value_from_list(self, identifier, value):
         if value is None:
@@ -653,7 +653,7 @@ class UIModuleCCSession(MultiConfigData):
         # for type any, we determine the 'type' by what value is set
         # (which would be either list or dict)
         cur_value, _ = self.get_value(identifier)
-        type_any = module_spec['item_type'] == 'any'
+        type_any = isc.config.config_data.spec_part_is_any(module_spec)
 
         # there's two forms of 'remove from list'; the remove-value-from-list
         # form, and the 'remove-by-index' form. We can recognize the second
@@ -668,7 +668,7 @@ class UIModuleCCSession(MultiConfigData):
            (type_any and type(cur_value) == dict):
             self._remove_value_from_named_set(identifier, value_str)
         else:
-            raise isc.cc.data.DataNotFoundError(str(identifier) + " is not a list or a named_set")
+            raise isc.cc.data.DataTypeError(str(identifier) + " is not a list or a named_set")
 
 
 
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index 413d052..ae61e2a 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -45,6 +45,13 @@ def spec_part_is_named_set(spec_part):
        named_set specification, and False otherwise."""
     return (type(spec_part) == dict and 'named_set_item_spec' in spec_part)
 
+def spec_part_is_any(spec_part):
+    """Returns true if the given spec_part specifies an element of type
+       any, and False otherwise.
+    """
+    return (type(spec_part) == dict and 'item_type' in spec_part and
+            spec_part['item_type'] == "any")
+
 def check_type(spec_part, value):
     """Does nothing if the value is of the correct type given the
        specification part relevant for the value. Raises an
@@ -237,7 +244,8 @@ def spec_name_list(spec, prefix="", recurse=False):
         elif 'named_set_item_spec' in spec:
             # we added a '/' above, but in this one case we don't want it
             result.append(prefix[:-1])
-        else:
+        # ignore any
+        elif not spec_part_is_any(spec):
             for name in spec:
                 result.append(prefix + name + "/")
                 if recurse:
@@ -392,14 +400,25 @@ class MultiConfigData:
            identifier, or None if not found. The first part of the
            identifier (up to the first /) is interpreted as the module
            name. Returns None if not found, or if identifier is not a
-           string."""
+           string.
+           If an index is given for a List-type element, it returns
+           the specification of the list elements, not of the list itself
+           """
         if type(identifier) != str or identifier == "":
             return None
         if identifier[0] == '/':
             identifier = identifier[1:]
         module, sep, id = identifier.partition("/")
+        if id != "":
+            id, indices = isc.cc.data.split_identifier_list_indices(id)
+        else:
+            indices = None
         try:
-            return find_spec_part(self._specifications[module].get_config_spec(), id)
+            spec_part = find_spec_part(self._specifications[module].get_config_spec(), id)
+            if indices is not None and spec_part_is_list(spec_part):
+                return spec_part['list_item_spec']
+            else:
+                return spec_part
         except isc.cc.data.DataNotFoundError as dnfe:
             return None
         except KeyError as ke:
@@ -780,19 +799,75 @@ class MultiConfigData:
            indices and named_set names to the completion list. If
            the given item_name is for a list or named_set, it'll
            return a list of those (appended to item_name), otherwise
-           the list will only contain the item_name itself."""
+           the list will only contain the item_name itself.
+
+           If the item is a named set, and it's contents are maps
+           or named_sets as well, a / is appended to the result
+           strings.
+
+           If the item is a list, this method is then called recursively
+           for each list entry.
+
+           This behaviour is slightly arbitrary, and currently reflects
+           the most probable way the resulting data should look like;
+           for lists, bindctl would always expect their contents to
+           be added as well. For named_sets, however, we do not
+           do recursion, since the resulting list may be too long.
+           This will probably change in a revision of the way this
+           data is handled; ideally, the result should always recurse,
+           but only up to a limited depth, and the resulting list
+           should probably be paginated clientside.
+
+           Parameters:
+           item_name (string): the (full) identifier for the list or
+                               named_set to enumerate.
+
+           Returns a list of strings with item names
+
+           Examples:
+           _get_list_items("Module/some_item")
+               where item is not a list of a named_set, or where
+               said list or named set is empty, returns
+               ["Module/some_item"]
+           _get_list_items("Module/named_set")
+               where the named_set contains items with names 'a'
+               and 'b', returns
+               [ "Module/named_set/a", "Module/named_set/b" ]
+           _get_list_items("Module/named_set_of_maps")
+               where the named_set contains items with names 'a'
+               and 'b', and those items are maps themselves
+               (or other named_sets), returns
+               [ "Module/named_set/a/", "Module/named_set/b/" ]
+           _get_list_items("Module/list")
+               where the list contains 2 elements, returns
+               [ "Module/list[0]", "Module/list[1]" ]
+        """
         spec_part = self.find_spec_part(item_name)
-        if 'item_type' in spec_part and \
-           spec_part['item_type'] == 'named_set':
-            subslash = ""
-            if spec_part['named_set_item_spec']['item_type'] == 'map' or\
-               spec_part['named_set_item_spec']['item_type'] == 'named_set':
-                subslash = "/"
-            values, status = self.get_value(item_name)
-            if len(values) > 0:
+        if spec_part_is_named_set(spec_part):
+            values, _ = self.get_value(item_name)
+            if values is not None and len(values) > 0:
+                subslash = ""
+                if spec_part['named_set_item_spec']['item_type'] == 'map' or\
+                   spec_part['named_set_item_spec']['item_type'] == 'named_set':
+                    subslash = "/"
+                # Don't recurse for named_sets (so as not to return too
+                # much data), but do add a / so the client so that
+                # the user can immediately tab-complete further if needed.
                 return [ item_name + "/" + v + subslash for v in values.keys() ]
             else:
                 return [ item_name ]
+        elif spec_part_is_list(spec_part):
+            values, _ = self.get_value(item_name)
+            if values is not None and len(values) > 0:
+                result = []
+                for i in range(len(values)):
+                    name = item_name + '[%d]' % i
+                    # Recurse for list entries, so that its sub-contents
+                    # are also added to the result
+                    result.extend(self._get_list_items(name))
+                return result
+            else:
+                return [ item_name ]
         else:
             return [ item_name ]
 
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index 0101d50..ad364ac 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -993,10 +993,15 @@ class TestUIModuleCCSession(unittest.TestCase):
 
         self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, 1, "a")
         self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "no_such_item", "a")
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec2/item1", "a")
         self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, 1, "a")
         self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "no_such_item", "a")
-        self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "Spec2/item1", "a")
+        # add and remove should raise DataNotFoundError when used with items
+        # that are not a list or named_set (more importantly, they should
+        # not raise TypeError)
+        self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec2/item1", "a")
+        self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec2/item1", "a")
+        self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec2", "")
+        self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec2", "")
 
         self.assertEqual({}, uccs._local_changes)
         uccs.add_value("Spec2/item5", "foo")
@@ -1020,6 +1025,7 @@ class TestUIModuleCCSession(unittest.TestCase):
         self.assertRaises(isc.cc.data.DataTypeError,
                           uccs.remove_value, "Spec2/item5", None)
 
+
     # Check that the difference between no default and default = null
     # is recognized
     def test_default_null(self):
@@ -1042,9 +1048,9 @@ class TestUIModuleCCSession(unittest.TestCase):
         items_as_str = [ '1234', 'foo', 'true', 'false' ]
 
         def test_fails():
-            self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec40/item1", "foo")
-            self.assertRaises(isc.cc.data.DataNotFoundError, uccs.add_value, "Spec40/item1", "foo", "bar")
-            self.assertRaises(isc.cc.data.DataNotFoundError, uccs.remove_value, "Spec40/item1", "foo")
+            self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec40/item1", "foo")
+            self.assertRaises(isc.cc.data.DataTypeError, uccs.add_value, "Spec40/item1", "foo", "bar")
+            self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec40/item1", "foo")
             self.assertRaises(isc.cc.data.DataTypeError, uccs.remove_value, "Spec40/item1[0]", None)
 
         # A few helper functions to perform a number of tests
diff --git a/src/lib/python/isc/config/tests/config_data_test.py b/src/lib/python/isc/config/tests/config_data_test.py
index 86fbc11..45feb35 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -252,6 +252,18 @@ class TestConfigData(unittest.TestCase):
         self.assertRaises(ConfigDataError, spec_name_list, 1)
         self.assertRaises(ConfigDataError, spec_name_list, [ 'a' ])
 
+        # Test one with type any as well
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec40.spec")
+        spec_part = module_spec.get_config_spec()
+        name_list = spec_name_list(module_spec.get_config_spec())
+        self.assertEqual(['item1', 'item2', 'item3'], name_list)
+
+        # item3 itself is 'empty'
+        spec_part = find_spec_part(spec_part, 'item3')
+        name_list = spec_name_list(spec_part)
+        self.assertEqual([], name_list)
+
+
     def test_init(self):
         self.assertRaises(ConfigDataError, ConfigData, "asdf")
 
@@ -377,12 +389,37 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(None, spec_part)
         spec_part = self.mcd.find_spec_part("/Spec2/item1")
         self.assertEqual(None, spec_part)
-        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
+        module_spec = isc.config.module_spec_from_file(self.data_path +
+                                                       os.sep + "spec2.spec")
         self.mcd.set_specification(module_spec)
         spec_part = self.mcd.find_spec_part("Spec2/item1")
-        self.assertEqual({'item_name': 'item1', 'item_type': 'integer', 'item_optional': False, 'item_default': 1, }, spec_part)
+        self.assertEqual({'item_name': 'item1', 'item_type': 'integer',
+                          'item_optional': False, 'item_default': 1, },
+                         spec_part)
+
+        # For lists, either the spec of the list itself, or the
+        # spec for the list contents should be returned (the
+        # latter when an index is given in the identifier)
+        spec_part = self.mcd.find_spec_part("Spec2/item5")
+        self.assertEqual({'item_default': ['a', 'b'],
+                          'item_name': 'item5',
+                          'item_optional': False,
+                          'item_type': 'list',
+                          'list_item_spec': {'item_default': '',
+                                             'item_name': 'list_element',
+                                             'item_optional': False,
+                                             'item_type': 'string'}},
+                         spec_part)
+        spec_part = self.mcd.find_spec_part("Spec2/item5[0]")
+        self.assertEqual({'item_default': '',
+                          'item_name': 'list_element',
+                          'item_optional': False,
+                          'item_type': 'string'},
+                         spec_part)
+
 
     def test_find_spec_part_nested(self):
+        # Check that find_spec_part works for nested lists
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec30.spec")
         self.mcd.set_specification(module_spec)
         spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/final_element")
@@ -391,6 +428,7 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(None, spec_part)
 
     def test_find_spec_part_nested2(self):
+        # Check that find_spec_part works for nested lists and maps
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec31.spec")
         self.mcd.set_specification(module_spec)
         spec_part = self.mcd.find_spec_part("/lists/first_list_items[0]/second_list_items[1]/map_element/list1[1]/list2[2]")
@@ -638,7 +676,7 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(expected, maps)
 
         # A slash at the end should not produce different output with
-        # indices too
+        # indices either
         expected2 = [{'default': True,
                       'type': 'integer',
                       'name': 'Spec22/value5[1]',
@@ -720,6 +758,8 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertRaises(isc.cc.data.DataNotFoundError, self.mcd.unset, "Spec2/doesnotexist")
 
     def test_get_config_item_list(self):
+        # Test get_config_item_list(), which returns a list of the config
+        # items in a specification.
         config_items = self.mcd.get_config_item_list()
         self.assertEqual([], config_items)
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
@@ -729,15 +769,42 @@ class TestMultiConfigData(unittest.TestCase):
         config_items = self.mcd.get_config_item_list(None, False)
         self.assertEqual(['Spec2'], config_items)
         config_items = self.mcd.get_config_item_list(None, True)
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3',
+                          'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1',
+                          'Spec2/item6/value2'], config_items)
         config_items = self.mcd.get_config_item_list("Spec2", True)
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3',
+                          'Spec2/item4', 'Spec2/item5[0]', 'Spec2/item5[1]',
+                          'Spec2/item6/value1', 'Spec2/item6/value2'],
+                          config_items)
         config_items = self.mcd.get_config_item_list("Spec2")
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6'], config_items)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3',
+                          'Spec2/item4', 'Spec2/item5[0]', 'Spec2/item5[1]',
+                          'Spec2/item6'], config_items)
         config_items = self.mcd.get_config_item_list("/Spec2")
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6'], config_items)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3',
+                          'Spec2/item4', 'Spec2/item5[0]', 'Spec2/item5[1]',
+                          'Spec2/item6'], config_items)
+        config_items = self.mcd.get_config_item_list("Spec2", True)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3',
+                          'Spec2/item4', 'Spec2/item5[0]', 'Spec2/item5[1]',
+                          'Spec2/item6/value1', 'Spec2/item6/value2'],
+                          config_items)
+
+        # When lists are empty, it should only show the name
+        self.mcd.set_value('Spec2/item5', [])
         config_items = self.mcd.get_config_item_list("Spec2", True)
-        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3', 'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1', 'Spec2/item6/value2'], config_items)
+        self.assertEqual(['Spec2/item1', 'Spec2/item2', 'Spec2/item3',
+                          'Spec2/item4', 'Spec2/item5', 'Spec2/item6/value1',
+                          'Spec2/item6/value2'], config_items)
+
+        # Also if the list is None (optional value and no default)
+        module_spec = isc.config.module_spec_from_file(self.data_path
+                                                       + os.sep
+                                                       + "spec42.spec")
+        self.mcd.set_specification(module_spec)
+        config_items = self.mcd.get_config_item_list("Spec42", True)
+        self.assertEqual(['Spec42/list_item'], config_items)
 
     def test_is_named_set(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec")
@@ -756,7 +823,8 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(['Spec32'], config_items)
         config_items = self.mcd.get_config_item_list(None, True)
         self.assertEqual(['Spec32/named_set_item', 'Spec32/named_set_item2',
-                          'Spec32/named_set_item3'], config_items)
+                          'Spec32/named_set_item3', 'Spec32/named_set_item4'],
+                         config_items)
         self.mcd.set_value('Spec32/named_set_item', { "aaaa": 4, "aabb": 5,
                                                       "bbbb": 6})
         config_items = self.mcd.get_config_item_list("/Spec32/named_set_item",
@@ -766,6 +834,19 @@ class TestMultiConfigData(unittest.TestCase):
                           'Spec32/named_set_item/bbbb',
                          ], config_items)
 
+        self.mcd.set_value('Spec32/named_set_item', {})
+        config_items = self.mcd.get_config_item_list("/Spec32/named_set_item",
+                                                     True)
+        self.assertEqual(['Spec32/named_set_item'], config_items)
+
+        self.mcd.set_value('Spec32/named_set_item4', { "a": { "aa": 4 } } )
+        config_items = self.mcd.get_config_item_list("/Spec32/named_set_item4",
+                                                     True)
+        self.assertEqual(['Spec32/named_set_item4/a/'], config_items)
+        config_items = self.mcd.get_config_item_list("/Spec32/named_set_item4/a",
+                                                     True)
+        self.assertEqual(['Spec32/named_set_item4/a/aa'], config_items)
+
     def test_set_named_set_nonlocal(self):
         # Test whether a default named set is copied to local if a subitem
         # is changed, and that other items in the set do not get lost
diff --git a/src/lib/python/isc/datasrc/tests/clientlist_test.py b/src/lib/python/isc/datasrc/tests/clientlist_test.py
index 54b20e1..ea39d4e 100644
--- a/src/lib/python/isc/datasrc/tests/clientlist_test.py
+++ b/src/lib/python/isc/datasrc/tests/clientlist_test.py
@@ -28,6 +28,16 @@ class ClientListTest(unittest.TestCase):
     contain the ConfigurableClientList only.
     """
 
+    def tearDown(self):
+        # The unit test module could keep internal objects alive longer than
+        # we expect.  But cached zone finder and cache client cannot stay
+        # longer than the originating client list.  So we explicitly clean
+        # them up here.  The ordering is important: clist must be destroyed
+        # last.
+        self.dsrc = None
+        self.finder = None
+        self.clist = None
+
     def test_constructors(self):
         """
         Test the constructor. It should accept an RRClass. Check it
@@ -50,16 +60,16 @@ class ClientListTest(unittest.TestCase):
         ones are acceptend and invalid rejected. We check the changes
         have effect.
         """
-        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
+        self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
         # This should be NOP now
-        clist.configure("[]", True)
+        self.clist.configure("[]", True)
         # Check the zone is not there yet
-        dsrc, finder, exact = clist.find(isc.dns.Name("example.org"))
+        dsrc, finder, exact = self.clist.find(isc.dns.Name("example.org"))
         self.assertIsNone(dsrc)
         self.assertIsNone(finder)
         self.assertFalse(exact)
         # We can use this type, as it is not loaded dynamically.
-        clist.configure('''[{
+        self.clist.configure('''[{
             "type": "MasterFiles",
             "params": {
                 "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
@@ -68,38 +78,39 @@ class ClientListTest(unittest.TestCase):
         }]''', True)
         # Check the zone is there now. Proper tests of find are in other
         # test methods.
-        dsrc, finder, exact = clist.find(isc.dns.Name("example.org"))
-        self.assertIsNotNone(dsrc)
-        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
-        self.assertIsNotNone(finder)
-        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("example.org"))
+        self.assertIsNotNone(self.dsrc)
+        self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(self.finder)
+        self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
         self.assertTrue(exact)
-        self.assertRaises(isc.datasrc.Error, clist.configure, '"bad type"',
-                          True)
-        self.assertRaises(isc.datasrc.Error, clist.configure, '''[{
+        self.assertRaises(isc.datasrc.Error, self.clist.configure,
+                          '"bad type"', True)
+        self.assertRaises(isc.datasrc.Error, self.clist.configure, '''[{
             "type": "bad type"
         }]''', True)
-        self.assertRaises(isc.datasrc.Error, clist.configure, '''[{
+        self.assertRaises(isc.datasrc.Error, self.clist.configure, '''[{
             bad JSON,
         }]''', True)
-        self.assertRaises(TypeError, clist.configure, [], True)
-        self.assertRaises(TypeError, clist.configure, "[]")
-        self.assertRaises(TypeError, clist.configure, "[]", "true")
+        self.assertRaises(TypeError, self.clist.configure, [], True)
+        self.assertRaises(TypeError, self.clist.configure, "[]")
+        self.assertRaises(TypeError, self.clist.configure, "[]", "true")
 
     def test_find(self):
         """
         Test the find accepts the right arguments, some of them can be omitted,
         etc.
         """
-        clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
-        clist.configure('''[{
+        self.clist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.IN())
+        self.clist.configure('''[{
             "type": "MasterFiles",
             "params": {
                 "example.org": "''' + TESTDATA_PATH + '''example.org.zone"
             },
             "cache-enable": true
         }]''', True)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"))
+        dsrc, finder, exact = self.clist.find(isc.dns.Name("sub.example.org"))
         self.assertIsNotNone(dsrc)
         self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
         self.assertIsNotNone(finder)
@@ -112,33 +123,33 @@ class ClientListTest(unittest.TestCase):
         self.assertEqual(2, sys.getrefcount(dsrc))
         # We check an exact match in test_configure already
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         False)
-        self.assertIsNotNone(dsrc)
-        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
-        self.assertIsNotNone(finder)
-        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), False)
+        self.assertIsNotNone(self.dsrc)
+        self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(self.finder)
+        self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         True)
-        self.assertIsNone(dsrc)
-        self.assertIsNone(finder)
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), True)
+        self.assertIsNone(self.dsrc)
+        self.assertIsNone(self.finder)
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         False, False)
-        self.assertIsNotNone(dsrc)
-        self.assertTrue(isinstance(dsrc, isc.datasrc.DataSourceClient))
-        self.assertIsNotNone(finder)
-        self.assertTrue(isinstance(finder, isc.datasrc.ZoneFinder))
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), False, False)
+        self.assertIsNotNone(self.dsrc)
+        self.assertTrue(isinstance(self.dsrc, isc.datasrc.DataSourceClient))
+        self.assertIsNotNone(self.finder)
+        self.assertTrue(isinstance(self.finder, isc.datasrc.ZoneFinder))
         self.assertFalse(exact)
-        dsrc, finder, exact = clist.find(isc.dns.Name("sub.example.org"),
-                                         True, False)
-        self.assertIsNone(dsrc)
-        self.assertIsNone(finder)
+        self.dsrc, self.finder, exact = \
+            self.clist.find(isc.dns.Name("sub.example.org"), True, False)
+        self.assertIsNone(self.dsrc)
+        self.assertIsNone(self.finder)
         self.assertFalse(exact)
         # Some invalid inputs
-        self.assertRaises(TypeError, clist.find, "example.org")
-        self.assertRaises(TypeError, clist.find)
+        self.assertRaises(TypeError, self.clist.find, "example.org")
+        self.assertRaises(TypeError, self.clist.find)
 
 if __name__ == "__main__":
     isc.log.init("bind10")
diff --git a/src/lib/python/isc/ddns/session.py b/src/lib/python/isc/ddns/session.py
index 366bc8b..60834fb 100644
--- a/src/lib/python/isc/ddns/session.py
+++ b/src/lib/python/isc/ddns/session.py
@@ -199,8 +199,6 @@ class UpdateSession:
           logging and access control.
         - zone_config (ZoneConfig) A tentative container that encapsulates
           the server's zone configuration.  See zone_config.py.
-        - req_data (binary) Wire format data of the request message.
-          It will be used for TSIG verification if necessary.
 
         '''
         self.__message = req_message
diff --git a/src/lib/python/isc/testutils/tsigctx_mock.py b/src/lib/python/isc/testutils/tsigctx_mock.py
index a9af9b9..0158987 100644
--- a/src/lib/python/isc/testutils/tsigctx_mock.py
+++ b/src/lib/python/isc/testutils/tsigctx_mock.py
@@ -51,3 +51,6 @@ class MockTSIGContext(TSIGContext):
         if hasattr(self.error, '__call__'):
             return self.error(self)
         return self.error
+
+    def last_has_signature(self):
+        return True
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index 8d03c1c..c5b280f 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -176,7 +176,7 @@ class RunningQuery : public IOFetch::Callback {
 class ResolverNSASCallback : public isc::nsas::AddressRequestCallback {
 public:
     ResolverNSASCallback(RunningQuery* rq) : rq_(rq) {}
-    
+
     void success(const isc::nsas::NameserverAddress& address) {
         // Success callback, send query to found namesever
         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_SUCCESS)
@@ -184,7 +184,7 @@ public:
         rq_->nsasCallbackCalled();
         rq_->sendTo(address);
     }
-    
+
     void unreachable() {
         // Nameservers unreachable: drop query or send servfail?
         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CB, RESLIB_RUNQ_FAIL);
@@ -261,7 +261,7 @@ private:
     bool done_;
 
     // If we have a client timeout, we call back with a failure message,
-    // but we do not stop yet. We use this variable to make sure we 
+    // but we do not stop yet. We use this variable to make sure we
     // don't call back a second time later
     bool callback_called_;
 
@@ -270,7 +270,7 @@ private:
 
     // Reference to our cache
     isc::cache::ResolverCache& cache_;
-    
+
     // the 'current' zone we are in (i.e.) we start out at the root,
     // and for each delegation this gets updated with the zone the
     // delegation points to.
@@ -278,7 +278,7 @@ private:
     // of the call we use it in take a string, we need update those
     // too).
     std::string cur_zone_;
-    
+
     // This is the handler we pass on to the NSAS; it is called when
     // the NSAS has an address for us to query
     boost::shared_ptr<ResolverNSASCallback> nsas_callback_;
@@ -295,7 +295,7 @@ private:
 
     // The moment in time we sent a query to the nameserver above.
     struct timeval current_ns_qsent_time;
-    
+
     // RunningQuery deletes itself when it is done. In order for us
     // to do this safely, we must make sure that there are no events
     // that might call back to it. There are two types of events in
@@ -365,7 +365,7 @@ private:
             io_.get_io_service().post(query);
         }
     }
-    
+
     // 'general' send, ask the NSAS to give us an address.
     void send(IOFetch::Protocol protocol = IOFetch::UDP, bool edns = true) {
         protocol_ = protocol;   // Store protocol being used for this
@@ -397,7 +397,7 @@ private:
             nsas_.lookup(cur_zone_, question_.getClass(), nsas_callback_);
         }
     }
-    
+
     // Called by our NSAS callback handler so we know we do not have
     // an outstanding NSAS call anymore.
     void nsasCallbackCalled() {
@@ -422,13 +422,13 @@ private:
         // here (classify() will set it when it walks through
         // the cname chain to verify it).
         Name cname_target(question_.getName());
-        
+
         isc::resolve::ResponseClassifier::Category category =
             isc::resolve::ResponseClassifier::classify(
                 question_, incoming, cname_target, cname_count_);
 
         bool found_ns = false;
-            
+
         switch (category) {
         case isc::resolve::ResponseClassifier::ANSWER:
         case isc::resolve::ResponseClassifier::ANSWERCNAME:
@@ -569,7 +569,7 @@ private:
                 // SERVFAIL if we get FORMERR instead
             }
             goto SERVFAIL;
-            
+
         default:
 SERVFAIL:
             // Some error in received packet it.  Report it and return SERVFAIL
@@ -718,7 +718,7 @@ public:
             ++outstanding_events_;
             lookup_timer.async_wait(boost::bind(&RunningQuery::lookupTimeout, this));
         }
-        
+
         // Setup the timer to send an answer (client_timeout)
         if (client_timeout >= 0) {
             client_timer.expires_from_now(
@@ -726,7 +726,7 @@ public:
             ++outstanding_events_;
             client_timer.async_wait(boost::bind(&RunningQuery::clientTimeout, this));
         }
-        
+
         doLookup();
     }
 
@@ -741,7 +741,7 @@ public:
         --outstanding_events_;
         stop();
     }
-    
+
     // called if we have a client timeout; if our callback has
     // not been called, call it now. But do not stop.
     void clientTimeout() {
@@ -810,7 +810,7 @@ public:
         // XXX is this the place for TCP retry?
         assert(outstanding_events_ > 0);
         --outstanding_events_;
-        
+
         if (!done_ && result != IOFetch::TIME_OUT) {
             // we got an answer
 
@@ -890,7 +890,7 @@ public:
             stop();
         }
     }
-    
+
     // Clear the answer parts of answer_message, and set the rcode
     // to servfail
     void makeSERVFAIL() {
@@ -1096,7 +1096,7 @@ RecursiveQuery::resolve(const QuestionPtr& question,
         // Message found, return that
         LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_CACHE, RESLIB_RECQ_CACHE_FIND)
                   .arg(questionText(*question)).arg(1);
-        
+
         // TODO: err, should cache set rcode as well?
         answer_message->setRcode(Rcode::NOERROR());
         callback->success(answer_message);
@@ -1146,11 +1146,11 @@ RecursiveQuery::resolve(const Question& question,
     // TODO: general 'prepareinitialanswer'
     answer_message->setOpcode(isc::dns::Opcode::QUERY());
     answer_message->addQuestion(question);
-    
+
     // First try to see if we have something cached in the messagecache
     LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RESOLVE)
               .arg(questionText(question)).arg(2);
-    
+
     if (cache_.lookup(question.getName(), question.getType(),
                       question.getClass(), *answer_message) &&
         answer_message->getRRCount(Message::SECTION_ANSWER) > 0) {
@@ -1181,7 +1181,7 @@ RecursiveQuery::resolve(const Question& question,
             // delete itself when it is done
             LOG_DEBUG(isc::resolve::logger, RESLIB_DBG_TRACE, RESLIB_RECQ_CACHE_NO_FIND)
                       .arg(questionText(question)).arg(2);
-            new RunningQuery(io, question, answer_message, 
+            new RunningQuery(io, question, answer_message,
                              test_server_, buffer, crs, query_timeout_,
                              client_timeout_, lookup_timeout_, retries_,
                              nsas_, cache_, rtt_recorder_);
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
index 02721f1..4513458 100644
--- a/src/lib/resolve/tests/recursive_query_unittest.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -218,7 +218,7 @@ protected:
     }
 
     // Receive a UDP packet from a mock server; used for testing
-    // recursive lookup.  The caller must place a RecursiveQuery 
+    // recursive lookup.  The caller must place a RecursiveQuery
     // on the IO Service queue before running this routine.
     void recvUDP(const int family, void* buffer, size_t& size) {
         ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, true));
@@ -267,7 +267,7 @@ protected:
         if (ret < 0) {
             isc_throw(IOError, "recvfrom failed: " << strerror(errno));
         }
-        
+
         // Pass the message size back via the size parameter
         size = ret;
     }
@@ -693,37 +693,6 @@ createTestSocket() {
     return (sock.release());
 }
 
-int
-setSocketTimeout(int sock, size_t tv_sec, size_t tv_usec) {
-    const struct timeval timeo = { tv_sec, tv_usec };
-    int recv_options = 0;
-    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo))) {
-        if (errno == ENOPROTOOPT) { // see RecursiveQueryTest::recvUDP()
-            recv_options = MSG_DONTWAIT;
-        } else {
-            isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
-        }
-    }
-    return (recv_options);
-}
-
-// try to read from the socket max time
-// *num is incremented for every succesfull read
-// returns true if it can read max times, false otherwise
-bool tryRead(int sock, int recv_options, size_t max, int* num) {
-    size_t i = 0;
-    do {
-        char inbuff[512];
-        if (recv(sock, inbuff, sizeof(inbuff), recv_options) < 0) {
-            return false;
-        } else {
-            ++i;
-            ++*num;
-        }
-    } while (i < max);
-    return true;
-}
-
 // Mock resolver callback for testing forward query.
 class MockResolverCallback : public isc::resolve::ResolverInterface::Callback {
 public:
@@ -904,7 +873,7 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
 TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
     setDNSService(true, false);
     bool done;
-    
+
     MockServerStop server(io_service_, &done);
     vector<pair<string, uint16_t> > empty_vector;
     RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector,
@@ -930,7 +899,7 @@ TEST_F(RecursiveQueryTest, DISABLED_recursiveSendOk) {
 TEST_F(RecursiveQueryTest, DISABLED_recursiveSendNXDOMAIN) {
     setDNSService(true, false);
     bool done;
-    
+
     MockServerStop server(io_service_, &done);
     vector<pair<string, uint16_t> > empty_vector;
     RecursiveQuery rq(*dns_service_, *nsas_, cache_, empty_vector,
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
index 2b3d129..6cb404d 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -76,14 +76,17 @@ using namespace std;
 /// directed to one or other of the "servers" in the RecursiveQueryTest2 class,
 /// regardless of the glue returned in referrals.
 
+namespace {
+const char* const TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address
+const uint16_t TEST_PORT = 5301;              ///< ... and this port
+const size_t BUFFER_SIZE = 1024;              ///< For all buffers
+const char* const WWW_EXAMPLE_ORG = "192.0.2.254";
+                                              ///< Address of www.example.org
+} //end anonymous namespace
+
 namespace isc {
 namespace asiodns {
 
-const std::string TEST_ADDRESS = "127.0.0.1";   ///< Servers are on this address
-const uint16_t TEST_PORT = 5301;                ///< ... and this port
-const size_t BUFFER_SIZE = 1024;                ///< For all buffers
-const char* WWW_EXAMPLE_ORG = "192.0.2.254";    ///< Address of www.example.org
-
 // As the test is fairly long and complex, debugging "print" statements have
 // been left in although they are disabled.  Set the following to "true" to
 // enable them.
diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
index 168ec80..abfea9a 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_3.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -69,15 +69,16 @@ using namespace std;
 /// By using the "test_server_" element of RecursiveQuery, all queries are
 /// directed to one or other of the "servers" in the RecursiveQueryTest3 class.
 
-namespace isc {
-namespace asiodns {
-
-const std::string TEST_ADDRESS3 = "127.0.0.1"; 
-                                               ///< Servers are on this address
+namespace {
+const char* const TEST_ADDRESS3 = "127.0.0.1"; ///< Servers are on this address
 const uint16_t TEST_PORT3 = 5303;              ///< ... and this port
-const size_t BUFFER_SIZE = 1024;              ///< For all buffers
+const size_t BUFFER_SIZE = 1024;               ///< For all buffers
 
-const std::string DUMMY_ADDR3 = "1.2.3.4";     ///< address to return as A
+const char* const DUMMY_ADDR3 = "1.2.3.4";     ///< address to return as A
+} // end anonymous namespace
+
+namespace isc {
+namespace asiodns {
 
 class MockResolver3 : public isc::resolve::ResolverInterface {
 public:
diff --git a/src/lib/server_common/tests/socket_requestor_test.cc b/src/lib/server_common/tests/socket_requestor_test.cc
index 9878c63..ac1731f 100644
--- a/src/lib/server_common/tests/socket_requestor_test.cc
+++ b/src/lib/server_common/tests/socket_requestor_test.cc
@@ -410,7 +410,8 @@ private:
             isc_throw(Unexpected,
                       "mkstemp() created a filename too long for sun_path");
         }
-        strncpy(socket_address.sun_path, path_, len);
+        strncpy(socket_address.sun_path, path_,
+                sizeof(socket_address.sun_path));
 #ifdef HAVE_SA_LEN
         socket_address.sun_len = len;
 #endif
@@ -542,7 +543,6 @@ TEST_F(SocketRequestorTest, testSocketPassing) {
         EXPECT_EQ("foo", socket_id.second);
         EXPECT_EQ(0, close(socket_id.first));
     }
-
     // Create a second socket server, to test that multiple different
     // domains sockets would work as well (even though we don't actually
     // use that feature)
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index c56e4b8..fe1b327 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = . io unittests tests pyunittests python
+SUBDIRS = . io unittests tests pyunittests python threads
 
 AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
 AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
diff --git a/src/lib/util/hash/sha1.cc b/src/lib/util/hash/sha1.cc
index 091e293..1fdadfd 100644
--- a/src/lib/util/hash/sha1.cc
+++ b/src/lib/util/hash/sha1.cc
@@ -48,7 +48,7 @@
  *      merchantability of this software or the suitability of this
  *      software for any particular purpose.  It is provided "as is"
  *      without express or implied warranty of any kind.
- *      
+ *
  */
 #include <util/hash/sha1.h>
 
@@ -79,7 +79,7 @@ SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) {
     return ((x) ^ (y) ^ (z));
 }
 
-static inline int 
+static inline int
 SHA1CircularShift(uint8_t bits, uint32_t word) {
     return ((word << bits) | (word >> (32 - bits)));
 }
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
index 7adbbbe..8666441 100644
--- a/src/lib/util/io/fd_share.cc
+++ b/src/lib/util/io/fd_share.cc
@@ -142,6 +142,7 @@ send_fd(const int sock, const int fd) {
     if (msghdr.msg_control == NULL) {
         return (FD_OTHER_ERROR);
     }
+    std::memset(msghdr.msg_control, 0, msghdr.msg_controllen);
 
     struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msghdr);
     cmsg->cmsg_len = cmsg_len(sizeof(int));
diff --git a/src/lib/util/tests/fd_tests.cc b/src/lib/util/tests/fd_tests.cc
index 6ba2766..b709405 100644
--- a/src/lib/util/tests/fd_tests.cc
+++ b/src/lib/util/tests/fd_tests.cc
@@ -34,7 +34,9 @@ class FDTest : public ::testing::Test {
             // We do not care what is inside, we just need it to be the same
             data(new unsigned char[TEST_DATA_SIZE]),
             buffer(NULL)
-        { }
+        {
+            memset(data, 0, TEST_DATA_SIZE);
+        }
         ~ FDTest() {
             delete[] data;
             delete[] buffer;
diff --git a/src/lib/util/tests/lru_list_unittest.cc b/src/lib/util/tests/lru_list_unittest.cc
index bfb3b4d..c0201ea 100644
--- a/src/lib/util/tests/lru_list_unittest.cc
+++ b/src/lib/util/tests/lru_list_unittest.cc
@@ -168,7 +168,7 @@ protected:
         entry7_(new TestEntry("eta", 1))
     {}
 
-    virtual ~LruListTest() 
+    virtual ~LruListTest()
     {}
 
     boost::shared_ptr<TestEntry>    entry1_;
@@ -355,7 +355,7 @@ TEST_F(LruListTest, Dropped) {
     lru.add(entry5_);
     EXPECT_NE(0, (entry2_->getCode() & 0x8000));
 
-    // Delete an entry and check that the handler does not run. 
+    // Delete an entry and check that the handler does not run.
     EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
     lru.remove(entry3_);
     EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
@@ -386,7 +386,7 @@ TEST_F(LruListTest, Clear) {
     EXPECT_NE(0, (entry1_->getCode() & 0x8000));
     EXPECT_NE(0, (entry2_->getCode() & 0x8000));
     EXPECT_NE(0, (entry3_->getCode() & 0x8000));
- 
+
     EXPECT_EQ(0, lru.size());
 }
 
diff --git a/src/lib/util/threads/Makefile.am b/src/lib/util/threads/Makefile.am
new file mode 100644
index 0000000..7b9fb8f
--- /dev/null
+++ b/src/lib/util/threads/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = . tests
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+
+lib_LTLIBRARIES = libb10-threads.la
+libb10_threads_la_SOURCES  = lock.h lock.cc
+libb10_threads_la_SOURCES += thread.h thread.cc
+libb10_threads_la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/threads/lock.cc b/src/lib/util/threads/lock.cc
new file mode 100644
index 0000000..6a165aa
--- /dev/null
+++ b/src/lib/util/threads/lock.cc
@@ -0,0 +1,138 @@
+// 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 "lock.h"
+
+#include <exceptions/exceptions.h>
+
+#include <cstring>
+#include <memory>
+#include <cerrno>
+#include <cassert>
+
+#include <pthread.h>
+
+using std::auto_ptr;
+
+namespace isc {
+namespace util {
+namespace thread {
+
+class Mutex::Impl {
+public:
+    Impl() :
+        locked_count(0)
+    {}
+    pthread_mutex_t mutex;
+    // Only in debug mode
+    size_t locked_count;
+};
+
+namespace {
+
+struct Deinitializer {
+    Deinitializer(pthread_mutexattr_t& attributes):
+        attributes_(attributes)
+    {}
+    ~Deinitializer() {
+        const int result = pthread_mutexattr_destroy(&attributes_);
+        // This should never happen. According to the man page,
+        // if there's error, it's our fault.
+        assert(result == 0);
+    }
+    pthread_mutexattr_t& attributes_;
+};
+
+}
+
+Mutex::Mutex() :
+    impl_(NULL)
+{
+    pthread_mutexattr_t attributes;
+    int result = pthread_mutexattr_init(&attributes);
+    switch (result) {
+        case 0: // All 0K
+            break;
+        case ENOMEM:
+            throw std::bad_alloc();
+        default:
+            isc_throw(isc::InvalidOperation, std::strerror(result));
+    }
+    Deinitializer deinitializer(attributes);
+    // TODO: Distinguish if debug mode is enabled in compilation.
+    // If so, it should be PTHREAD_MUTEX_NORMAL or NULL
+    result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
+    if (result != 0) {
+        isc_throw(isc::InvalidOperation, std::strerror(result));
+    }
+    auto_ptr<Impl> impl(new Impl);
+    result = pthread_mutex_init(&impl->mutex, &attributes);
+    switch (result) {
+        case 0: // All 0K
+            impl_ = impl.release();
+            break;
+        case ENOMEM:
+        case EAGAIN:
+            throw std::bad_alloc();
+        default:
+            isc_throw(isc::InvalidOperation, std::strerror(result));
+    }
+}
+
+Mutex::~Mutex() {
+    if (impl_ != NULL) {
+        const int result = pthread_mutex_destroy(&impl_->mutex);
+        const bool locked = impl_->locked_count != 0;
+        delete impl_;
+        // We don't want to throw from the destructor. Also, if this ever
+        // fails, something is really screwed up a lot.
+        assert(result == 0);
+
+        // We should not try to destroy a locked mutex, bad threaded monsters
+        // could get loose if we ever do and it is also forbidden by pthreads.
+
+        // This should not be possible to happen, since the
+        // pthread_mutex_destroy should check for it already. But it seems
+        // there are systems that don't check it.
+        assert(!locked);
+    }
+}
+
+void
+Mutex::lock() {
+    assert(impl_ != NULL);
+    const int result = pthread_mutex_lock(&impl_->mutex);
+    if (result != 0) {
+        isc_throw(isc::InvalidOperation, std::strerror(result));
+    }
+    ++impl_->locked_count; // Only in debug mode
+}
+
+void
+Mutex::unlock() {
+    assert(impl_ != NULL);
+    --impl_->locked_count; // Only in debug mode
+    const int result = pthread_mutex_unlock(&impl_->mutex);
+    assert(result == 0); // This should never be possible
+}
+
+// TODO: Disable in non-debug build
+bool
+Mutex::locked() const {
+    return (impl_->locked_count != 0);
+}
+
+}
+}
+}
diff --git a/src/lib/util/threads/lock.h b/src/lib/util/threads/lock.h
new file mode 100644
index 0000000..fef537b
--- /dev/null
+++ b/src/lib/util/threads/lock.h
@@ -0,0 +1,128 @@
+// 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 B10_THREAD_LOCK_H
+#define B10_THREAD_LOCK_H
+
+#include <boost/noncopyable.hpp>
+
+#include <cstdlib> // for NULL.
+
+namespace isc {
+namespace util {
+namespace thread {
+
+/// \brief Mutex with very simple interface
+///
+/// Since mutexes are very system dependant, we create our own wrapper around
+/// whatever is available on the system and hide it.
+///
+/// To use this mutex, create it and then lock and unlock it by creating the
+/// Mutex::Locker object.
+///
+/// Also, as mutex is a low-level system object, an error might happen at any
+/// operation with it. We convert many errors to the isc::InvalidOperation,
+/// since the errors usually happen only when used in a wrong way. Any methods
+/// or constructors in this class can throw. Allocation errors are converted
+/// to std::bad_alloc (for example when OS-dependant limit of mutexes is
+/// exceeded). Some errors which usually mean a programmer error abort the
+/// program, since there could be no safe way to recover from them.
+///
+/// The current interface is somewhat minimalistic. If we ever need more, we
+/// can add it later.
+class Mutex : public boost::noncopyable {
+public:
+    /// \brief Constructor.
+    ///
+    /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
+    /// if the same threads tries to lock it again, Bad Things Happen).
+    ///
+    /// Depending on compilation parameters and OS, the mutex may or may not
+    /// do some error and sanity checking. However, such checking is meant
+    /// only to aid development, not rely on it as a feature.
+    ///
+    /// \throw std::bad_alloc In case allocation of something (memory, the
+    ///     OS mutex) fails.
+    /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
+    ///     This should be rare.
+    Mutex();
+
+    /// \brief Destructor.
+    ///
+    /// Destroys the mutex. It is not allowed to destroy a mutex which is
+    /// currently locked. This means a Locker created with this Mutex must
+    /// never live longer than the Mutex itself.
+    ~Mutex();
+
+    /// \brief This holds a lock on a Mutex.
+    ///
+    /// To lock a mutex, create a locker. It'll get unlocked when the locker
+    /// is destroyed.
+    ///
+    /// If you create the locker on the stack or using some other "garbage
+    /// collecting" mechanism (auto_ptr, for example), it ensures exception
+    /// safety with regards to the mutex - it'll get released on the exit
+    /// of function no matter by what means.
+    class Locker : public boost::noncopyable {
+    public:
+        /// \brief Constructor.
+        ///
+        /// Locks the mutex. May block for extended period of time.
+        ///
+        /// \throw isc::InvalidOperation when OS reports error. This usually
+        ///     means an attempt to use the mutex in a wrong way (locking
+        ///     a mutex second time from the same thread, for example).
+        Locker(Mutex& mutex) :
+            mutex_(NULL)
+        {
+            // Set the mutex_ after we acquire the lock. This is because of
+            // exception safety. If lock() throws, it didn't work, so we must
+            // not unlock when we are destroyed. In such case, mutex_ is
+            // NULL and checked in the destructor.
+            mutex.lock();
+            mutex_ = &mutex;
+        }
+
+        /// \brief Destructor.
+        ///
+        /// Unlocks the mutex.
+        ~Locker() {
+            if (mutex_ != NULL) {
+                mutex_->unlock();
+            }
+        }
+    private:
+        Mutex* mutex_;
+    };
+    /// \brief If the mutex is currently locked
+    ///
+    /// This is debug aiding method only. And it might be unavailable in
+    /// non-debug build (because keeping the state might be needlesly
+    /// slow).
+    ///
+    /// \todo Disable in non-debug build
+    bool locked() const;
+private:
+    class Impl;
+    Impl* impl_;
+    void lock();
+    void unlock();
+};
+
+
+}
+}
+}
+
+#endif
diff --git a/src/lib/util/threads/tests/Makefile.am b/src/lib/util/threads/tests/Makefile.am
new file mode 100644
index 0000000..2d9d4f9
--- /dev/null
+++ b/src/lib/util/threads/tests/Makefile.am
@@ -0,0 +1,36 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+# XXX: we'll pollute the top builddir for creating a temporary test file
+# # used to bind a UNIX domain socket so we can minimize the risk of exceeding
+# # the limit of file name path size.
+AM_CPPFLAGS += -DTEST_DATA_TOPBUILDDIR=\"$(abs_top_builddir)\"
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS_ENVIRONMENT = \
+        $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES  = run_unittests.cc
+run_unittests_SOURCES += thread_unittest.cc
+run_unittests_SOURCES += lock_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
+
+run_unittests_LDADD = $(top_builddir)/src/lib/util/threads/libb10-threads.la
+run_unittests_LDADD += \
+        $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(GTEST_LDADD)
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/threads/tests/lock_unittest.cc b/src/lib/util/threads/tests/lock_unittest.cc
new file mode 100644
index 0000000..0b4d3ce
--- /dev/null
+++ b/src/lib/util/threads/tests/lock_unittest.cc
@@ -0,0 +1,116 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/threads/lock.h>
+#include <util/threads/thread.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <unistd.h>
+#include <signal.h>
+
+using namespace isc::util::thread;
+
+namespace {
+
+// If we try to lock the debug mutex multiple times, it should throw.
+TEST(MutexTest, lockMultiple) {
+    // TODO: Once we support non-debug mutexes, disable the test if we compile
+    // with them.
+    Mutex mutex;
+    EXPECT_FALSE(mutex.locked()); // Debug-only build
+    Mutex::Locker l1(mutex);
+    EXPECT_TRUE(mutex.locked()); // Debug-only build
+    EXPECT_THROW({
+        Mutex::Locker l2(mutex); // Attempt to lock again.
+    }, isc::InvalidOperation);
+    EXPECT_TRUE(mutex.locked()); // Debug-only build
+}
+
+// Destroying a locked mutex is a bad idea as well
+#ifdef EXPECT_DEATH
+TEST(MutexTest, destroyLocked) {
+    EXPECT_DEATH({
+        Mutex* mutex = new Mutex;
+        new Mutex::Locker(*mutex);
+        delete mutex;
+        // This'll leak the locker, but inside the slave process, it should
+        // not be an issue.
+    }, "");
+}
+#endif
+
+// In this test, we try to check if a mutex really locks. We could try that
+// with a deadlock, but that's not practical (the test would not end).
+//
+// Instead, we try do to some operation on the same data from multiple threads
+// that's likely to break if not locked. Also, the test must run for a while
+// to have an opportunity to manifest.
+//
+// Currently we try incrementing a double variable. That one is large enough
+// and complex enough so it should not be possible for the CPU to do it as an
+// atomic operation, at least on common architectures.
+const size_t iterations = 100000;
+
+void
+performIncrement(volatile double* canary, volatile bool* ready_me,
+                 volatile bool* ready_other, Mutex* mutex)
+{
+    // Loosely (busy) wait for the other thread so both will start
+    // approximately at the same time.
+    *ready_me = true;
+    while (!*ready_other) {}
+
+    for (size_t i = 0; i < iterations; ++i) {
+        Mutex::Locker lock(*mutex);
+        *canary += 1;
+    }
+}
+
+void
+no_handler(int) {}
+
+TEST(MutexTest, swarm) {
+    // Create a timeout in case something got stuck here
+    struct sigaction ignored, original;
+    memset(&ignored, 0, sizeof ignored);
+    ignored.sa_handler = no_handler;
+    if (sigaction(SIGALRM, &ignored, &original)) {
+        FAIL() << "Couldn't set alarm";
+    }
+    alarm(10);
+    // This type has a low chance of being atomic itself, further raising
+    // the chance of problems appearing.
+    double canary = 0;
+    Mutex mutex;
+    // Run two parallel threads
+    bool ready1 = false;
+    bool ready2 = false;
+    Thread t1(boost::bind(&performIncrement, &canary, &ready1, &ready2,
+                          &mutex));
+    Thread t2(boost::bind(&performIncrement, &canary, &ready2, &ready1,
+                          &mutex));
+    t1.wait();
+    t2.wait();
+    // Check it the sum is the expected value.
+    EXPECT_EQ(iterations * 2, canary) << "Threads are badly synchronized";
+    // Cancel the alarm and return the original handler
+    alarm(0);
+    if (sigaction(SIGALRM, &original, NULL)) {
+        FAIL() << "Couldn't restore alarm";
+    }
+}
+
+}
diff --git a/src/lib/util/threads/tests/run_unittests.cc b/src/lib/util/threads/tests/run_unittests.cc
new file mode 100644
index 0000000..6f71ad6
--- /dev/null
+++ b/src/lib/util/threads/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+#include <stdlib.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    setenv("B10_LOCKFILE_DIR_FROM_BUILD", TEST_DATA_TOPBUILDDIR, 1);
+    return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/util/threads/tests/thread_unittest.cc b/src/lib/util/threads/tests/thread_unittest.cc
new file mode 100644
index 0000000..5afdf3f
--- /dev/null
+++ b/src/lib/util/threads/tests/thread_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/threads/thread.h>
+
+#include <boost/bind.hpp>
+
+#include <gtest/gtest.h>
+
+// This file tests the Thread class. It's hard to test an actual thread is
+// started, but we at least check the function is run and exceptions are
+// propagated as they should.
+//
+// We run some tests mutiple times to see if there happen to be a race
+// condition (then it would have better chance showing up).
+//
+// The detached tests are not run as many times to prevent many threads being
+// started in parallel (the other tests wait for the previous one to terminate
+// before starting new one).
+
+using namespace isc::util::thread;
+
+namespace {
+const size_t iterations = 200;
+const size_t detached_iterations = 25;
+
+void
+doSomething(int*) { }
+
+// We just test that we can forget about the thread and nothing
+// bad will happen on our side.
+TEST(ThreadTest, detached) {
+    int x;
+    for (size_t i = 0; i < detached_iterations; ++i) {
+        Thread thread(boost::bind(&doSomething, &x));
+    }
+}
+
+void
+markRun(bool* mark) {
+    EXPECT_FALSE(*mark);
+    *mark = true;
+}
+
+// Wait for a thread to end first. The variable must be set at the time.
+TEST(ThreadTest, wait) {
+    for (size_t i = 0; i < iterations; ++i) {
+        bool mark = false;
+        Thread thread(boost::bind(markRun, &mark));
+        thread.wait();
+        ASSERT_TRUE(mark) << "Not finished yet in " << i << "th iteration";
+        // Can't wait second time
+        ASSERT_THROW(thread.wait(), isc::InvalidOperation);
+    }
+}
+
+void
+throwSomething() {
+    throw 42; // Throw something really unusual, to see everything is caught.
+}
+
+void
+throwException() {
+    throw std::exception();
+}
+
+// Exception in the thread we forget about should not do anything to us
+TEST(ThreadTest, detachedException) {
+    for (size_t i = 0; i < detached_iterations; ++i) {
+        Thread thread(throwSomething);
+    }
+    for (size_t i = 0; i < detached_iterations; ++i) {
+        Thread thread(throwException);
+    }
+}
+
+// An uncaught exception in the thread should propagate through wait
+TEST(ThreadTest, exception) {
+    for (size_t i = 0; i < iterations; ++i) {
+        Thread thread(throwSomething);
+        Thread thread2(throwException);
+        ASSERT_THROW(thread.wait(), Thread::UncaughtException);
+        ASSERT_THROW(thread2.wait(), Thread::UncaughtException);
+    }
+}
+
+}
diff --git a/src/lib/util/threads/thread.cc b/src/lib/util/threads/thread.cc
new file mode 100644
index 0000000..c1c9cdf
--- /dev/null
+++ b/src/lib/util/threads/thread.cc
@@ -0,0 +1,172 @@
+// 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 "thread.h"
+#include "lock.h"
+
+#include <memory>
+#include <string>
+#include <cstring>
+#include <cerrno>
+
+#include <pthread.h>
+
+#include <boost/scoped_ptr.hpp>
+
+using std::string;
+using std::exception;
+using std::auto_ptr;
+using boost::scoped_ptr;
+
+namespace isc {
+namespace util {
+namespace thread {
+
+// The implementation of the Thread class.
+//
+// This internal state is not deleted until the thread terminates and is either
+// waited for or detached. We could do this with shared_ptr (or, shared_ptr and
+// weak_ptr), but we plan on compiling boost without thread support, so it
+// might not be safe. Therefore we use an explicit mutex. It is being locked
+// only 2-3 times in the lifetime of the thread, which should be negligible
+// overhead anyway.
+class Thread::Impl {
+public:
+    Impl(const boost::function<void ()>& main) :
+        // Two things to happen before destruction - thread needs to terminate
+        // and the creating thread needs to release it.
+        waiting_(2),
+        main_(main),
+        exception_(false)
+    {}
+    // Another of the waiting events is done. If there are no more, delete
+    // impl.
+    static void done(Impl* impl) {
+        bool should_delete(false);
+        { // We need to make sure the mutex is unlocked before it is deleted
+            Mutex::Locker locker(impl->mutex_);
+            if (--impl->waiting_ == 0) {
+                should_delete = true;
+            }
+        }
+        if (should_delete) {
+            delete impl;
+        }
+    }
+    // Run the thread. The type of parameter is because the pthread API.
+    static void* run(void* impl_raw) {
+        Impl* impl = static_cast<Impl*>(impl_raw);
+        try {
+            impl->main_();
+        } catch (const exception& e) {
+            impl->exception_ = true;
+            impl->exception_text_ = e.what();
+        } catch (...) {
+            impl->exception_ = true;
+            impl->exception_text_ = "Uknown exception";
+        }
+        done(impl);
+        return (NULL);
+    }
+    // How many events are waiting? One is for the thread to finish, one
+    // for the destructor of Thread or wait. Once both happen, this is
+    // no longer needed.
+    size_t waiting_;
+    // The main function of the thread.
+    boost::function<void ()> main_;
+    // Was there an exception?
+    bool exception_;
+    string exception_text_;
+    // The mutex protects the waiting_ member, which ensures there are
+    // no race conditions and collisions when terminating. The other members
+    // should be safe, because:
+    // * tid_ is read only.
+    // * exception_ and exception_text_ is accessed outside of the thread
+    //   only after join, by that time the thread must have terminated.
+    // * main_ is used in a read-only way here. If there are any shared
+    //   resources used inside, it is up to the main_ itself to take care.
+    Mutex mutex_;
+    // Which thread are we talking about anyway?
+    pthread_t tid_;
+};
+
+Thread::Thread(const boost::function<void ()>& main) :
+    impl_(NULL)
+{
+    auto_ptr<Impl> impl(new Impl(main));
+    const int result = pthread_create(&impl->tid_, NULL, &Impl::run,
+                                      impl.get());
+    // Any error here?
+    switch (result) {
+        case 0: // All 0K
+            impl_ = impl.release();
+            break;
+        case EAGAIN:
+            throw std::bad_alloc();
+        default: // Other errors. They should not happen.
+            isc_throw(isc::InvalidOperation, std::strerror(result));
+    }
+}
+
+Thread::~Thread() {
+    if (impl_ != NULL) {
+        // In case we didn't call wait yet
+        const int result = pthread_detach(impl_->tid_);
+        Impl::done(impl_);
+        impl_ = NULL;
+        // If the detach ever fails, something is screwed rather badly.
+        assert(result == 0);
+    }
+}
+
+void
+Thread::wait() {
+    if (impl_ == NULL) {
+        isc_throw(isc::InvalidOperation,
+                  "Wait called and no thread to wait for");
+    }
+
+    const int result = pthread_join(impl_->tid_, NULL);
+    if (result != 0) {
+        isc_throw(isc::InvalidOperation, std::strerror(result));
+    }
+
+    // Was there an exception in the thread?
+    scoped_ptr<UncaughtException> ex;
+    // Something here could in theory throw. But we already terminated the thread, so
+    // we need to make sure we are in consistent state even in such situation (like
+    // releasing the mutex and impl_).
+    try {
+        if (impl_->exception_) {
+            ex.reset(new UncaughtException(__FILE__, __LINE__,
+                                           impl_->exception_text_.c_str()));
+        }
+    } catch (...) {
+        Impl::done(impl_);
+        impl_ = NULL;
+        // We have eaten the UncaughtException by now, but there's another
+        // exception instead, so we have at least something.
+        throw;
+    }
+
+    Impl::done(impl_);
+    impl_ = NULL;
+    if (ex.get() != NULL) {
+        throw UncaughtException(*ex);
+    }
+}
+
+}
+}
+}
diff --git a/src/lib/util/threads/thread.h b/src/lib/util/threads/thread.h
new file mode 100644
index 0000000..9c6d20a
--- /dev/null
+++ b/src/lib/util/threads/thread.h
@@ -0,0 +1,112 @@
+// 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 B10_THREAD_H
+#define B10_THREAD_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+
+namespace isc {
+namespace util {
+/// \brief Wrappers for thread related functionality
+///
+/// We provide our own wrappers, currently around pthreads. We tried using
+/// the boost thread support, but it gave us some trouble, so we implemented
+/// in-house ones.
+namespace thread {
+
+/// \brief A separate thread.
+///
+/// A thread of execution. When created, starts running in the background.
+/// You can wait for it then or just forget it ever existed and leave it
+/// live peacefully.
+///
+/// The interface is minimalistic for now. We may need to extend it later.
+///
+/// \note While the objects of this class represent another thread, they
+///     are not thread-safe. You're not supposed to call wait() on the same
+///     object from multiple threads or so. They are reentrant (you can
+///     wait for different threads from different threads).
+class Thread : public boost::noncopyable {
+public:
+    /// \brief There's an uncaught exception in a thread.
+    ///
+    /// When a thread terminates because it the main function of the thread
+    /// throws, this one is re-thrown out of wait() and contains the what
+    /// of the original exception.
+    class UncaughtException : public isc::Exception {
+    public:
+        UncaughtException(const char* file, size_t line, const char* what) :
+            Exception(file, line, what)
+        {}
+    };
+
+    /// \brief Create and start a thread.
+    ///
+    /// Create a new thread and run body inside it.
+    ///
+    /// If you need to pass parameters to body, or return some result, you
+    /// may just want to use boost::bind or alike to store them within the
+    /// body functor.
+    ///
+    /// \note The main functor will be copied internally. You need to consider
+    ///     this when returning the result.
+    ///
+    /// The body should terminate by exiting the function. If it throws, it
+    /// is considered an error. You should generally catch any exceptions form
+    /// within there and handle them somehow.
+    ///
+    /// \param main The code to run inside the thread.
+    ///
+    /// \throw std::bad_alloc if allocation of the new thread or other
+    ///     resources fails.
+    /// \throw isc::InvalidOperation for other errors (should not happen).
+    Thread(const boost::function<void()>& main);
+
+    /// \brief Destructor.
+    ///
+    /// It is completely legitimate to destroy the thread without calling
+    /// wait() before. In such case, the thread will just live on until it
+    /// terminates. However, if the thread dies due to exception, for example,
+    /// it's up to you to detect that, no error is reported from this class.
+    ///
+    /// \throw isc::InvalidOperation in the rare case of OS reporting a
+    ///     problem. This should not happen unless you messed up with the raw
+    ///     thread by the low-level API.
+    ~Thread();
+
+    /// \brief Wait for the thread to terminate.
+    ///
+    /// Waits until the thread terminates. Must be called at most once.
+    ///
+    /// \throw isc::InvalidOperation if the OS API returns error. This usually
+    ///     mean a programmer error (like two threads trying to wait on each
+    ///     other).
+    /// \throw isc::InvalidOperation calling wait a second time.
+    /// \throw UncaughtException if the thread terminated by throwing an
+    ///     exception instead of just returning from the function.
+    void wait();
+private:
+    class Impl;
+    Impl* impl_;
+};
+
+}
+}
+}
+
+#endif
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
index b0e3ac0..c04d917 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
+++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf.orig
@@ -33,6 +33,9 @@
             "port": 47806
         } ]
     },
+    "Stats": {
+        "poll-interval": 1
+    },
     "Boss": {
         "components": {
             "b10-auth": { "kind": "needed", "special": "auth" },
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index b1c8ace..142a78e 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2011  Internet Systems Consortium.
+# Copyright (C) 2011-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
@@ -391,19 +391,21 @@ def find_value(dictionary, key):
         for v in dictionary.values():
             return find_value(v, key)
 
- at step('The counter (\S+)(?: for the zone (\S+))? should be' + \
-          '(?:( greater than| less than))? (\d+)')
-def check_statistics(step, counter, zone, gtlt, number):
+ at step('the statistics counter (\S+)(?: for the zone (\S+))? should be' + \
+          '(?:( greater than| less than| between))? (\-?\d+)(?: and (\-?\d+))?')
+def check_statistics(step, counter, zone, gtltbt, number, upper):
     """
     check the output of bindctl for statistics of specified counter
     and zone.
     Parameters:
     counter ('counter <counter>'): The counter name of statistics.
     zone ('zone <zone>', optional): The zone name.
-    gtlt (' greater than'|' less than', optional): greater than
-          <number> or less than <number>.
+    gtltbt (' greater than'|' less than'|' between', optional): greater than
+          <number> or less than <number> or between <number> and <upper>.
     number ('<number>): The expect counter number. <number> is assumed
           to be an unsigned integer.
+    upper ('<upper>, optional): The expect upper counter number when
+          using 'between'.
     """
     output = parse_bindctl_output_as_data_structure()
     found = None
@@ -416,10 +418,15 @@ def check_statistics(step, counter, zone, gtlt, number):
     assert found is not None, \
         'Not found statistics counter %s%s' % (counter, zone_str)
     msg = "Got %s, expected%s %s as counter %s%s" % \
-        (found, gtlt, number, counter, zone_str)
-    if gtlt and 'greater' in gtlt:
+        (found, gtltbt, number, counter, zone_str)
+    if gtltbt and 'between' in gtltbt and upper:
+        msg = "Got %s, expected%s %s and %s as counter %s%s" % \
+            (found, gtltbt, number, upper, counter, zone_str)
+        assert int(number) <= int(found) \
+            and int(found) <= int(upper), msg
+    elif gtltbt and 'greater' in gtltbt:
         assert int(found) > int(number), msg
-    elif gtlt and 'less' in gtlt:
+    elif gtltbt and 'less' in gtltbt:
         assert int(found) < int(number), msg
     else:
         assert int(found) == int(number), msg
diff --git a/tests/lettuce/features/xfrin_notify_handling.feature b/tests/lettuce/features/xfrin_notify_handling.feature
index 96a9721..80a8873 100644
--- a/tests/lettuce/features/xfrin_notify_handling.feature
+++ b/tests/lettuce/features/xfrin_notify_handling.feature
@@ -27,10 +27,18 @@ Feature: Xfrin incoming notify handling
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
     last bindctl output should not contain "error"
     last bindctl output should not contain "example.org."
-    The counter notifyoutv4 for the zone _SERVER_ should be 0
-    The counter notifyoutv6 for the zone _SERVER_ should be 0
-    The counter xfrrej for the zone _SERVER_ should be 0
-    The counter xfrreqdone for the zone _SERVER_ should be 0
+    Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
+    Then the statistics counter notifyoutv6 for the zone _SERVER_ should be 0
+    Then the statistics counter xfrrej for the zone _SERVER_ should be 0
+    Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
+
+    When I query statistics ixfr_running of bind10 module Xfrout with cmdctl port 47804
+    Then the statistics counter ixfr_running should be 0
+    Then the statistics counter ixfr_running should be 0
+
+    When I query statistics axfr_running of bind10 module Xfrout with cmdctl port 47804
+    Then the statistics counter axfr_running should be 0
+    Then the statistics counter axfr_running should be 0
 
     When I send bind10 with cmdctl port 47804 the command Xfrout notify example.org IN
     Then wait for new master stderr message XFROUT_NOTIFY_COMMAND
@@ -49,17 +57,21 @@ Feature: Xfrin incoming notify handling
     #
     # check for statistics change
     #
+
+    # wait until the last stats requesting is finished
+    wait for new master stderr message STATS_SEND_STATISTICS_REQUEST
+    wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
+
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
     last bindctl output should not contain "error"
-    Then wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
-    The counter notifyoutv4 for the zone _SERVER_ should be 0
-    The counter notifyoutv4 for the zone example.org. should be 0
-    The counter notifyoutv6 for the zone _SERVER_ should be 5
-    The counter notifyoutv6 for the zone example.org. should be 5
-    The counter xfrrej for the zone _SERVER_ should be 0
-    The counter xfrrej for the zone example.org. should be 0
-    The counter xfrreqdone for the zone _SERVER_ should be 1
-    The counter xfrreqdone for the zone example.org. should be 1
+    Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
+    Then the statistics counter notifyoutv4 for the zone example.org. should be 0
+    Then the statistics counter notifyoutv6 for the zone _SERVER_ should be 5
+    Then the statistics counter notifyoutv6 for the zone example.org. should be 5
+    Then the statistics counter xfrrej for the zone _SERVER_ should be 0
+    Then the statistics counter xfrrej for the zone example.org. should be 0
+    Then the statistics counter xfrreqdone for the zone _SERVER_ should be 1
+    Then the statistics counter xfrreqdone for the zone example.org. should be 1
 
     #
     # Test for Xfr request rejected
@@ -90,10 +102,10 @@ Feature: Xfrin incoming notify handling
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
     last bindctl output should not contain "error"
     last bindctl output should not contain "example.org."
-    The counter notifyoutv4 for the zone _SERVER_ should be 0
-    The counter notifyoutv6 for the zone _SERVER_ should be 0
-    The counter xfrrej for the zone _SERVER_ should be 0
-    The counter xfrreqdone for the zone _SERVER_ should be 0
+    Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
+    Then the statistics counter notifyoutv6 for the zone _SERVER_ should be 0
+    Then the statistics counter xfrrej for the zone _SERVER_ should be 0
+    Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
 
     #
     # set transfer_acl rejection
@@ -123,18 +135,20 @@ Feature: Xfrin incoming notify handling
     #
     # check for statistics change
     #
+
+    # wait until the last stats requesting is finished
+    wait for new master stderr message STATS_SEND_STATISTICS_REQUEST
+    wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
+
     When I query statistics zones of bind10 module Xfrout with cmdctl port 47804
     last bindctl output should not contain "error"
-    Then wait for new master stderr message XFROUT_RECEIVED_GETSTATS_COMMAND
-    The counter notifyoutv4 for the zone _SERVER_ should be 0
-    The counter notifyoutv4 for the zone example.org. should be 0
-    The counter notifyoutv6 for the zone _SERVER_ should be 5
-    The counter notifyoutv6 for the zone example.org. should be 5
+    Then the statistics counter notifyoutv4 for the zone _SERVER_ should be 0
+    Then the statistics counter notifyoutv4 for the zone example.org. should be 0
+    Then the statistics counter notifyoutv6 for the zone _SERVER_ should be 5
+    Then the statistics counter notifyoutv6 for the zone example.org. should be 5
     # The counts of rejection would be between 1 and 2. They are not
     # fixed. It would depend on timing or the platform.
-    The counter xfrrej for the zone _SERVER_ should be greater than 0
-    The counter xfrrej for the zone _SERVER_ should be less than 3
-    The counter xfrrej for the zone example.org. should be greater than 0
-    The counter xfrrej for the zone example.org. should be less than 3
-    The counter xfrreqdone for the zone _SERVER_ should be 0
-    The counter xfrreqdone for the zone example.org. should be 0
+    Then the statistics counter xfrrej for the zone _SERVER_ should be greater than 0
+    Then the statistics counter xfrrej for the zone example.org. should be greater than 0
+    Then the statistics counter xfrreqdone for the zone _SERVER_ should be 0
+    Then the statistics counter xfrreqdone for the zone example.org. should be 0
diff --git a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml
index f878af3..583b155 100644
--- a/tests/tools/dhcp-ubench/dhcp-perf-guide.xml
+++ b/tests/tools/dhcp-ubench/dhcp-perf-guide.xml
@@ -1020,7 +1020,7 @@ SQLite version: 3.7.9sourceid version is 2011-11-01 00:52:41 c7c6050ef060877ebe7
         command line switches.  This summary also lists the tool's
         possible exit codes as well as describing the
         error counters printed when the test is complete:
-        <screen>$ <userinput>./perfdhcp -h</userinput>
+        <screen>$ <userinput>perfdhcp -h</userinput>
 <![CDATA[perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]
     [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]
     [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]
@@ -1172,12 +1172,12 @@ The exit status is:
         As DHCP uses low port numbers (67 for DHCPv4 relays and
         547 for DHCPv6), running perfdhcp with non-root privileges will
         usually result in the error message similar to this:
-        <screen><userinput>$./perfdhcp -4 -l eth3 -r 100 all</userinput>
+        <screen><userinput>$ perfdhcp -4 -l eth3 -r 100 all</userinput>
 Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
         </screen>
         The '-L' command line switch allows the use of a custom local port.
         However, although the following command line will work:
-        <screen><userinput>$./perfdhcp -4 -l eth3 -r 100 -L 10067 all</userinput></screen>
+        <screen><userinput>$ perfdhcp -4 -l eth3 -r 100 -L 10067 all</userinput></screen>
         in the standard configuration no responses will be received
         from the DHCP server because the server responds to default relay
         port 67.  A way to overcome this issue is to run
@@ -1198,7 +1198,7 @@ Error running perfdhcp: Failed to bind socket 3 to 172.16.1.2/port=67
         <para>
           If server is listening on interface with IPv4 address 172.16.1.1,
           the simplest perfdhcp command line will look like:
-          <screen><userinput>#./perfdhcp 172.16.1.1</userinput>
+          <screen><userinput># perfdhcp 172.16.1.1</userinput>
 ***Rate statistics***
 Rate: 206.345
 
@@ -1281,9 +1281,9 @@ collected packets: 0
           Note: should multiple interfaces on the system running perfdhcp be
           connected to the same subnet, the interface to be used for the test
           can be specified using either the interface name:
-          <screen><userinput>#./perfdhcp -l eth3</userinput></screen>
+          <screen><userinput># perfdhcp -l eth3</userinput></screen>
           or a local address assigned to it:
-          <screen><userinput>#./perfdhcp -l 172.16.1.2</userinput></screen>
+          <screen><userinput># perfdhcp -l 172.16.1.2</userinput></screen>
         </para>
       </section>
       <section id="perfdhcp-rate-control">
@@ -1296,7 +1296,7 @@ collected packets: 0
           without dropping any packets. The following command is an example of such
           a test: it causes perfdhcp to initiate 300 four-way exchanges
           per second, and runs the test for 60 seconds:
-          <screen><userinput>#./perfdhcp -l eth3 -p 60 -r 300</userinput>
+          <screen><userinput># perfdhcp -l eth3 -p 60 -r 300</userinput>
 ***Rate statistics***
 Rate: 256.683 exchanges/second, expected rate: 300 exchanges/second
 
@@ -1331,7 +1331,7 @@ collected packets: 0
           rate (256/s) indicate that server's measured performance is lower than 300 leases
           per second. A further rate decrease should eliminate most of the packet
           drops and bring the achieved rate close to expected rate:
-          <screen><userinput>#./perfdhcp -l eth3 -p 60 -r 100 -R 30</userinput>
+          <screen><userinput># perfdhcp -l eth3 -p 60 -r 100 -R 30</userinput>
 ***Rate statistics***
 Rate: 99.8164 exchanges/second, expected rate: 100 exchanges/second
 
@@ -1393,7 +1393,7 @@ collected packets: 0
           specify offsets for particular options and fields. With the following
           command line the DHCPv6 SOLICIT and REQUEST packets will be formed from
           solicit.hex and request6.hex packets:
-          <screen><userinput>#./perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers</userinput>
+          <screen><userinput># perfdhcp -6 -l eth3 -r 100 -R 20 -T solicit.hex -T request6.hex -O 21 -E 84 -S 22 -I 40 servers</userinput>
 ***Rate statistics***
 Rate: 99.5398 exchanges/second, expected rate: 100 exchanges/second
 
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 816eeeb..08a21a4 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -12,36 +12,33 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 # But older GCC compilers don't have the flag.
 AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
-AM_LDFLAGS = $(CLOCK_GETTIME_LDFLAGS)
-AM_LDFLAGS += -lm
+AM_LDFLAGS = -lm
 if USE_STATIC_LINK
 AM_LDFLAGS += -static
 endif
 
-pkglibexec_PROGRAMS = perfdhcp2
-perfdhcp2_SOURCES = main.cc
-perfdhcp2_SOURCES += command_options.cc command_options.h
-perfdhcp2_SOURCES += localized_option.h
-perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h
-perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h
-perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h
-perfdhcp2_SOURCES += stats_mgr.h
-perfdhcp2_SOURCES += test_control.cc test_control.h
+bin_PROGRAMS = perfdhcp
+perfdhcp_SOURCES = main.cc
+perfdhcp_SOURCES += command_options.cc command_options.h
+perfdhcp_SOURCES += localized_option.h
+perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
+perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
+perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
+perfdhcp_SOURCES += stats_mgr.h
+perfdhcp_SOURCES += test_control.cc test_control.h
 libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 
-perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS)
+perfdhcp_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # Boost headers when compiling with clang.
-perfdhcp2_CXXFLAGS += -Wno-unused-parameter
+perfdhcp_CXXFLAGS += -Wno-unused-parameter
 endif
 
-perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
-perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 
-#pkglibexec_PROGRAMS  = perfdhcp
-#perfdhcp_SOURCES  = perfdhcp.c
 
 # ... and the documentation
-EXTRA_DIST = perfdhcp_internals.dox
+EXTRA_DIST = perfdhcp_internals.dox
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 24e7e79..9aa6abf 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -18,13 +18,10 @@
 #include <stdint.h>
 #include <unistd.h>
 
-#include <boost/algorithm/string.hpp>
-#include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 #include <exceptions/exceptions.h>
-#include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include "command_options.h"
 
@@ -86,10 +83,9 @@ CommandOptions::reset() {
     wrapped_.clear();
     server_name_.clear();
     generateDuidTemplate();
-    commandline_.clear();
 }
 
-void
+bool
 CommandOptions::parse(int argc, char** const argv) {
     // Reset internal variables used by getopt
     // to eliminate undefined behavior when
@@ -113,23 +109,37 @@ CommandOptions::parse(int argc, char** const argv) {
     optind = 1;
 #endif
 
+    // optreset is declared on BSD systems and is used to reset internal
+    // state of getopt(). When parsing command line arguments multiple
+    // times with getopt() the optreset must be set to 1 every time before
+    // parsing starts. Failing to do so will result in random behavior of
+    // getopt().
+#ifdef HAVE_OPTRESET
+    optreset = 1;
+#endif
+
     opterr = 0;
 
     // Reset values of class members
     reset();
 
-    initialize(argc, argv);
-    validate();
+    // Informs if program has been run with 'h' or 'v' option.
+    bool help_or_version_mode = initialize(argc, argv);
+    if (!help_or_version_mode) {
+        validate();
+    }
+    return (help_or_version_mode);
 }
 
-void
+bool
 CommandOptions::initialize(int argc, char** argv) {
     char opt = 0;               // Subsequent options returned by getopt()
     std::string drop_arg;       // Value of -D<value>argument
     size_t percent_loc = 0;     // Location of % sign in -D<value>
     double drop_percent = 0;    // % value (1..100) in -D<value%>
     int num_drops = 0;          // Max number of drops specified in -D<value>
-    int num_req = 0;            // Max number of dropped requests in -n<max-drops>
+    int num_req = 0;            // Max number of dropped
+                                // requests in -n<max-drops>
     int offset_arg = 0;         // Temporary variable holding offset arguments
     std::string sarg;           // Temporary variable for string args
 
@@ -138,16 +148,13 @@ CommandOptions::initialize(int argc, char** argv) {
 
     // In this section we collect argument values from command line
     // they will be tuned and validated elsewhere
-    while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
+    while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
+                        "s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
         stream << " -" << opt;
         if (optarg) {
             stream << " " << optarg;
         }  
         switch (opt) {
-        case 'v':
-            version();
-            return;
-
         case '1':
             use_first_ = true;
             break;
@@ -163,11 +170,13 @@ CommandOptions::initialize(int argc, char** argv) {
             break;
 
         case 'a':
-            aggressivity_ = positiveInteger("value of aggressivity: -a<value> must be a positive integer");
+            aggressivity_ = positiveInteger("value of aggressivity: -a<value>"
+                                            " must be a positive integer");
             break;
 
         case 'b':
-            check(base_.size() > 3, "-b<value> already specified, unexpected occurence of 5th -b<value>");
+            check(base_.size() > 3, "-b<value> already specified,"
+                  " unexpected occurence of 5th -b<value>");
             base_.push_back(optarg);
             decodeBase(base_.back());
             break;
@@ -181,53 +190,64 @@ CommandOptions::initialize(int argc, char** argv) {
             break;
 
         case 'd':
-            check(drop_time_set_ > 1, "maximum number of drops already specified, "
+            check(drop_time_set_ > 1,
+                  "maximum number of drops already specified, "
                   "unexpected 3rd occurence of -d<value>");
             try {
-                drop_time_[drop_time_set_] = boost::lexical_cast<double>(optarg);
+                drop_time_[drop_time_set_] =
+                    boost::lexical_cast<double>(optarg);
             } catch (boost::bad_lexical_cast&) {
                 isc_throw(isc::InvalidParameter,
-                          "value of drop time: -d<value> must be positive number");
+                          "value of drop time: -d<value>"
+                          " must be positive number");
             }
-            check(drop_time_[drop_time_set_] <= 0., "drop-time must be a positive number");
+            check(drop_time_[drop_time_set_] <= 0.,
+                  "drop-time must be a positive number");
             drop_time_set_ = true;
             break;
 
         case 'D':
             drop_arg = std::string(optarg);
             percent_loc = drop_arg.find('%');
-            check(max_pdrop_.size() > 1 || max_drop_.size() > 1, "values of maximum drops: -D<value> already "
+            check(max_pdrop_.size() > 1 || max_drop_.size() > 1,
+                  "values of maximum drops: -D<value> already "
                   "specified, unexpected 3rd occurence of -D,value>");
             if ((percent_loc) != std::string::npos) {
                 try {
-                    drop_percent = boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
+                    drop_percent =
+                        boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
                 } catch (boost::bad_lexical_cast&) {
                     isc_throw(isc::InvalidParameter,
-                              "value of drop percentage: -D<value%> must be 0..100");
+                              "value of drop percentage: -D<value%>"
+                              " must be 0..100");
                 }
                 check((drop_percent <= 0) || (drop_percent >= 100),
                   "value of drop percentage: -D<value%> must be 0..100");
                 max_pdrop_.push_back(drop_percent);
             } else {
-                num_drops = positiveInteger("value of max drops number: -d<value> must be a positive integer");
+                num_drops = positiveInteger("value of max drops number:"
+                                            " -d<value> must be a positive integer");
                 max_drop_.push_back(num_drops);
             }
             break;
 
         case 'E':
-            elp_offset_ = nonNegativeInteger("value of time-offset: -E<value> must not be a negative integer");
+            elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
+                                             " must not be a negative integer");
             break;
 
         case 'h':
             usage();
-            return;
+            return (true);
 
         case 'i':
             exchange_mode_ = DO_SA;
             break;
 
         case 'I':
-            rip_offset_ = positiveInteger("value of ip address offset: -I<value> must be a positive integer");
+            rip_offset_ = positiveInteger("value of ip address offset:"
+                                          " -I<value> must be a"
+                                          " positive integer");
             break;
 
         case 'l':
@@ -236,43 +256,55 @@ CommandOptions::initialize(int argc, char** argv) {
             break;
 
         case 'L':
-             local_port_ = nonNegativeInteger("value of local port: -L<value> must not be a negative integer");
-             check(local_port_ > static_cast<int>(std::numeric_limits<uint16_t>::max()),
+             local_port_ = nonNegativeInteger("value of local port:"
+                                              " -L<value> must not be a"
+                                              " negative integer");
+             check(local_port_ >
+                   static_cast<int>(std::numeric_limits<uint16_t>::max()),
                   "local-port must be lower than " +
                   boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
             break;
 
         case 'n':
-            num_req = positiveInteger("value of num-request: -n<value> must be a positive integer");
+            num_req = positiveInteger("value of num-request:"
+                                      " -n<value> must be a positive integer");
             if (num_request_.size() >= 2) {
-                isc_throw(isc::InvalidParameter,"value of maximum number of requests: -n<value> "
-                          "already specified, unexpected 3rd occurence of -n<value>");
+                isc_throw(isc::InvalidParameter,
+                          "value of maximum number of requests: -n<value> "
+                          "already specified, unexpected 3rd occurence"
+                          " of -n<value>");
             }
             num_request_.push_back(num_req);
             break;
 
         case 'O':
             if (rnd_offset_.size() < 2) {
-                offset_arg = positiveInteger("value of random offset: -O<value> must be greater than 3");
+                offset_arg = positiveInteger("value of random offset: "
+                                             "-O<value> must be greater than 3");
             } else {
                 isc_throw(isc::InvalidParameter,
-                          "random offsets already specified, unexpected 3rd occurence of -O<value>");
+                          "random offsets already specified,"
+                          " unexpected 3rd occurence of -O<value>");
             }
-            check(offset_arg < 3, "value of random random-offset: -O<value> must be greater than 3 ");
+            check(offset_arg < 3, "value of random random-offset:"
+                  " -O<value> must be greater than 3 ");
             rnd_offset_.push_back(offset_arg);
             break;
 
         case 'p':
-            period_ = positiveInteger("value of test period: -p<value> must be a positive integer");
+            period_ = positiveInteger("value of test period:"
+                                      " -p<value> must be a positive integer");
             break;
 
         case 'P':
-            preload_ = nonNegativeInteger("number of preload packets: -P<value> must not be "
+            preload_ = nonNegativeInteger("number of preload packets:"
+                                          " -P<value> must not be "
                                           "a negative integer");
             break;
 
         case 'r':
-            rate_ = positiveInteger("value of rate: -r<value> must be a positive integer");
+            rate_ = positiveInteger("value of rate:"
+                                    " -r<value> must be a positive integer");
             break;
 
         case 'R':
@@ -281,42 +313,58 @@ CommandOptions::initialize(int argc, char** argv) {
 
         case 's':
             seed_ = static_cast<unsigned int>
-                (nonNegativeInteger("value of seed: -s <seed> must be non-negative integer"));
+                (nonNegativeInteger("value of seed:"
+                                    " -s <seed> must be non-negative integer"));
             seeded_ = seed_ > 0 ? true : false;
             break;
 
         case 'S':
-            sid_offset_ = positiveInteger("value of server id offset: -S<value> must be a positive integer");
+            sid_offset_ = positiveInteger("value of server id offset:"
+                                          " -S<value> must be a"
+                                          " positive integer");
             break;
 
         case 't':
-            report_delay_ = positiveInteger("value of report delay: -t<value> must be a positive integer");
+            report_delay_ = positiveInteger("value of report delay:"
+                                            " -t<value> must be a"
+                                            " positive integer");
             break;
 
         case 'T':
             if (template_file_.size() < 2) {
-                sarg = nonEmptyString("template file name not specified, expected -T<filename>");
+                sarg = nonEmptyString("template file name not specified,"
+                                      " expected -T<filename>");
                 template_file_.push_back(sarg);
             } else {
                 isc_throw(isc::InvalidParameter,
-                          "template files are already specified, unexpected 3rd -T<filename> occurence");
+                          "template files are already specified,"
+                          " unexpected 3rd -T<filename> occurence");
             }
             break;
 
+        case 'v':
+            version();
+            return (true);
+
         case 'w':
-            wrapped_ = nonEmptyString("command for wrapped mode: -w<command> must be specified");
+            wrapped_ = nonEmptyString("command for wrapped mode:"
+                                      " -w<command> must be specified");
             break;
 
         case 'x':
-            diags_ = nonEmptyString("value of diagnostics selectors: -x<value> must be specified");
+            diags_ = nonEmptyString("value of diagnostics selectors:"
+                                    " -x<value> must be specified");
             break;
 
         case 'X':
             if (xid_offset_.size() < 2) {
-                offset_arg = positiveInteger("value of transaction id: -X<value> must be a positive integer");
+                offset_arg = positiveInteger("value of transaction id:"
+                                             " -X<value> must be a"
+                                             " positive integer");
             } else {
                 isc_throw(isc::InvalidParameter,
-                          "transaction ids already specified, unexpected 3rd -X<value> occurence");
+                          "transaction ids already specified,"
+                          " unexpected 3rd -X<value> occurence");
             }
             xid_offset_.push_back(offset_arg);
             break;
@@ -326,8 +374,6 @@ CommandOptions::initialize(int argc, char** argv) {
         }
     }
 
-    std::cout << "Running: " << stream.str() << std::endl;
-
     // If the IP version was not specified in the
     // command line, assume IPv4.
     if (ipversion_ == 0) {
@@ -355,26 +401,30 @@ CommandOptions::initialize(int argc, char** argv) {
     check(optind < argc -1, "extra arguments?");
     if (optind == argc - 1) {
         server_name_ = argv[optind];
+        stream << " " << server_name_;
         // Decode special cases
         if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) {
-            broadcast_ = 1;
-            // 255.255.255.255 is IPv4 broadcast address
-            server_name_ = "255.255.255.255";
+            broadcast_ = true;
+            // Use broadcast address as server name.
+            server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
         } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) {
-            server_name_ = "FF02::1:2";
-        } else if ((ipversion_ == 6) && (server_name_.compare("servers") == 0)) {
-            server_name_ = "FF05::1:3";
+            server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
+        } else if ((ipversion_ == 6) &&
+                   (server_name_.compare("servers") == 0)) {
+            server_name_ = ALL_DHCP_SERVERS;
         }
     }
 
+    std::cout << "Running: " << stream.str() << std::endl;
+
     // Handle the local '-l' address/interface
     if (!localname_.empty()) {
         if (server_name_.empty()) {
             if (is_interface_ && (ipversion_ == 4)) {
-                broadcast_ = 1;
-                server_name_ = "255.255.255.255";
+                broadcast_ = true;
+                server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
             } else if (is_interface_ && (ipversion_ == 6)) {
-                server_name_ = "FF02::1:2";
+                server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
             }
         }
     }
@@ -388,11 +438,13 @@ CommandOptions::initialize(int argc, char** argv) {
     if (duid_template_.size() == 0) {
         generateDuidTemplate();
     }
+    return (false);
 }
 
 void
 CommandOptions::initClientsNum() {
-    const std::string errmsg = "value of -R <value> must be non-negative integer";
+    const std::string errmsg =
+        "value of -R <value> must be non-negative integer";
 
     // Declare clients_num as as 64-bit signed value to
     // be able to detect negative values provided
@@ -401,11 +453,7 @@ CommandOptions::initClientsNum() {
     long long clients_num = 0;
     try {
         clients_num = boost::lexical_cast<long long>(optarg);
-    } catch (boost::bad_lexical_cast&) {
-        isc_throw(isc::InvalidParameter, errmsg.c_str());
-    }
-    check(clients_num < 0, errmsg);
-    try {
+        check(clients_num < 0, errmsg);
         clients_num_ = boost::lexical_cast<uint32_t>(optarg);
     } catch (boost::bad_lexical_cast&) {
         isc_throw(isc::InvalidParameter, errmsg);
@@ -435,7 +483,8 @@ CommandOptions::decodeBase(const std::string& base) {
         decodeDuid(b);
     } else {
         isc_throw(isc::InvalidParameter,
-                  "base value not provided as -b<value>, expected -b mac=<mac> or -b duid=<duid>");
+                  "base value not provided as -b<value>,"
+                  " expected -b mac=<mac> or -b duid=<duid>");
     }
 }
 
@@ -443,7 +492,9 @@ void
 CommandOptions::decodeMac(const std::string& base) {
     // Strip string from mac=
     size_t found = base.find('=');
-    static const char* errmsg = "expected -b<base> format for mac address is -b mac=00::0C::01::02::03::04";
+    static const char* errmsg = "expected -b<base> format for"
+        " mac address is -b mac=00::0C::01::02::03::04 or"
+        " -b mac=00:0C:01:02:03:04";
     check(found == std::string::npos, errmsg);
 
     // Decode mac address to vector of uint8_t
@@ -476,7 +527,8 @@ CommandOptions::decodeDuid(const std::string& base) {
     // Strip argument from duid=
     std::vector<uint8_t> duid_template;
     size_t found = base.find('=');
-    check(found == std::string::npos, "expected -b<base> format for duid is -b duid=<duid>");
+    check(found == std::string::npos, "expected -b<base>"
+          " format for duid is -b duid=<duid>");
     std::string b = base.substr(found + 1);
 
     // DUID must have even number of digits and must not be longer than 64 bytes
@@ -492,7 +544,8 @@ CommandOptions::decodeDuid(const std::string& base) {
             ui = convertHexString(b.substr(i, 2));
         } catch (isc::InvalidParameter&) {
             isc_throw(isc::InvalidParameter,
-                      "invalid characters in DUID provided, exepected hex digits");
+                      "invalid characters in DUID provided,"
+                      " exepected hex digits");
         }
         duid_template.push_back(static_cast<uint8_t>(ui));
     }
@@ -518,7 +571,7 @@ CommandOptions::generateDuidTemplate() {
     duid_template_[1] = DUID_LLT & 0xff;
     duid_template_[2] = HWTYPE_ETHERNET >> 8;
     duid_template_[3] = HWTYPE_ETHERNET & 0xff;
-    
+
     // As described in RFC3315: 'the time value is the time
     // that the DUID is generated represented in seconds
     // since midnight (UTC), January 1, 2000, modulo 2^32.'
@@ -550,7 +603,8 @@ CommandOptions::convertHexString(const std::string& text) const {
     text_stream >> std::hex >> ui >> std::dec;
     // Check if for some reason we have overflow - this should never happen!
     if (ui > 0xFF) {
-        isc_throw(isc::InvalidParameter, "Can't convert more than two hex digits to byte");
+        isc_throw(isc::InvalidParameter, "Can't convert more than"
+                  " two hex digits to byte");
     }
     return ui;
 }
@@ -582,29 +636,29 @@ CommandOptions::validate() const {
           "-S<srvid-offset> is not compatible with -i\n");
     check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
           "-I<ip-offset> is not compatible with -i\n");
-	check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
+    check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
           "-i must be set to use -c\n");
-	check((getRate() == 0) && (getReportDelay() != 0),
+    check((getRate() == 0) && (getReportDelay() != 0),
           "-r<rate> must be set to use -t<report>\n");
-	check((getRate() == 0) && (getNumRequests().size() > 0),
+    check((getRate() == 0) && (getNumRequests().size() > 0),
           "-r<rate> must be set to use -n<num-request>\n");
-	check((getRate() == 0) && (getPeriod() != 0),
+    check((getRate() == 0) && (getPeriod() != 0),
           "-r<rate> must be set to use -p<test-period>\n");
-	check((getRate() == 0) &&
+    check((getRate() == 0) &&
           ((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
           "-r<rate> must be set to use -D<max-drop>\n");
-	check((getTemplateFiles().size() < getTransactionIdOffset().size()),
+    check((getTemplateFiles().size() < getTransactionIdOffset().size()),
           "-T<template-file> must be set to use -X<xid-offset>\n");
-	check((getTemplateFiles().size() < getRandomOffset().size()),
+    check((getTemplateFiles().size() < getRandomOffset().size()),
           "-T<template-file> must be set to use -O<random-offset>\n");
-	check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
+    check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
           "second/request -T<template-file> must be set to use -E<time-offset>\n");
-	check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
+    check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
           "second/request -T<template-file> must be set to "
           "use -S<srvid-offset>\n");
-	check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
-			"second/request -T<template-file> must be set to "
-			"use -I<ip-offset>\n");
+    check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
+          "second/request -T<template-file> must be set to "
+          "use -I<ip-offset>\n");
 
 }
 
@@ -657,7 +711,7 @@ CommandOptions::printCommandLine() const {
         } else {
             std::cout << "SOLICIT-ADVERETISE only" << std::endl;
         }
-    } 
+    }
     if (rate_ != 0) {
         std::cout << "rate[1/s]=" << rate_ <<  std::endl;
     }
@@ -666,7 +720,7 @@ CommandOptions::printCommandLine() const {
     }
     if (clients_num_ != 0) {
         std::cout << "clients=" << clients_num_ << std::endl;
-    } 
+    }
     for (int i = 0; i < base_.size(); ++i) {
         std::cout << "base[" << i << "]=" << base_[i] <<  std::endl;
     }
@@ -742,127 +796,126 @@ CommandOptions::printCommandLine() const {
 
 void
 CommandOptions::usage() const {
-	fprintf(stdout, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-"    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-"    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-"    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-"    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-"    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-"    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact.  For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-"    control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
-"    clients.  This can be specified multiple times, each instance is\n"
-"    in the <type>=<value> form, for instance:\n"
-"    (and default) mac=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-"    having been lost.  The value is given in seconds and may contain a\n"
-"    fractional component.  The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-"    elapsed-time option in the (second/request) template.\n"
-"    The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-"    whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-"    option / (DHCPv6) IA_NA option in the (second/request) template.\n"
-"-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
-"    hostname/address to use when communicating with the server.  By\n"
-"    default, the interface address through which traffic would\n"
-"    normally be routed to the server is used.\n"
-"    For DHCPv6 operation, specify the name of the network interface\n"
-"    via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-"    (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-"    exchanges per second.  A periodic report is generated showing the\n"
-"    number of exchanges which were not completed, as well as the\n"
-"    average response latency.  The program continues until\n"
-"    interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-"    (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-"    (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-"    as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-"    the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-"    <diagnostic-selector> is a string of single-keywords specifying\n"
-"    the operations for which verbose output is desired.  The selector\n"
-"    keyletters are:\n"
-"   * 'a': print the decoded command line arguments\n"
-"   * 'e': print the exit reason\n"
-"   * 'i': print rate processing details\n"
-"   * 'r': print randomization details\n"
-"   * 's': print first server-id\n"
-"   * 't': when finished, print timers of all successful exchanges\n"
-"   * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-"    been dropped.  Use -D0 to abort if even a single request has been\n"
-"    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
-"    maximum percentage of requests that may be dropped before abort.\n"
-"    In this case, testing of the threshold begins after 10 requests\n"
-"    have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions.  No report is\n"
-"    generated until all transactions have been initiated/waited-for,\n"
-"    after which a report is generated and the program terminates.\n"
-"-p<test-period>: Send requests for the given test period, which is\n"
-"    specified in the same manner as -d.  This can be used as an\n"
-"    alternative to -n, or both options can be given, in which case the\n"
-"    testing is completed when either limit is reached.\n"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-"   (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-"  exchanges are not successfully completed.\n");
+    std::cout <<
+        "perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
+        "    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
+        "    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
+        "    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
+        "    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
+        "    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
+        "    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+        "\n"
+        "The [server] argument is the name/address of the DHCP server to\n"
+        "contact.  For DHCPv4 operation, exchanges are initiated by\n"
+        "transmitting a DHCP DISCOVER to this address.\n"
+        "\n"
+        "For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
+        "SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
+        "can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
+        "multicast address FF02::1:2), or the special name 'servers' to refer\n"
+        "to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
+        "argument is optional only in the case that -l is used to specify an\n"
+        "interface, in which case [server] defaults to 'all'.\n"
+        "\n"
+        "The default is to perform a single 4-way exchange, effectively pinging\n"
+        "the server.\n"
+        "The -r option is used to set up a performance test, without\n"
+        "it exchanges are initiated as fast as possible.\n"
+        "\n"
+        "Options:\n"
+        "-1: Take the server-ID option from the first received message.\n"
+        "-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
+        "-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
+        "-a<aggressivity>: When the target sending rate is not yet reached,\n"
+        "    control how many exchanges are initiated before the next pause.\n"
+        "-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
+        "    clients.  This can be specified multiple times, each instance is\n"
+        "    in the <type>=<value> form, for instance:\n"
+        "    (and default) mac=00:0c:01:02:03:04.\n"
+        "-d<drop-time>: Specify the time after which a requeqst is treated as\n"
+        "    having been lost.  The value is given in seconds and may contain a\n"
+        "    fractional component.  The default is 1 second.\n"
+        "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
+        "    elapsed-time option in the (second/request) template.\n"
+        "    The value 0 disables it.\n"
+        "-h: Print this help.\n"
+        "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
+        "    whether -6 is given.\n"
+        "-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
+        "    option / (DHCPv6) IA_NA option in the (second/request) template.\n"
+        "-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
+        "    hostname/address to use when communicating with the server.  By\n"
+        "    default, the interface address through which traffic would\n"
+        "    normally be routed to the server is used.\n"
+        "    For DHCPv6 operation, specify the name of the network interface\n"
+        "    via which exchanges are initiated.\n"
+        "-L<local-port>: Specify the local port to use\n"
+        "    (the value 0 means to use the default).\n"
+        "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
+        "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
+        "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
+        "    exchanges per second.  A periodic report is generated showing the\n"
+        "    number of exchanges which were not completed, as well as the\n"
+        "    average response latency.  The program continues until\n"
+        "    interrupted, at which point a final report is generated.\n"
+        "-R<range>: Specify how many different clients are used. With 1\n"
+        "    (the default), all requests seem to come from the same client.\n"
+        "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
+        "-S<srvid-offset>: Offset of the server-ID option in the\n"
+        "    (second/request) template.\n"
+        "-T<template-file>: The name of a file containing the template to use\n"
+        "    as a stream of hexadecimal digits.\n"
+        "-v: Report the version number of this program.\n"
+        "-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
+        "    the program.\n"
+        "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
+        "    <diagnostic-selector> is a string of single-keywords specifying\n"
+        "    the operations for which verbose output is desired.  The selector\n"
+        "    keyletters are:\n"
+        "   * 'a': print the decoded command line arguments\n"
+        "   * 'e': print the exit reason\n"
+        "   * 'i': print rate processing details\n"
+        "   * 's': print first server-id\n"
+        "   * 't': when finished, print timers of all successful exchanges\n"
+        "   * 'T': when finished, print templates\n"
+        "-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
+        "\n"
+        "DHCPv4 only options:\n"
+        "-B: Force broadcast handling.\n"
+        "\n"
+        "DHCPv6 only options:\n"
+        "-c: Add a rapid commit option (exchanges will be SA).\n"
+        "\n"
+        "The remaining options are used only in conjunction with -r:\n"
+        "\n"
+        "-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
+        "    been dropped.  Use -D0 to abort if even a single request has been\n"
+        "    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
+        "    maximum percentage of requests that may be dropped before abort.\n"
+        "    In this case, testing of the threshold begins after 10 requests\n"
+        "    have been expected to be received.\n"
+        "-n<num-request>: Initiate <num-request> transactions.  No report is\n"
+        "    generated until all transactions have been initiated/waited-for,\n"
+        "    after which a report is generated and the program terminates.\n"
+        "-p<test-period>: Send requests for the given test period, which is\n"
+        "    specified in the same manner as -d.  This can be used as an\n"
+        "    alternative to -n, or both options can be given, in which case the\n"
+        "    testing is completed when either limit is reached.\n"
+        "-t<report>: Delay in seconds between two periodic reports.\n"
+        "\n"
+        "Errors:\n"
+        "- tooshort: received a too short message\n"
+        "- orphans: received a message which doesn't match an exchange\n"
+        "   (duplicate, late or not related)\n"
+        "- locallimit: reached to local system limits when sending a message.\n"
+        "\n"
+        "Exit status:\n"
+        "The exit status is:\n"
+        "0 on complete success.\n"
+        "1 for a general error.\n"
+        "2 if an error is found in the command line arguments.\n"
+        "3 if there are no general failures in operation, but one or more\n"
+        "  exchanges are not successfully completed.\n";
 }
 
 void
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index b9e2b9e..4668f73 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -57,7 +57,8 @@ public:
     /// \param argc Argument count passed to main().
     /// \param argv Argument value array passed to main().
     /// \throws isc::InvalidParameter if parse fails.
-    void parse(int argc, char** const argv);
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    bool parse(int argc, char** const argv);
 
     /// \brief Returns IP version.
     ///
@@ -230,7 +231,7 @@ public:
     ///
     /// \return server name.
     std::string getServerName() const { return server_name_; }
-    
+
     /// \brief Print command line arguments.
     void printCommandLine() const;
 
@@ -261,7 +262,8 @@ private:
     /// \param argc Argument count passed to main().
     /// \param argv Argument value array passed to main().
     /// \throws isc::InvalidParameter if command line options initialization fails.
-    void initialize(int argc, char** argv);
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    bool initialize(int argc, char** argv);
 
     /// \brief Validates initialized options.
     ///
@@ -349,7 +351,7 @@ private:
     /// \param base Base string given as -b duid=0F1234.
     /// \throws isc::InvalidParameter if DUID is invalid.
     void decodeDuid(const std::string& base);
-    
+
     /// \brief Generates DUID-LLT (based on link layer address).
     ///
     /// Function generates DUID based on link layer address and
@@ -364,65 +366,97 @@ private:
     /// \throw isc::InvalidParameter if string does not represent hex byte.
     uint8_t convertHexString(const std::string& hex_text) const;
 
-    uint8_t ipversion_;                      ///< IP protocol version to be used, expected values are:
-                                             ///< 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
-    ExchangeMode exchange_mode_;             ///< Packet exchange mode (e.g. DORA/SARR)
-    int rate_;                               ///< Rate in exchange per second
-    int report_delay_;                       ///< Delay between generation of two consecutive
-                                             ///< performance reports
-    uint32_t clients_num_;                   ///< Number of simulated clients (aka randomization range).
-    std::vector<uint8_t> mac_template_;      ///< MAC address template used to generate unique DUIDs
-                                             ///< for simulated clients.
-    std::vector<uint8_t> duid_template_;     ///< DUID template used to generate unique DUIDs for
-                                             ///< simulated clients
-    std::vector<std::string> base_;          ///< Collection of base values specified with -b<value>
-                                             ///< options. Supported "bases" are mac=<mac> and duid=<duid>
-    std::vector<int> num_request_;           ///< Number of 2 or 4-way exchanges to perform
-    int period_;                             ///< Test period in seconds
-    uint8_t drop_time_set_;                  ///< Indicates number of -d<value> parameters specified by user.
-                                             ///< If this value goes above 2, command line parsing fails.
-    std::vector<double> drop_time_;          ///< Time to elapse before request is lost. The fisrt value of
-                                             ///< two-element vector refers to DO/SA exchanges,
-                                             ///< second value refers to RA/RR. Default values are { 1, 1 }
-    std::vector<int> max_drop_;              ///< Maximum number of drops request before aborting test.
-                                             ///< First value of two-element vector specifies maximum
-                                             ///< number of drops for DO/SA exchange, second value
-                                             ///< specifies maximum number of drops for RA/RR.
-    std::vector<double> max_pdrop_;          ///< Maximal percentage of lost requests before aborting test.
-                                             ///< First value of two-element vector specifies percentage for
-                                             ///< DO/SA exchanges, second value for RA/RR.
-    std::string localname_;                  ///< Local address or interface specified with -l<value> option.
-    bool is_interface_;                      ///< Indicates that specified value with -l<value> is
-                                             ///< rather interface (not address)
-    int preload_;                            ///< Number of preload packets. Preload packets are used to
-                                             ///< initiate communication with server before doing performance
-                                             ///< measurements.
-    int aggressivity_;                       ///< Number of exchanges sent before next pause.
-    int local_port_;                         ///< Local port number (host endian)
-    bool seeded_;                            ///< Indicates that randomization seed was provided.
-    uint32_t seed_;                          ///< Randomization seed.
-    bool broadcast_;                         ///< Indicates that we use broadcast address.
-    bool rapid_commit_;                      ///< Indicates that we do rapid commit option.
-    bool use_first_;                         ///< Indicates that we take server id from first received packet.
-    std::vector<std::string> template_file_; ///< Packet template file names. These files store template packets
-                                             ///< that are used for initiating echanges. Template packets
-                                             ///< read from files are later tuned with variable data.
-    std::vector<int> xid_offset_;            ///< Offset of transaction id in template files. First vector
-                                             ///< element points to offset for DISCOVER/SOLICIT messages,
-                                             ///< second element points to trasaction id offset for
-                                             ///< REQUEST messages
-    std::vector<int> rnd_offset_;            ///< Random value offset in templates. Random value offset
-                                             ///< points to last octet of DUID. Up to 4 last octets of
-                                             ///< DUID are randomized to simulate differnt clients.
-    int elp_offset_;                         ///< Offset of elapsed time option in template packet.
-    int sid_offset_;                         ///< Offset of server id option in template packet.
-    int rip_offset_;                         ///< Offset of requested ip data in template packet/
-    std::string diags_;                      ///< String representing diagnostic selectors specified
-                                             ///< by user with -x<value>.
-    std::string wrapped_;                    ///< Wrapped command specified as -w<value>. Expected
-                                             ///< values are start and stop.
-    std::string server_name_;                ///< Server name specified as last argument of command line.
-    std::string commandline_;                ///< Entire command line as typed in by the user.
+    /// IP protocol version to be used, expected values are:
+    /// 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
+    uint8_t ipversion_;
+    /// Packet exchange mode (e.g. DORA/SARR)
+    ExchangeMode exchange_mode_;
+    /// Rate in exchange per second
+    int rate_;
+    /// Delay between generation of two consecutive
+    /// performance reports
+    int report_delay_;
+    /// Number of simulated clients (aka randomization range).
+    uint32_t clients_num_;
+    /// MAC address template used to generate unique MAC
+    /// addresses for simulated clients.
+    std::vector<uint8_t> mac_template_;
+    /// DUID template used to generate unique DUIDs for
+    /// simulated clients
+    std::vector<uint8_t> duid_template_;
+    /// Collection of base values specified with -b<value>
+    /// options. Supported "bases" are mac=<mac> and duid=<duid>
+    std::vector<std::string> base_;
+    /// Number of 2 or 4-way exchanges to perform.
+    std::vector<int> num_request_;
+    /// Test period in seconds
+    int period_;
+    /// Indicates number of -d<value> parameters specified by user.
+    /// If this value goes above 2, command line parsing fails.
+    uint8_t drop_time_set_;
+    /// Time to elapse before request is lost. The fisrt value of
+    /// two-element vector refers to DO/SA exchanges,
+    /// second value refers to RA/RR. Default values are { 1, 1 }
+    std::vector<double> drop_time_;
+    /// Maximum number of drops request before aborting test.
+    /// First value of two-element vector specifies maximum
+    /// number of drops for DO/SA exchange, second value
+    /// specifies maximum number of drops for RA/RR.
+    std::vector<int> max_drop_;
+    /// Maximal percentage of lost requests before aborting test.
+    /// First value of two-element vector specifies percentage for
+    /// DO/SA exchanges, second value for RA/RR.
+    std::vector<double> max_pdrop_;
+    /// Local address or interface specified with -l<value> option.
+    std::string localname_;
+    /// Indicates that specified value with -l<value> is
+    /// rather interface (not address)
+    bool is_interface_;
+    /// Number of preload packets. Preload packets are used to
+    /// initiate communication with server before doing performance
+    /// measurements.
+    int preload_;
+    /// Number of exchanges sent before next pause.
+    int aggressivity_;
+    /// Local port number (host endian)
+    int local_port_;
+    /// Randomization seed.
+    uint32_t seed_;
+    /// Indicates that randomization seed was provided.
+    bool seeded_;
+    /// Indicates that we use broadcast address.
+    bool broadcast_;
+    /// Indicates that we do rapid commit option.
+    bool rapid_commit_;
+    /// Indicates that we take server id from first received packet.
+    bool use_first_;
+    /// Packet template file names. These files store template packets
+    /// that are used for initiating echanges. Template packets
+    /// read from files are later tuned with variable data.
+    std::vector<std::string> template_file_;
+    /// Offset of transaction id in template files. First vector
+    /// element points to offset for DISCOVER/SOLICIT messages,
+    /// second element points to trasaction id offset for
+    /// REQUEST messages
+    std::vector<int> xid_offset_;
+    /// Random value offset in templates. Random value offset
+    /// points to last octet of DUID. Up to 4 last octets of
+    /// DUID are randomized to simulate differnt clients.
+    std::vector<int> rnd_offset_;
+    /// Offset of elapsed time option in template packet.
+    int elp_offset_;
+    /// Offset of server id option in template packet.
+    int sid_offset_;
+    /// Offset of requested ip data in template packet
+    int rip_offset_;
+    /// String representing diagnostic selectors specified
+    /// by user with -x<value>.
+    std::string diags_;
+    /// Command to be executed at the beginning/end of the test.
+    /// This command is expected to expose start and stop argument.
+    std::string wrapped_;
+    /// Server name specified as last argument of command line.
+    std::string server_name_;
 };
 
 } // namespace perfdhcp
diff --git a/tests/tools/perfdhcp/main.cc b/tests/tools/perfdhcp/main.cc
index 0c706a2..aa202f1 100644
--- a/tests/tools/perfdhcp/main.cc
+++ b/tests/tools/perfdhcp/main.cc
@@ -26,25 +26,35 @@ using namespace isc::perfdhcp;
 int
 main(int argc, char* argv[]) {
     CommandOptions& command_options = CommandOptions::instance();
+    std::string diags(command_options.getDiags());
+    int ret_code = 0;
     try {
-        command_options.parse(argc, argv);
+        // If parser returns true it means that user specified
+        // 'h' or 'v' command line option. Program shows the
+        // help or version message and exits here.
+        if (command_options.parse(argc, argv)) {
+            return (ret_code);
+        }
     } catch(isc::Exception& e) {
-        std::cout << "Error parsing command line options: "
+        ret_code = 1;
+        std::cerr << "Error parsing command line options: "
                   << e.what() << std::endl;
         command_options.usage();
-        return(1);
+        if (diags.find('e') != std::string::npos) {
+            std::cerr << "Fatal error" << std::endl;
+        }
+        return (ret_code);
     }
     try{
         TestControl& test_control = TestControl::instance();
-        test_control.run();
+        ret_code =  test_control.run();
     } catch (isc::Exception& e) {
-        std::cout << "Error running perfdhcp: " << e.what() << std::endl;
-        std::string diags(command_options.getDiags());
+        ret_code = 1;
+        std::cerr << "Error running perfdhcp: " << e.what() << std::endl;
         if (diags.find('e') != std::string::npos) {
-            std::cout << "Fatal error" << std::endl;
+            std::cerr << "Fatal error" << std::endl;
         }
-        return(1);
     }
-    return(0);
+    return (ret_code);
 }
 
diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c
deleted file mode 100644
index 3ab9a2e..0000000
--- a/tests/tools/perfdhcp/perfdhcp.c
+++ /dev/null
@@ -1,3559 +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 <config.h>
-
-#ifndef HAVE_GETIFADDRS
-
-/*
- * Solaris 10 does not have the getifaddrs() function available (although it
- * is present on Solaris 11 and later).  For the present we will not implement
- * a replacement (as we do with clock_gettime) as the implementation is
- * relatively complex.  Just output a message saying that the utility is not
- * supported on this operating system.
- */
-
-#include <stdio.h>
-
-int
-main(const int argc, char* const argv[])
-{
-    fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n");
-    return (1);
-}
-
-#else
-
-/* getifaddrs() present, so the code should compile */
-
-#ifdef __linux__
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ifaddrs.h>
-#include <math.h>
-#include <netdb.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef HAVE_PSELECT
-
-#include <assert.h>
-
-/* Platforms such as OpenBSD don't provide a pselect(), so we use our
-   own implementation for this testcase, which wraps around select() and
-   hence doesn't implement the high precision timer. This implementation
-   is fine for our purpose. */
-
-static int
-pselect (int nfds, fd_set *readfds, fd_set *writefds,
-         fd_set *exceptfds, const struct timespec *timeout,
-         const sigset_t *sigmask)
-{
-	struct timeval my_timeout;
-
-	/* Our particular usage of pselect() doesn't use these fields. */
-	assert(writefds == NULL);
-	assert(exceptfds == NULL);
-	assert(sigmask == NULL);
-
-	my_timeout.tv_sec = timeout->tv_sec;
-	my_timeout.tv_usec = timeout->tv_nsec / 1000;
-
-	return (select(nfds, readfds, writefds, exceptfds, &my_timeout));
-}
-
-#endif /* !HAVE_PSELECT */
-
-/* DHCPv4 defines (to be moved/shared) */
-
-#define DHCP_OFF_OPCODE		0
-#define DHCP_OFF_HTYPE		1
-#define DHCP_OFF_HLEN		2
-#define DHCP_OFF_HOPS		3
-#define DHCP_OFF_XID		4
-#define DHCP_OFF_SECS		8
-#define DHCP_OFF_FLAGS		10
-#define DHCP_OFF_CIADDR		12
-#define DHCP_OFF_YIADDR		16
-#define DHCP_OFF_SADDR		20
-#define DHCP_OFF_GIADDR		24
-#define DHCP_OFF_CHADDR		28
-#define DHCP_OFF_SNAME		44
-#define DHCP_OFF_FILE		108
-#define DHCP_OFF_COOKIE		236
-#define DHCP_OFF_OPTIONS	240
-
-#define BOOTP_OP_REQUEST	1
-#define BOOTP_OP_REPLY		2
-#define BOOTP_MIN_LEN		300
-
-#define DHCP_OP_DISCOVER	1
-#define DHCP_OP_OFFER		2
-#define DHCP_OP_REQUEST		3
-#define DHCP_OP_DECLINE		4
-#define DHCP_OP_ACK		5
-#define DHCP_OP_NAK		6
-#define DHCP_OP_RELEASE		7
-#define DHCP_OP_INFORM		8
-
-#define DHCP_HTYPE_ETHER	1
-
-#define DHCP_OPT_PAD		0
-#define DHCP_OPT_SUBNET_MASK	1
-#define DHCP_OPT_TIME_OFFSET	2
-#define DHCP_OPT_ROUTERS	3
-#define DHCP_OPT_DNS_SERVERS	6
-#define DHCP_OPT_HOST_NAME	12
-#define DHCP_OPT_DOMAIN_NAME	15
-#define DHCP_OPT_BROADCAST	28
-#define DHCP_OPT_DHCP_ADDRESS	50
-#define DHCP_OPT_DHCP_LEASE	51
-#define DHCP_OPT_DHCP_MSGTYPE	53
-#define DHCP_OPT_DHCP_SRVID	54
-#define DHCP_OPT_DHCP_PRL	55
-#define DHCP_OPT_END		255
-
-#define DHCP_OPTLEN_SRVID	6
-
-/* DHCPv6 defines  (to be moved/shared) */
-
-#define DHCP6_OFF_MSGTYP	0
-#define DHCP6_OFF_XID		1
-#define DHCP6_OFF_OPTIONS	4
-
-#define DHCP6_OP_SOLICIT	1
-#define DHCP6_OP_ADVERTISE	2
-#define DHCP6_OP_REQUEST	3
-#define DHCP6_OP_REPLY		7
-
-#define DHCP6_OPT_CLIENTID	1
-#define DHCP6_OPT_SERVERID	2
-#define DHCP6_OPT_IA_NA		3
-#define DHCP6_OPT_ORO		6
-#define DHCP6_OPT_ELAPSED_TIME	8
-#define DHCP6_OPT_STATUS_CODE	13
-#define DHCP6_OPT_RAPID_COMMIT	14
-#define DHCP6_OPT_NAME_SERVERS	23
-#define DHCP6_OPT_DOMAIN_SEARCH	24
-
-#define DHCP6_ST_SUCCESS	0
-#define DHCP6_ST_NOADDRSAVAIL	2
-
-#define DHCP6_DUID_LLT		1
-#define DHCP6_DUID_EPOCH	946684800
-
-/* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */
-
-#define ISC_TAILQ_HEAD(name, type)					\
-struct name {								\
-	struct type *tqh_first;						\
-	struct type **tqh_last;						\
-}
-
-#define ISC_TAILQ_ENTRY(type)						\
-struct {								\
-	struct type *tqe_next;						\
-	struct type **tqe_prev;						\
-}
-
-#define ISC_TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
-
-#define ISC_TAILQ_FIRST(head)	((head)->tqh_first)
-
-#define ISC_TAILQ_LAST(head, headname)					\
-	(*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define ISC_TAILQ_NEXT(elm, field)	((elm)->field.tqe_next)
-
-#define ISC_TAILQ_PREV(elm, headname, field)				\
-	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define ISC_TAILQ_INIT(head)	do {					\
-	ISC_TAILQ_FIRST((head)) = NULL;					\
-	(head)->tqh_last = &ISC_TAILQ_FIRST((head));			\
-} while (0)
-
-#define ISC_TAILQ_INSERT_HEAD(head, elm, field)	do {			\
-	ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head));		\
-	if (!ISC_TAILQ_EMPTY((head)))					\
-		ISC_TAILQ_FIRST((head))->field.tqe_prev =		\
-			&ISC_TAILQ_NEXT((elm), field);			\
-	else								\
-		(head)->tqh_last = &ISC_TAILQ_NEXT((elm), field);	\
-	ISC_TAILQ_FIRST((head)) = (elm);				\
-	(elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head));		\
-} while (0)
-
-#define ISC_TAILQ_INSERT_TAIL(head, elm, field)	do {			\
-	ISC_TAILQ_NEXT((elm), field) = NULL;				\
-	(elm)->field.tqe_prev = (head)->tqh_last;			\
-	*(head)->tqh_last = (elm);					\
-	(head)->tqh_last = &ISC_TAILQ_NEXT((elm), field);		\
-} while (0)
-
-#define ISC_TAILQ_REMOVE(head, elm, field)	do {			\
-	if ((ISC_TAILQ_NEXT((elm), field)) != NULL)			\
-		ISC_TAILQ_NEXT((elm), field)->field.tqe_prev =		\
-			(elm)->field.tqe_prev;				\
-	else								\
-		(head)->tqh_last = (elm)->field.tqe_prev;		\
-	*(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field);		\
-} while (0)
-
-#define ISC_TAILQ_FOREACH(var, head, field)				\
-	for ((var) = ISC_TAILQ_FIRST((head));				\
-	     (var);							\
-	     (var) = ISC_TAILQ_NEXT((var), field))
-
-#define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
-	for ((var) = ISC_TAILQ_FIRST((head));				\
-	     (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1);	\
-	     (var) = (tvar))
-
-/*
- * Data structures
- */
-
-/*
- * exchange:
- *    - per exchange values:
- *	* order (for debugging)
- *	* xid (even/odd for 4 packet exchanges)
- *	* random (for debugging)
- *	* time-stamps
- *	* server ID (for 3rd packet)
- *	* IA_NA (for IPv6 3rd packet)
- *
- * sent/rcvd global chains, "next to be received" on entry cache,
- * and hash table for xid -> data structure fast matching
- * (using the assumption collisions are unlikely, cf birthday problem)
- */
-
-struct exchange {				/* per exchange structure */
-	ISC_TAILQ_ENTRY(exchange) gchain;	/* global chaining */
-	ISC_TAILQ_ENTRY(exchange) hchain;	/* hash table chaining */
-	uint64_t order0, order2;		/* number of this exchange */
-	uint32_t xid;				/* transaction ID */
-	uint32_t rnd;				/* random part */
-	struct timespec ts0, ts1, ts2, ts3;	/* timespecs */
-	uint8_t *sid;				/* server ID */
-	size_t sidlen;				/* server ID length */
-	uint8_t *iana;				/* (IPv6) IA_NA */
-	size_t ianalen;				/* (IPv6) IA_NA length */
-};
-struct exchange *xnext0, *xnext2;		/* next to be received */
-ISC_TAILQ_HEAD(xlist, exchange);		/* exchange list */
-struct xlist xsent0, xsent2, xrcvd0, xrcvd2;	/* sent and received lists */
-uint64_t xscount0, xscount2;			/* sent counters */
-uint64_t xrcount0, xrcount2;			/* received counters */
-caddr_t exchanges0, exchanges2;			/* hash tables */
-uint32_t hashsize0, hashsize2;			/* hash table sizes */
-
-/*
- * statictics counters and accumulators
- */
-
-uint64_t tooshort, orphans, locallimit;		/* error counters */
-uint64_t latesent, compsend, latercvd;		/* rate stats */
-uint64_t multrcvd, shortwait, collected[2];	/* rate stats (cont) */
-double dmin0 = 999999999., dmin2 = 999999999.;	/* minimum delays */
-double dmax0 = 0., dmax2 = 0.;			/* maximum delays */
-double dsum0 = 0., dsum2 = 0.;			/* delay sums */
-double dsumsq0 = 0., dsumsq2 = 0.;		/* square delay sums */
-
-/*
- * command line parameters
- */
-
-int ipversion = 0;			/* IP version */
-int simple;				/* DO/SA in place of DORR/SARR */
-int rate;				/* rate in exchange per second */
-int report;				/* delay between two reports */
-uint32_t range;				/* randomization range */
-uint32_t maxrandom;			/* maximum random value */
-int basecnt;				/* base count */
-char *base[4];				/* bases */
-int gotnumreq;				/* numreq[0] was set */
-int numreq[2];				/* number of exchange */
-int period;				/* test period */
-int gotlosttime;			/* losttime[0] was set */
-double losttime[2] = {1., 1.};		/* time after a request is lost  */
-int gotmaxdrop;				/* max{p}drop[0] was set */
-int maxdrop[2];				/* maximum number of lost requests */
-double maxpdrop[2] = { 0., 0.};		/* maximum percentage */
-char *localname;			/* local address or interface */
-int isinterface;			/* interface vs local address */
-int preload;				/* preload exchanges */
-int aggressivity = 1;			/* back to back exchanges */
-int localport;				/* local port number (host endian) */
-int seeded;				/* is a seed provided */
-unsigned int seed;			/* randomization seed */
-int isbroadcast;			/* use broadcast */
-int rapidcommit;			/* add rapid commit option */
-int usefirst;				/* where to take the server-ID */
-char *templatefile[2];			/* template file name */
-int xidoffset[2] = {-1, -1};		/* template offsets (xid)*/
-int rndoffset[2] = {-1, -1};		/* template offsets (random) */
-int elpoffset = -1;			/* template offset (elapsed time) */
-int sidoffset = -1;			/* template offset (server ID) */
-int ripoffset = -1;			/* template offset (requested IP) */
-char *diags;				/* diagnostic selectors */
-char *wrapped;				/* wrapped command */
-char *servername;			/* server */
-
-/*
- * global variables
- */
-
-struct sockaddr_storage localaddr;	/* local socket address */
-struct sockaddr_storage serveraddr;	/* server socket address */
-
-int sock;				/* socket descriptor */
-int interrupted, fatal;			/* to finish flags */
-
-uint8_t obuf[4096], ibuf[4096];		/* I/O buffers */
-char tbuf[8200];			/* template buffer */
-
-struct timespec boot;			/* the date of boot */
-struct timespec last;			/* the date of last send */
-struct timespec due;			/* the date of next send */
-struct timespec dreport;		/* the date of next reporting */
-struct timespec finished;		/* the date of finish */
-
-uint8_t *gsrvid;			/* global server id */
-size_t gsrvidlen;			/*  and its length */
-uint8_t gsrvidbuf[64];			/*  and its storage */
-
-/* MAC address */
-uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 };
-
-/* DUID prefix */
-uint8_t *duid_prefix;
-int duid_length;
-
-/* magic cookie for BOOTP/DHCPv4 */
-uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
-
-/*
- * templates
- *
- * note: the only hard point is what are the offsets:
- *   - xid_discover4 and xid_request4: first of the 4 octet long
- *     transaction ID (default DHCP_OFF_XID = 4)
- *   - random_discover4 and random_request4: last of the 6 octet long
- *     MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6)
- *   - elapsed_request4: first of the 2 octet long secs field
- *     (default DHCP_OFF_SECS = 8, 0 means disabled)
- *   - serverid_request4: first of the 6 octet long server ID option
- *     (no default, required)
- *   - reqaddr_request4: first of the 4 octet long requested IP address
- *     option content (i.e., the address itself, btw OFFER yiaddr)
- *     (no default, required)
- *   - xid_solicit6 and xid_request6: first of the 3 octet long
- *     transaction ID (default DHCP6_OFF_XID = 1)
- *   - random_solicit6 and random_request6: last of the DUID in the
- *     client ID option (no default, required when rate is set)
- *   - elapsed_request6: first of the 2 octet long content of
- *     the option elapsed time option (no default, 0 means disabled)
- *   - serverid_request6: position where the variable length server ID
- *     option is inserted (no default, required, set to length means append)
- *   - reqaddr_request6: position where of the variable length requested
- *     IP address option is inserted (no default, required, set to
- *     length means append)
- */
-
-size_t length_discover4;
-uint8_t template_discover4[4096];
-size_t xid_discover4;
-size_t random_discover4;
-size_t length_request4;
-uint8_t template_request4[4096];
-size_t xid_request4;
-size_t elapsed_request4;
-size_t random_request4;
-size_t serverid_request4;
-size_t reqaddr_request4;
-size_t length_solicit6;
-uint8_t template_solicit6[4096];
-size_t xid_solicit6;
-size_t random_solicit6;
-size_t length_request6;
-uint8_t template_request6[4096];
-size_t xid_request6;
-size_t elapsed_request6;
-size_t random_request6;
-size_t serverid_request6;
-size_t reqaddr_request6;
-
-
-// use definition of CLOCK_REALTIME (or lack of thereof) as an indicator
-// if the code is being compiled or Linux (or somewhere else)
-// Perhaps this should be based on OS_LINUX define?
-
-#if !defined (CLOCK_REALTIME)
-#define CLOCK_REALTIME 0
-
-/// @brief clock_gettime implementation for non-Linux systems
-///
-/// This implementation lacks nanosecond resolution. It is intended
-/// to be used on non-Linux systems that does not provide clock_gettime
-/// implementation.
-///
-/// @param clockid ignored (kept for Linux prototype compatibility)
-/// @param tp timespec structure
-///
-/// @return always zero (kept for compatibility reasons)
-int clock_gettime(int clockid, struct timespec *tp) {
-
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    tp->tv_sec = tv.tv_sec;
-    tp->tv_nsec = tv.tv_usec*1000;
-
-    return (0);
-}
-
-#endif
-
-/*
- * initialize data structures handling exchanges
- */
-
-void
-inits(void)
-{
-	struct xlist *bucket;
-	caddr_t p;
-	size_t len, i;
-
-	ISC_TAILQ_INIT(&xsent0);
-	ISC_TAILQ_INIT(&xsent2);
-	ISC_TAILQ_INIT(&xrcvd0);
-	ISC_TAILQ_INIT(&xrcvd2);
-
-	/// compute hashsizes
-	hashsize0 = 1024;
-	len = sizeof(*bucket) * hashsize0;
-	exchanges0 = malloc(len);
-	if (exchanges0 == NULL) {
-		perror("malloc(exchanges0)");
-		exit(1);
-	}
-	for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) {
-		bucket = (struct xlist *) p;
-		ISC_TAILQ_INIT(bucket);
-	}
-	if (simple != 0)
-		return;
-	hashsize2 = 1024;
-	len = sizeof(*bucket) * hashsize2;
-	exchanges2 = malloc(len);
-	if (exchanges2 == NULL) {
-		perror("malloc(exchanges2)");
-		exit(1);
-	}
-	for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) {
-		bucket = (struct xlist *) p;
-		ISC_TAILQ_INIT(bucket);
-	}
-}
-
-/*
- * randomize the value of the given field:
- *   - offset of the field
- *   - random seed (used as it when suitable)
- *   - returns the random value which was used
- */
-
-uint32_t
-randomize(size_t offset, uint32_t r)
-{
-	uint32_t v;
-
-	if (range == 0)
-		return 0;
-	if (range == UINT32_MAX)
-		return r;
-	if (maxrandom != 0)
-		while (r >= maxrandom)
-			r = (uint32_t) random();
-	r %= range + 1;
-	v = r;
-	v += obuf[offset];
-	obuf[offset] = v;
-	if (v < 256)
-		return r;
-	v >>= 8;
-	v += obuf[offset - 1];
-	obuf[offset - 1] = v;
-	if (v < 256)
-		return r;
-	v >>= 8;
-	v += obuf[offset - 2];
-	obuf[offset - 2] = v;
-	if (v < 256)
-		return r;
-	v >>= 8;
-	v += obuf[offset - 3];
-	obuf[offset - 3] = v;
-	return r;
-}
-
-/*
- * receive a reply (4th packet), shared between IPv4 and IPv6:
- *   - transaction ID xid
- *   - receiving time-stamp now
- * called from receive[46]() when the xid is odd
- */
-
-void
-receive_reply(uint32_t xid, struct timespec *now)
-{
-	struct exchange *x, *t;
-	struct xlist *bucket;
-	uint32_t hash;
-	int checklost;
-	double delta;
-
-	/* bucket is needed even when the next cache matches */
-	hash = (xid >> 1) & (hashsize2 - 1);
-	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
-	/* try the 'next to be received' cache */
-	if ((xnext2 != NULL) && (xnext2->xid == xid)) {
-		x = xnext2;
-		goto found;
-	}
-	/* usually the lost probability is low for request/reply */
-	checklost = 1;
-	/* look for the exchange */
-	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
-		double waited;
-
-		if (x->xid == xid)
-			goto found;
-		if (checklost <= 0)
-			continue;
-		checklost = 0;
-		/* check for a timed-out exchange */
-		waited = now->tv_sec - x->ts2.tv_sec;
-		waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9;
-		if (waited < losttime[1])
-			continue;
-		/* garbage collect timed-out exchange */
-		ISC_TAILQ_REMOVE(bucket, x, hchain);
-		ISC_TAILQ_REMOVE(&xsent2, x, gchain);
-		free(x);
-		collected[1] += 1;
-	}
-	/* no match? very late or not for us */
-	orphans++;
-	return;
-
-	/* got it: update stats and move to the received queue */
-    found:
-	xrcount2++;
-	x->ts3 = *now;
-	delta = x->ts3.tv_sec - x->ts2.tv_sec;
-	delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
-	if (delta < dmin2)
-		dmin2 = delta;
-	if (delta > dmax2)
-		dmax2 = delta;
-	dsum2 += delta;
-	dsumsq2 += delta * delta;
-	xnext2 = ISC_TAILQ_NEXT(x, gchain);
-	ISC_TAILQ_REMOVE(bucket, x, hchain);
-	ISC_TAILQ_REMOVE(&xsent2, x, gchain);
-	ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain);
-}
-
-/*
- * get the DHCPv4 socket descriptor
- * (the only complexity is broadcast enabling: there is no easy way to
- *  recognize broadcast addresses, so the command line -B flag)
- */
-
-void
-getsock4(void)
-{
-	int ret;
-
-	/* update local port */
-	if (localport != 0) {
-		uint16_t lp = htons((uint16_t) localport);
-
-		((struct sockaddr_in *) &localaddr)->sin_port = lp;
-	}
-	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
-	if (sock < 0) {
-		perror("socket");
-		exit(1);
-	}
-	ret = bind(sock,
-		   (struct sockaddr *) &localaddr,
-		   sizeof(struct sockaddr_in));
-	if (ret < 0) {
-		perror("bind");
-		exit(1);
-	}
-	/* enable broadcast if needed or required */
-	if (isbroadcast != 0) {
-		int on = 1;
-
-		ret = setsockopt(sock,
-				 SOL_SOCKET, SO_BROADCAST,
-				 &on, sizeof(on));
-		if (ret < 0) {
-			perror("setsockopt(SO_BROADCAST)");
-			exit(1);
-		}
-	}
-}
-
-/*
- * build a DHCPv4 DISCOVER from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_discover4(void)
-{
-	uint8_t *p = template_discover4;
-
-	length_discover4 = BOOTP_MIN_LEN;
-	xid_discover4 = DHCP_OFF_XID;
-	random_discover4 = DHCP_OFF_CHADDR + 6;
-	/* opcode */
-	p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
-	/* hardware address type */
-	p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
-	/* hardware address length */
-	p[DHCP_OFF_HLEN] = 6;
-	/* hops */
-	p[DHCP_OFF_HOPS] = 1;
-	/* gateway address */
-	memcpy(p + DHCP_OFF_GIADDR,
-	       &((struct sockaddr_in *) &localaddr)->sin_addr,
-	       4);
-	/* hardware address */
-	memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
-	/* cookie */
-	memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
-	/* options */
-	p += DHCP_OFF_OPTIONS;
-	/* inline DHCP message type */
-	*p++ = DHCP_OPT_DHCP_MSGTYPE;
-	*p++ = 1;
-	*p++ = DHCP_OP_DISCOVER;
-	/* inline DHCP parameter request list (default) */
-	*p++ = DHCP_OPT_DHCP_PRL;
-	*p++ = 7;
-	*p++ = DHCP_OPT_SUBNET_MASK;
-	*p++ = DHCP_OPT_BROADCAST;
-	*p++ = DHCP_OPT_TIME_OFFSET;
-	*p++ = DHCP_OPT_ROUTERS;
-	*p++ = DHCP_OPT_DOMAIN_NAME;
-	*p++ = DHCP_OPT_DNS_SERVERS;
-	*p++ = DHCP_OPT_HOST_NAME;
-	/* end */
-	*p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay first packet (usually a DISCOVER) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_discover4(void)
-{
-	uint8_t *p = template_discover4;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[0], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(1);
-	}
-	if (cc < 100) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[0]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[0]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[0]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[0]);
-		exit(2);
-	}
-	length_discover4 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[0] >= 0)
-		xid_discover4 = (size_t) xidoffset[0];
-	else
-		xid_discover4 = DHCP_OFF_XID;
-	if (xid_discover4 + 4 > length_discover4) {
-		fprintf(stderr,
-			"xid (at %zu) outside the template (length %zu)?\n",
-			xid_discover4, length_discover4);
-		exit(2);
-	}
-	if (rndoffset[0] >= 0)
-		random_discover4 = (size_t) rndoffset[0];
-	else
-		random_discover4 = DHCP_OFF_CHADDR + 6;
-	if (random_discover4 > length_discover4) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_discover4, length_discover4);
-		exit(2);
-	}
-}
-
-/*
- * build a DHCPv4 REQUEST from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_request4(void)
-{
-	uint8_t *p = template_request4;
-
-	length_request4 = BOOTP_MIN_LEN;
-	xid_request4 = DHCP_OFF_XID;
-	elapsed_request4 = DHCP_OFF_SECS;
-	random_request4 = DHCP_OFF_CHADDR + 6;
-	/* opcode */
-	p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
-	/* hardware address type */
-	p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
-	/* hardware address length */
-	p[DHCP_OFF_HLEN] = 6;
-	/* hops */
-	p[DHCP_OFF_HOPS] = 1;
-	/* gateway address */
-	memcpy(p + DHCP_OFF_GIADDR,
-	       &((struct sockaddr_in *) &localaddr)->sin_addr,
-	       4);
-	/* hardware address */
-	memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
-	/* cookie */
-	memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
-	/* options */
-	p += DHCP_OFF_OPTIONS;
-	/* inline DHCP message type */
-	*p++ = DHCP_OPT_DHCP_MSGTYPE;
-	*p++ = 1;
-	*p++ = DHCP_OP_REQUEST;
-	/* place for DHCP server id (option) */
-	serverid_request4 = p - template_request4;
-	p += DHCP_OPTLEN_SRVID;
-	/* place for DHCP requested IP address (address) */
-	*p++ = DHCP_OPT_DHCP_ADDRESS;
-	*p++ = 4;
-	reqaddr_request4 = p - template_request4;
-	p += 4;
-	/* inline DHCP parameter request list (default) */
-	*p++ = DHCP_OPT_DHCP_PRL;
-	*p++ = 7;
-	*p++ = DHCP_OPT_SUBNET_MASK;
-	*p++ = DHCP_OPT_BROADCAST;
-	*p++ = DHCP_OPT_TIME_OFFSET;
-	*p++ = DHCP_OPT_ROUTERS;
-	*p++ = DHCP_OPT_DOMAIN_NAME;
-	*p++ = DHCP_OPT_DNS_SERVERS;
-	*p++ = DHCP_OPT_HOST_NAME;
-	/* end */
-	*p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request4(void)
-{
-	uint8_t *p = template_request4;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[1], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(1);
-	}
-	if (cc < 100) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[1]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[1]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[1]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[1]);
-		exit(2);
-	}
-	length_request4 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[1] >= 0)
-		xid_request4 = (size_t) xidoffset[1];
-	else
-		xid_request4 = DHCP_OFF_XID;
-	if (xid_request4 + 4 > length_request4) {
-		fprintf(stderr,
-			"xid (at %zu) outside the template (length %zu)?\n",
-			xid_request4, length_request4);
-		exit(2);
-	}
-	if (rndoffset[1] >= 0)
-		random_request4 = (size_t) rndoffset[1];
-	else
-		random_request4 = DHCP_OFF_CHADDR + 6;
-	if (random_request4 > length_request4) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_request4, length_request4);
-		exit(2);
-	}
-	if (elpoffset >= 0)
-		elapsed_request4 = (size_t) elpoffset;
-	else
-		elapsed_request4 = DHCP_OFF_SECS;
-	if (elapsed_request4 + 2 > length_request4) {
-		fprintf(stderr,
-			"secs (at %zu) outside the template (length %zu)?\n",
-			elapsed_request4, length_request4);
-		exit(2);
-	}
-	serverid_request4 = (size_t) sidoffset;
-	if (serverid_request4 + 6 > length_request4) {
-		fprintf(stderr,
-			"server-id option (at %zu) outside the template "
-			"(length %zu)?\n",
-			serverid_request4, length_request4);
-		exit(2);
-	}
-	reqaddr_request4 = (size_t) ripoffset;
-	if (reqaddr_request4 + 4 > length_request4) {
-		fprintf(stderr,
-			"requested-ip-address option (at %zu) outside "
-			"the template (length %zu)?\n",
-			reqaddr_request4, length_request4);
-		exit(2);
-	}
-}
-
-/*
- * send the DHCPv4 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the OFFER)
- */
-
-void
-send_request4(struct exchange *x0)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL) {
-		locallimit++;
-		perror("send2");
-		return;
-	}
-
-	memcpy(x, x0, sizeof(*x));
-	x->order2 = xscount2++;
-	x->xid |= 1;
-	hash = x->xid >> 1;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
-	hash &= hashsize2 - 1;
-	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	memcpy(obuf, template_request4, length_request4);
-	/* xid */
-	memcpy(obuf + xid_request4, &x->xid, 4);
-	/* random */
-	randomize(random_request4, x->rnd);
-	/* secs */
-	if (elapsed_request4 > 0) {
-		int secs;
-
-		secs = x->ts1.tv_sec - x->ts0.tv_sec;
-		if (x->ts1.tv_nsec < x->ts0.tv_nsec)
-			secs += 1;
-		if (secs > 0) {
-			obuf[elapsed_request4] = secs >> 8;
-			obuf[elapsed_request4 + 1] = secs & 0xff;
-		}
-	}
-	/* server ID */
-	memcpy(obuf + serverid_request4, x->sid, x->sidlen);
-	/* requested IP address */
-	memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4);
-
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
-	if (ret < 0) {
-		perror("clock_gettime(send2)");
-		fatal = 1;
-		return;
-	}
-	ret = sendto(sock, obuf, length_request4, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in));
-	if (ret >= 0)
-		return;
-	if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
-	    (errno == ENOBUFS) || (errno == ENOMEM))
-		locallimit++;
-	perror("send2");
-}
-
-/*
- * send the DHCPv4 DISCOVER first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send4(void)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL)
-		return -ENOMEM;
-
-	memset(x, 0, sizeof(*x));
-	x->order0 = xscount0++;
-	hash = x->rnd = (uint32_t) random();
-	if (simple == 0)
-		x->xid = hash << 1;
-	else
-		x->xid = hash;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
-	hash &= hashsize0 - 1;
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	memcpy(obuf, template_discover4, length_discover4);
-	/* xid */
-	memcpy(obuf + xid_discover4, &x->xid, 4);
-	/* random */
-	x->rnd = randomize(random_discover4, x->rnd);
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &last);
-	if (ret < 0) {
-		perror("clock_gettime(send)");
-		fatal = 1;
-		return -errno;
-	}
-	x->ts0 = last;
-	errno = 0;
-	ret = sendto(sock, obuf, length_discover4, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in));
-	if (ret == (ssize_t) length_discover4)
-		return 0;
-	return -errno;
-}	
-
-/*
- * scan a DHCPv4 OFFER to get its server-id option
- */
-
-int
-scan_for_srvid4(struct exchange *x, size_t cc)
-{
-	size_t off = DHCP_OFF_OPTIONS;
-
-	for (;;) {
-		if (off + DHCP_OPTLEN_SRVID > cc) {
-			fprintf(stderr, "truncated\n");
-			return -1;
-		}
-		if (ibuf[off] == DHCP_OPT_DHCP_SRVID)
-			break;
-		if (ibuf[off] == DHCP_OPT_END) {
-			fprintf(stderr, "server-id not found\n");
-			return -1;
-		}
-		if (ibuf[off] == DHCP_OPT_PAD) {
-			off++;
-			continue;
-		}
-		off += 2 + ibuf[off + 1];
-	}
-	/* check length */
-	if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) {
-		fprintf(stderr,
-			"bad server-id length (%hhu)\n",
-			ibuf[off + 1]);
-		return -1;
-	}
-	/* cache it in the global variables when required and not yet done */
-	if ((usefirst != 0) && (gsrvid == NULL)) {
-		memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID);
-		gsrvid = gsrvidbuf;
-		gsrvidlen = DHCP_OPTLEN_SRVID;
-	}
-	x->sid = ibuf + off;
-	x->sidlen = DHCP_OPTLEN_SRVID;
-	return 0;
-}
-
-/*
- * receive a DHCPv4 packet
- */
-
-void
-receive4(void)
-{
-	struct exchange *x, *t;
-	struct xlist *bucket;
-	struct timespec now;
-	ssize_t cc;
-	uint32_t xid, hash;
-	int checklost = 0;
-	double delta;
-
-	cc = recv(sock, ibuf, sizeof(ibuf), 0);
-	if (cc < 0) {
-		if ((errno == EAGAIN) ||
-		    (errno == EWOULDBLOCK) ||
-		    (errno == EINTR))
-			return;
-		perror("recv");
-		fatal = 1;
-		return;
-	}
-	/* enforce a reasonable length */
-	if (cc < BOOTP_MIN_LEN) {
-		tooshort++;
-		return;
-	}
-	/* must be a BOOTP REPLY */
-	if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY)
-		return;
-	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-		perror("clock_gettime(receive)");
-		fatal = 1;
-		return;
-	}
-	memcpy(&xid, ibuf + xid_discover4, 4);
-	/* 4-packet exchange even/odd xid */
-	if (simple == 0) {
-		if ((xid & 1) != 0) {
-			receive_reply(xid, &now);
-			return;
-		}
-		hash = (xid >> 1) & (hashsize0 - 1);
-	} else
-		hash = xid & (hashsize0 - 1);
-	/* now it is the second packet, get the bucket which is needed */
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	/* try the 'next to be received' cache */
-	if ((xnext0 != NULL) && (xnext0->xid == xid)) {
-		x = xnext0;
-		goto found;
-	}
-	/* if the rate is not illimited, garbage collect up to 3
-	   timed-out exchanges */
-	if (rate != 0)
-		checklost = 3;
-	/* look for the exchange */
-	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
-		double waited;
-
-		if (x->xid == xid)
-			goto found;
-		if (checklost <= 0)
-			continue;
-		/* check for a timed-out exchange */
-		waited = now.tv_sec - x->ts0.tv_sec;
-		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
-		if (waited < losttime[0]) {
-			checklost = 0;
-			continue;
-		}
-		/* garbage collect timed-out exchange */
-		checklost--;
-		ISC_TAILQ_REMOVE(bucket, x, hchain);
-		ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-		free(x);
-		collected[0] += 1;
-	}
-	/* no match? very late or not for us */
-	orphans++;
-	return;
-
-	/* got it: update stats and move to the received queue */
-    found:
-	xrcount0++;
-	x->ts1 = now;
-	delta = x->ts1.tv_sec - x->ts0.tv_sec;
-	delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
-	if (delta < dmin0)
-		dmin0 = delta;
-	if (delta > dmax0)
-		dmax0 = delta;
-	dsum0 += delta;
-	dsumsq0 += delta * delta;
-	xnext0 = ISC_TAILQ_NEXT(x, gchain);
-	ISC_TAILQ_REMOVE(bucket, x, hchain);
-	ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-	ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
-	/* if the exchange is not finished, go to the second part */
-	if (simple == 0) {
-		int ret = 0;
-
-		/* the server-ID option is needed */
-		if ((usefirst != 0) && (gsrvid != NULL)) {
-			x->sid = gsrvid;
-			x->sidlen = gsrvidlen;
-		} else
-			ret = scan_for_srvid4(x, cc);
-		if (ret >= 0)
-			send_request4(x);
-	}
-}
-
-/*
- * get the DHCPv6 socket descriptor
- */
-
-void
-getsock6(void)
-{
-	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr;
-	int ret;
-
-	/* update local port */
-	if (localport != 0) {
-		uint16_t lp = htons((uint16_t) localport);
-
-		((struct sockaddr_in6 *) &localaddr)->sin6_port = lp;
-	}
-	sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-	if (sock < 0) {
-		perror("socket");
-		exit(1);
-	}
-	ret = bind(sock,
-		   (struct sockaddr *) &localaddr,
-		   sizeof(struct sockaddr_in6));
-	if (ret < 0) {
-		perror("Failed to bind v6 socket to local-link address");
-		exit(1);
-	}
-	/* perform the multicast stuff when the destination is multicast */
-	if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
-		int hops = 1;
-
-		ret = setsockopt(sock,
-				 IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-				 &hops, sizeof(hops));
-		if (ret < 0) {
-			perror("setsockopt(IPV6_MULTICAST_HOPS)");
-			exit(1);
-		}
-	}
-	if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
-		unsigned idx = if_nametoindex(localname);
-
-		if (idx == 0) {
-			fprintf(stderr,
-				"if_nametoindex(%s) failed\n",
-				localname);
-			exit(1);
-		}
-		ret = setsockopt(sock,
-				 IPPROTO_IPV6, IPV6_MULTICAST_IF,
-				 &idx, sizeof(idx));
-		if (ret < 0) {
-			perror("setsockopt(IPV6_MULTICAST_IF)");
-			exit(1);
-		}
-	}
-}
-
-/*
- * build a DHCPv6 SOLICIT template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_solicit6(void)
-{
-	uint8_t *p = template_solicit6;
-
-	xid_solicit6 = DHCP6_OFF_XID;
-	/* message type */
-	p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
-	/* options */
-	p += DHCP6_OFF_OPTIONS;
-	/* elapsed time */
-	p[1] = DHCP6_OPT_ELAPSED_TIME;
-	p[3] = 2;
-	p += 6;
-	/* rapid commit */
-	if (rapidcommit != 0) {
-		p[1] = DHCP6_OPT_RAPID_COMMIT;
-		p += 4;
-	}
-	/* client ID */
-	p[1] = DHCP6_OPT_CLIENTID;
-	p[3] = duid_length;
-	memcpy(p + 4, duid_prefix, duid_length);
-	p += 4 + duid_length;
-	random_solicit6 = p - template_solicit6;
-	/* option request option */
-	p[1] = DHCP6_OPT_ORO;
-	p[3] = 4;
-	p[5] = DHCP6_OPT_NAME_SERVERS;
-	p[7] = DHCP6_OPT_DOMAIN_SEARCH;
-	p += 8;
-	/* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
-	p[1] = DHCP6_OPT_IA_NA;
-	p[3] = 12;
-	p[7] = 1;
-	p[10] = 3600 >> 8;
-	p[11] = 3600 & 0xff;
-	p[14] = 5400 >> 8;
-	p[15] = 5400 & 0xff;
-	p += 16;
-	/* set length */
-	length_solicit6 = p - template_solicit6;
-}
-
-/*
- * get a DHCPv6 first packet (usually a SOLICIT) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_solicit6(void)
-{
-	uint8_t *p = template_solicit6;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[0], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(1);
-	}
-	if (cc < 10) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[0]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[0]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[0]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[0]);
-		exit(2);
-	}
-	length_solicit6 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[0] >= 0)
-		xid_solicit6 = (size_t) xidoffset[0];
-	else
-		xid_solicit6 = DHCP6_OFF_XID;
-	if (xid_solicit6 + 3 > length_solicit6) {
-		fprintf(stderr,
-			"xid (at %zu) is outside the template (length %zu)?\n",
-			xid_solicit6, length_solicit6);
-		exit(2);
-	}
-	if (rndoffset[0] >= 0)
-		random_solicit6 = (size_t) rndoffset[0];
-	else
-		random_solicit6 = 0;
-	if (random_solicit6 > length_solicit6) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_solicit6, length_solicit6);
-		exit(2);
-	}
-}
-
-/*
- * build a DHCPv6 REQUEST template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_request6(void)
-{
-	uint8_t *p = template_request6;
-
-	xid_request6 = DHCP6_OFF_XID;
-	/* message type */
-	p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
-	/* options */
-	p += DHCP6_OFF_OPTIONS;
-	/* elapsed time */
-	p[1] = DHCP6_OPT_ELAPSED_TIME;
-	p[3] = 2;
-	p += 4;
-	elapsed_request6 = p - template_request6;
-	p += 2;
-	/* client ID */
-	p[1] = DHCP6_OPT_CLIENTID;
-	p[3] = duid_length;
-	memcpy(p + 4, duid_prefix, duid_length);
-	p += 4 + duid_length;
-	random_request6 = p - template_request6;
-	/* option request option */
-	p[1] = DHCP6_OPT_ORO;
-	p[3] = 4;
-	p[5] = DHCP6_OPT_NAME_SERVERS;
-	p[7] = DHCP6_OPT_DOMAIN_SEARCH;
-	p += 8;
-	/* server ID and IA_NA */
-	serverid_request6 = p - template_request6;
-	reqaddr_request6 = p - template_request6;
-	/* set length */
-	length_request6 = p - template_request6;
-}
-
-/*
- * get a DHCPv6 third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request6(void)
-{
-	uint8_t *p = template_request6;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[1], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(1);
-	}
-	if (cc < 10) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[1]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[1]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[1]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[1]);
-		exit(2);
-	}
-	length_request6 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[1] >= 0)
-		xid_request6 = (size_t) xidoffset[1];
-	else
-		xid_request6 = DHCP6_OFF_XID;
-	if (xid_request6 + 3 > length_request6) {
-		fprintf(stderr,
-			"xid (at %zu) is outside the template (length %zu)?\n",
-			xid_request6, length_request6);
-		exit(2);
-	}
-	if (rndoffset[1] >= 0)
-		random_request6 = (size_t) rndoffset[1];
-	else
-		random_request6 = 0;
-	if (random_request6 > length_request6) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_request6, length_request6);
-		exit(2);
-	}
-	if (elpoffset >= 0)
-		elapsed_request6 = (size_t) elpoffset;
-	if (elapsed_request6 + 2 > length_request6) {
-		fprintf(stderr,
-			"secs (at %zu) outside the template (length %zu)?\n",
-			elapsed_request6, length_request6);
-		exit(2);
-	}
-	serverid_request6 = (size_t) sidoffset;
-	if (serverid_request6 > length_request6) {
-		fprintf(stderr,
-			"server-id option (at %zu) outside the template "
-			"(length %zu)?\n",
-			serverid_request6, length_request6);
-		exit(2);
-	}
-	reqaddr_request6 = (size_t) ripoffset;
-	if (reqaddr_request6 > length_request6) {
-		fprintf(stderr,
-			"requested-ip-address option (at %zu) outside "
-			"the template (length %zu)?\n",
-			reqaddr_request6, length_request6);
-		exit(2);
-	}
-}
-
-/*
- * send the DHCPv6 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the ADVERTISE)
- */
-
-void
-send_request6(struct exchange *x0)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	size_t len;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL) {
-		locallimit++;
-		perror("send2");
-		return;
-	}
-
-	memcpy(x, x0, sizeof(*x));
-	x->order2 = xscount2++;
-	x->xid |= 1;
-	hash = x->xid >> 1;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
-	hash &= hashsize2 - 1;
-	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	len = length_request6;
-	memcpy(obuf, template_request6, len);
-	/* xid */
-	obuf[xid_request6] = x->xid >> 16;
-	obuf[xid_request6 + 1] = x->xid >> 8;
-	obuf[xid_request6 + 2] = x->xid;
-	/* random */
-	randomize(random_request6, x->rnd);
-	/* elapsed time */
-	if (elapsed_request6 > 0) {
-		int et;
-
-		et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
-		et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
-		if (et > 65535) {
-			obuf[elapsed_request6] = 0xff;
-			obuf[elapsed_request6 + 1] = 0xff;
-		} else if (et > 0) {
-			obuf[elapsed_request6] = et >> 8;
-			obuf[elapsed_request6 + 1] = et & 0xff;
-		}
-	}
-	/* server ID */
-	if (serverid_request6 < length_request6)
-		memmove(obuf + serverid_request6 + x->sidlen,
-			obuf + serverid_request6,
-			x->sidlen);
-	memcpy(obuf + serverid_request6, x->sid, x->sidlen);
-	len += x->sidlen;
-	/* IA_NA */
-	if (reqaddr_request6 < serverid_request6) {
-		memmove(obuf + reqaddr_request6 + x->ianalen,
-			obuf + reqaddr_request6,
-			x->ianalen);
-		memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
-	} else if (reqaddr_request6 < length_request6) {
-		memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
-			obuf + reqaddr_request6 + x->sidlen,
-			x->ianalen);
-		memcpy(obuf + reqaddr_request6+ x->sidlen,
-		       x->iana,
-		       x->ianalen);
-	} else
-		memcpy(obuf + len, x->iana, x->ianalen);
-	len += x->ianalen;
-
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
-	if (ret < 0) {
-		perror("clock_gettime(send2)");
-		fatal = 1;
-		return;
-	}
-	ret = sendto(sock, obuf, len, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in6));
-	if (ret >= 0)
-		return;
-	if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
-	    (errno == ENOBUFS) || (errno == ENOMEM))
-		locallimit++;
-	perror("send2");
-}
-
-/*
- * send the DHCPv6 SOLICIT first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send6(void)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL)
-		return -ENOMEM;
-
-	memset(x, 0, sizeof(*x));
-	x->order0 = xscount0++;
-	hash = x->rnd = (uint32_t) random();
-	if (simple == 0)
-		x->xid = (hash << 1) & 0x00ffffff;
-	else
-		x->xid = hash & 0x00ffffff;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
-	hash &= hashsize0 - 1;
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	memcpy(obuf, template_solicit6, length_solicit6);
-	/* xid */
-	obuf[xid_solicit6] = x->xid >> 16;
-	obuf[xid_solicit6 + 1] = x->xid >> 8;
-	obuf[xid_solicit6 + 2] = x->xid;
-	/* random */
-	x->rnd = randomize(random_solicit6, x->rnd);
-
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &last);
-	if (ret < 0) {
-		perror("clock_gettime(send)");
-		fatal = 1;
-		return -errno;
-	}
-	x->ts0 = last;
-	errno = 0;
-	ret = sendto(sock, obuf, length_solicit6, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in6));
-	if (ret == (ssize_t) length_solicit6)
-		return 0;
-	return -errno;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its server-id option
- */
-
-int
-scan_for_srvid6(struct exchange *x, size_t cc)
-{
-	size_t off = DHCP6_OFF_OPTIONS, len;
-
-	for (;;) {
-		if (off + 4 > cc) {
-			fprintf(stderr, "server-id not found\n");
-			return -1;
-		}
-		if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
-			break;
-		off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	}
-	len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	/* cache it in the global variables when required and not yet done */
-	if ((usefirst != 0) && (gsrvid == NULL)) {
-		if (len <= sizeof(gsrvidbuf)) {
-			memcpy(gsrvidbuf, ibuf + off, len);
-			gsrvid = gsrvidbuf;
-			gsrvidlen = len;
-		} else {
-			gsrvid = (uint8_t *) malloc(len);
-			if (gsrvid == NULL) {
-				perror("malloc(gsrvid");
-				return -1;
-			}
-			memcpy(gsrvid, ibuf + off, len);
-			gsrvidlen = len;
-		}
-	}
-	x->sid = ibuf + off;
-	x->sidlen = len;
-	return 0;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its IA_NA option
- * (TODO: check for errors)
- */
-
-int
-scan_for_ia_na(struct exchange *x, size_t cc)
-{
-	size_t off = DHCP6_OFF_OPTIONS, len;
-
-	for (;;) {
-		if (off + 4 > cc) {
-			fprintf(stderr, "ia-na not found\n");
-			return -1;
-		}
-		if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
-			break;
-		off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	}
-	len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	x->iana = ibuf + off;
-	x->ianalen = len;
-	return 0;
-}
-
-/*
- * receive a DHCPv6 packet
- */
-
-void
-receive6(void)
-{
-	struct exchange *x, *t;
-	struct xlist *bucket;
-	struct timespec now;
-	ssize_t cc;
-	uint32_t xid, hash;
-	int checklost = 0;
-	double delta;
-
-	cc = recv(sock, ibuf, sizeof(ibuf), 0);
-	if (cc < 0) {
-		if ((errno == EAGAIN) ||
-		    (errno == EWOULDBLOCK) ||
-		    (errno == EINTR))
-			return;
-		perror("recv");
-		fatal = 1;
-		return;
-	}
-	/* enforce a reasonable length */
-	if (cc < 22) {
-		tooshort++;
-		return;
-	}
-	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-		perror("clock_gettime(receive)");
-		fatal = 1;
-		return;
-	}
-	xid = ibuf[xid_solicit6] << 16;
-	xid |= ibuf[xid_solicit6 + 1] << 8;
-	xid |= ibuf[xid_solicit6 + 2];
-	/* 4-packet exchange even/odd xid */
-	if (simple == 0) {
-		if ((xid & 1) != 0) {
-			receive_reply(xid, &now);
-			return;
-		}
-		hash = (xid >> 1) & (hashsize0 - 1);
-	} else
-		hash = xid & (hashsize0 - 1);
-	/* now it is the second packet, get the bucket which is needed */
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	/* try the 'next to be received' cache */
-	if ((xnext0 != NULL) && (xnext0->xid == xid)) {
-		x = xnext0;
-		goto found;
-	}
-	/* if the rate is not illimited, garbage collect up to 3
-	   timed-out exchanges */
-	if (rate != 0)
-		checklost = 3;
-	/* look for the exchange */
-	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
-		double waited;
-
-		if (x->xid == xid)
-			goto found;
-		if (checklost <= 0)
-			continue;
-		/* check for a timed-out exchange */
-		waited = now.tv_sec - x->ts0.tv_sec;
-		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
-		if (waited < losttime[0]) {
-			checklost = 0;
-			continue;
-		}
-		/* garbage collect timed-out exchange */
-		checklost--;
-		ISC_TAILQ_REMOVE(bucket, x, hchain);
-		ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-		free(x);
-		collected[0] += 1;
-	}
-	/* no match? very late or not for us */
-	orphans++;
-	return;
-
-	/* got it: update stats and move to the received queue */
-    found:
-	xrcount0++;
-	x->ts1 = now;
-	delta = x->ts1.tv_sec - x->ts0.tv_sec;
-	delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
-	if (delta < dmin0)
-		dmin0 = delta;
-	if (delta > dmax0)
-		dmax0 = delta;
-	dsum0 += delta;
-	dsumsq0 += delta * delta;
-	xnext0 = ISC_TAILQ_NEXT(x, gchain);
-	ISC_TAILQ_REMOVE(bucket, x, hchain);
-	ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-	ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
-	/* if the exchange is not finished, go to the second part */
-	if (simple == 0) {
-		int ret = 0;
-
-		/* the server-ID option is needed */
-		if ((usefirst != 0) && (gsrvid != NULL)) {
-			x->sid = gsrvid;
-			x->sidlen = gsrvidlen;
-		} else
-			ret = scan_for_srvid6(x, cc);
-		/* and the IA_NA option too */
-		if (ret >= 0)
-			ret = scan_for_ia_na(x, cc);
-		if (ret >= 0)
-			send_request6(x);
-	}
-}
-
-/*
- * decode a base command line parameter
- * (currently only MAC address and DUID are supported)
- */
-
-void
-decodebase(void)
-{
-	char *b0 = base[basecnt];
-
-	/* MAC address (alias Ethernet address) */
-	if ((strncasecmp(b0, "mac=", 4) == 0) ||
-	    (strncasecmp(b0, "ether=", 6) == 0)) {
-		char *b;
-
-		b = strchr(b0, '=') + 1;
-		if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
-			   &mac_prefix[0], &mac_prefix[1],
-			   &mac_prefix[2], &mac_prefix[3],
-			   &mac_prefix[4], &mac_prefix[5]) == 6) {
-			if ((diags != NULL) && (strchr(diags, 'a') != NULL))
-				printf("set MAC to %s\n", b);
-			return;
-		}
-		fprintf(stderr,
-			"expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
-			(int) (b - b0 - 1), b0);
-			exit(2);
-	}
-	/* DUID */
-	if (strncasecmp(b0, "duid=", 5) == 0) {
-		char *b;
-		size_t i, l;
-
-		if (duid_prefix != NULL) {
-			fprintf(stderr, "duid was already set?\n");
-			exit(2);
-		}
-		b = strchr(b0, '=') + 1;
-		l = 0;
-		while (*b != '\0') {
-			if (!isxdigit((int) *b)) {
-				fprintf(stderr,
-					"illegal char '%c' in duid\n",
-					(int) *b);
-				exit(2);
-			}
-			b++;
-			l++;
-		}
-		if ((l & 1) != 0) {
-			fprintf(stderr,
-				"odd number of hexadecimal digits in duid\n");
-			exit(2);
-		}
-		l /= 2;
-		if (l > 64) {
-			fprintf(stderr, "duid too large\n");
-			exit(2);
-		}
-		duid_prefix = (uint8_t *) malloc(l);
-		if (duid_prefix == NULL) {
-			perror("malloc(duid)");
-			exit(1);
-		}
-		b = strchr(b0, '=') + 1;
-		for (i = 0; i < l; i++, b += 2)
-			(void) sscanf(b, "%02hhx", &duid_prefix[i]);
-		duid_length = l;
-		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-			b = strchr(b0, '=') + 1;
-			printf("set DUID[%d] to %s\n",
-			       duid_length, b);
-		}
-		return;
-	}
-	/* other */
-	fprintf(stderr, "not yet supported base '%s'\n", b0);
-	exit(2);
-}
-			   
-/*
- * get the server socket address from the command line:
- *  - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
- */
-
-void
-getserveraddr(const int flags)
-{
-	struct addrinfo hints, *res;
-	char *service;
-	int ret;
-
-	memset(&hints, 0, sizeof(hints));
-	if (ipversion == 4) {
-		hints.ai_family = AF_INET;
-		service = "67";
-	} else {
-		hints.ai_family = AF_INET6;
-		service = "547";
-	}
-	hints.ai_socktype = SOCK_DGRAM;
-
-	hints.ai_flags = AI_NUMERICSERV | flags;
-#if defined(AI_ADDRCONFIG)
-	hints.ai_flags |= AI_ADDRCONFIG;
-#endif
-	hints.ai_protocol = IPPROTO_UDP;
-	
-	ret = getaddrinfo(servername, service, &hints, &res);
-	if (ret != 0) {
-		fprintf(stderr, "bad server=%s: %s\n",
-			servername, gai_strerror(ret));
-		exit(2);
-	}
-	if (res->ai_next != NULL) {
-		fprintf(stderr, "ambiguous server=%s\n", servername);
-		exit(2);
-	}
-	memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
-	freeaddrinfo(res);
-}
-
-/*
- * get the interface from the command line:
- *   - name of the interface
- *   - socket address to fill
- * (in IPv6, get the first link-local address)
- */
-
-void
-getinterface(const char *name, struct sockaddr_storage *ss)
-{
-	struct ifaddrs *ifaddr, *ifa;
-	int ret;
-	size_t len;
-
-	ret = getifaddrs(&ifaddr);
-	if (ret < 0) {
-		perror("getifaddrs");
-		exit(1);
-	}
-
-	for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
-		if (ifa->ifa_name == NULL)
-			continue;
-		if (strcmp(ifa->ifa_name, name) != 0)
-			continue;
-		if ((ifa->ifa_flags & IFF_UP) == 0)
-			continue;
-		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
-			continue;
-		if (ifa->ifa_addr == NULL)
-			continue;
-		if ((ipversion == 4) &&
-		    (ifa->ifa_addr->sa_family != AF_INET))
-			continue;
-		if (ipversion == 6) {
-			struct sockaddr_in6 *a6;
-
-			a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
-			if (a6->sin6_family != AF_INET6)
-				continue;
-			if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
-				continue;
-		}
-		if (ipversion == 4)
-			len = sizeof(struct sockaddr_in);
-		else
-			len = sizeof(struct sockaddr_in6);
-		memcpy(ss, ifa->ifa_addr, len);
-		/* fill the server port */
-		if (ipversion == 4)
-			((struct sockaddr_in *) ss)->sin_port = htons(67);
-		else
-			((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
-		return;
-	}
-	fprintf(stderr, "can't find interface %s\n", name);
-	exit(1);
-}
-
-/*
- * get the local socket address from the command line
- * (if it doesn't work, try an interface name)
- */
-
-void
-getlocaladdr(void)
-{
-	struct addrinfo hints, *res;
-	char *service;
-	int ret;
-
-	memset(&hints, 0, sizeof(hints));
-	if (ipversion == 4) {
-		hints.ai_family = AF_INET;
-		service = "67";
-	} else {
-		hints.ai_family = AF_INET6;
-		service = "546";
-	}
-	hints.ai_socktype = SOCK_DGRAM;
-	hints.ai_flags =  AI_NUMERICSERV;
-#if defined(AI_ADDRCONFIG)
-	hints.ai_flags |= AI_ADDRCONFIG;
-#endif
-	hints.ai_protocol = IPPROTO_UDP;
-	
-	ret = getaddrinfo(localname, service, &hints, &res);
-	if ((ret == EAI_NONAME)
-#ifdef EAI_NODATA
-	    || (ret == EAI_NODATA)
-#endif
-	   ) {
-		isinterface = 1;
-		getinterface(localname, &localaddr);
-		return;
-	}
-	if (ret != 0) {
-		fprintf(stderr,
-			"bad -l<local-addr=%s>: %s\n",
-			localname,
-			gai_strerror(ret));
-		exit(2);
-	}
-	/* refuse multiple addresses */
-	if (res->ai_next != NULL) {
-		fprintf(stderr,
-			"ambiguous -l<local-addr=%s>\n",
-			localname);
-		exit(2);
-	}
-	memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
-	freeaddrinfo(res);
-	return;
-}
-
-/*
- * get the local socket address from the server one
- */
-
-void
-getlocal(void)
-{
-	int ret, s;
-	socklen_t l;
-
-	if (ipversion == 4) {
-		l = sizeof(struct sockaddr_in);
-		s = socket(PF_INET, SOCK_DGRAM, 0);
-	} else {
-		l = sizeof(struct sockaddr_in6);
-		s = socket(PF_INET6, SOCK_DGRAM, 0);
-	}
-	if (s < 0) {
-		perror("socket");
-		exit(1);
-	}
-	if ((ipversion == 4) && (isbroadcast != 0)) {
-		int on = 1;
-
-		ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
-		if (ret < 0) {
-			perror("setsockopt(SO_BROADCAST)");
-			exit(1);
-		}
-	}
-	ret = connect(s, (struct sockaddr *) &serveraddr, l);
-	if (ret < 0) {
-		perror("connect");
-		exit(1);
-	}
-	l = sizeof(localaddr);
-	ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
-	if (ret < 0) {
-		perror("getsockname");
-		exit(1);
-	}
-	(void) close(s);
-	/* fill the local port */
-	if (ipversion == 4)
-		((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
-	else
-		((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
-}
-
-/*
- * intermediate reporting
- * (note: an in-transit packet can be reported as dropped)
- */
-
-void
-reporting(void)
-{
-	dreport.tv_sec += report;
-
-	if (xscount2 == 0) {
-		printf("sent: %llu, received: %llu (drops: %lld)",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xrcount0,
-		       (long long) (xscount0 - xrcount0));
-		if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
-			double avg;
-
-			avg = dsum0 / xrcount0;
-			printf(" average: %.3f ms", avg * 1e3);
-		}
-	} else {
-		printf("sent: %llu/%llu received: %llu/%llu "
-		       "(drops: %lld/%lld)",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xscount2,
-		       (unsigned long long) xrcount0,
-		       (unsigned long long) xrcount2,
-		       (long long) (xscount0 - xrcount0),
-		       (long long) (xscount2 - xrcount2));
-		if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
-			double avg0, avg2;
-
-			avg0 = dsum0 / xrcount0;
-			if (xrcount2 != 0)
-				avg2 = dsum2 / xrcount2;
-			else
-				avg2 = 0.;
-			printf(" average: %.3f/%.3f ms",
-			       avg0 * 1e3, avg2 * 1e3);
-		}
-	}
-	printf("\n");
-}
-
-/*
- * SIGCHLD handler
- */
-
-void
-reapchild(int sig)
-{
-	int status;
-
-	sig = sig;
-	while (wait3(&status, WNOHANG, NULL) > 0)
-		/* continue */;
-}
-
-/*
- * SIGINT handler
- */
-
-void
-interrupt(int sig)
-{
-	sig = sig;
-	interrupted = 1;
-}
-
-/*
- * '-v' handler
- */
-
-void
-version(void)
-{
-	fprintf(stderr, "version 0.01\n");
-}
-
-/*
- * usage (from the wiki)
- */
-
-void
-usage(void)
-{
-	fprintf(stderr, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-"    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-"    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-"    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-"    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-"    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-"    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\f\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact.  For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-"    control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
-"    clients.  This can be specified multiple times, each instance is\n"
-"    in the <type>=<value> form, for instance:\n"
-"    (and default) MAC=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-"    having been lost.  The value is given in seconds and may contain a\n"
-"    fractional component.  The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-"    elapsed-time option in the (second/request) template.\n"
-"    The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-"    whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-"    option / (DHCPv6) IA_NA option in the (second/request) template.\n"
-"-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
-"    hostname/address to use when communicating with the server.  By\n"
-"    default, the interface address through which traffic would\n"
-"    normally be routed to the server is used.\n"
-"    For DHCPv6 operation, specify the name of the network interface\n"
-"    via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-"    (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-"    exchanges per second.  A periodic report is generated showing the\n"
-"    number of exchanges which were not completed, as well as the\n"
-"    average response latency.  The program continues until\n"
-"    interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-"    (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-"    (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-"    as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-"    the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-"    <diagnostic-selector> is a string of single-keywords specifying\n"
-"    the operations for which verbose output is desired.  The selector\n"
-"    keyletters are:\n"
-"   * 'a': print the decoded command line arguments\n"
-"   * 'e': print the exit reason\n"
-"   * 'i': print rate processing details\n"
-"   * 'r': print randomization details\n"
-"   * 's': print first server-id\n"
-"   * 't': when finished, print timers of all successful exchanges\n"
-"   * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-"    been dropped.  Use -D0 to abort if even a single request has been\n"
-"    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
-"    maximum percentage of requests that may be dropped before abort.\n"
-"    In this case, testing of the threshold begins after 10 requests\n"
-"    have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions.  No report is\n"
-"    generated until all transactions have been initiated/waited-for,\n"
-"    after which a report is generated and the program terminates.\n"
-"-p<test-period>: Send requests for the given test period, which is\n"
-"    specified in the same manner as -d.  This can be used as an\n"
-"    alternative to -n, or both options can be given, in which case the\n"
-"    testing is completed when either limit is reached.\n"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-"   (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-"  exchanges are not successfully completed.\n");
-}
-
-/*
- * main function / entry point
- */
-
-int
-main(const int argc, char * const argv[])
-{
-	int opt, flags = 0, ret, i;
-	long long r;
-	char *pc;
-	extern char *optarg;
-	extern int optind;
-
-#define OPTIONS	"hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
-
-	/* decode options */
-	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
-	switch (opt) {
-	case 'h':
-		usage();
-		exit(0);
-
-	case 'v':
-		version();
-		exit(0);
-
-	case '4':
-		if (ipversion == 6) {
-			fprintf(stderr, "IP version already set to 6\n");
-			usage();
-			exit(2);
-		}
-		ipversion = 4;
-		break;
-
-	case '6':
-		if (ipversion == 4) {
-			fprintf(stderr, "IP version already set to 4\n");
-			usage();
-			exit(2);
-		}
-		ipversion = 6;
-		break;
-
-	case 'r':
-		rate = atoi(optarg);
-		if (rate <= 0) {
-			fprintf(stderr, "rate must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 't':
-		report = atoi(optarg);
-		if (report <= 0) {
-			fprintf(stderr, "report must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'R':
-		r = atoll(optarg);
-		if (r < 0) {
-			fprintf(stderr,
-				"range must not be a negative integer\n");
-			usage();
-			exit(2);
-		}
-		range = (uint32_t) r;
-		if ((range != 0) && (range != UINT32_MAX)) {
-			uint32_t s = range + 1;
-			uint64_t b = UINT32_MAX + 1, m;
-
-			m = (b / s) * s;
-			if (m == b)
-				maxrandom = 0;
-			else
-				maxrandom = (uint32_t) m;
-		}
-		break;
-
-	case 'b':
-		if (basecnt > 3) {
-			fprintf(stderr, "too many bases\n");
-			usage();
-			exit(2);
-		}
-		base[basecnt] = optarg;
-		decodebase();
-		basecnt++;
-		break;
-
-	case 'n':
-		numreq[gotnumreq] = atoi(optarg);
-		if (numreq[gotnumreq] <= 0) {
-			fprintf(stderr,
-				"num-request must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		gotnumreq = 1;
-		break;
-
-	case 'p':
-		period = atoi(optarg);
-		if (period <= 0) {
-			fprintf(stderr,
-				"test-period must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'd':
-		losttime[gotlosttime] = atof(optarg);
-		if (losttime[gotlosttime] <= 0.) {
-			fprintf(stderr,
-				"drop-time must be a positive number\n");
-			usage();
-			exit(2);
-		}
-		gotlosttime = 1;
-		break;
-
-	case 'D':
-		pc = strchr(optarg, '%');
-		if (pc != NULL) {
-			*pc = '\0';
-			maxpdrop[gotmaxdrop] = atof(optarg);
-			if ((maxpdrop[gotmaxdrop] <= 0) ||
-			    (maxpdrop[gotmaxdrop] >= 100)) {
-				fprintf(stderr,
-					"invalid drop-time percentage\n");
-				usage();
-				exit(2);
-			}
-			gotmaxdrop = 1;
-			break;
-		}
-		maxdrop[gotmaxdrop] = atoi(optarg);
-		if (maxdrop[gotmaxdrop] <= 0) {
-			fprintf(stderr,
-				"max-drop must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		gotmaxdrop = 1;
-		break;
-
-	case 'l':
-		localname = optarg;
-		break;
-
-	case 'P':
-		preload = atoi(optarg);
-		if (preload < 0) {
-			fprintf(stderr,
-				"preload must not be a negative integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'a':
-		aggressivity = atoi(optarg);
-		if (aggressivity <= 0) {
-			fprintf(stderr,
-				"aggressivity must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'L':
-		localport = atoi(optarg);
-		if (localport < 0) {
-			fprintf(stderr,
-				"local-port must not be a negative integer\n");
-			usage();
-			exit(2);
-		}
-		if (localport > (int) UINT16_MAX) {
-			fprintf(stderr,
-				"local-port must be lower than %d\n",
-				(int) UINT16_MAX);
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 's':
-		seeded = 1;
-		seed = (unsigned int) atol(optarg);
-		break;
-
-	case 'i':
-		simple = 1;
-		break;
-
-	case 'B':
-		isbroadcast = 1;
-		break;
-
-	case 'c':
-		rapidcommit = 1;
-		break;
-
-	case '1':
-		usefirst = 1;
-		break;
-
-	case 'T':
-		if (templatefile[0] != NULL) {
-			if (templatefile[1] != NULL) {
-				fprintf(stderr,
-					"template-files are already set\n");
-				usage();
-				exit(2);
-			}
-			templatefile[1] = optarg;
-		} else
-			templatefile[0] = optarg;
-		break;
-
-	case 'X':
-		if (xidoffset[0] >= 0)
-			i = 1;
-		else
-			i = 0;
-		xidoffset[i] = atoi(optarg);
-		if (xidoffset[i] <= 0) {
-			fprintf(stderr,
-				"xid-offset must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'O':
-		if (rndoffset[0] >= 0)
-			i = 1;
-		else
-			i = 0;
-		rndoffset[i] = atoi(optarg);
-		if (rndoffset[i] < 3) {
-			fprintf(stderr,
-				"random-offset must be greater than 3\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'E':
-		elpoffset = atoi(optarg);
-		if (elpoffset < 0) {
-			fprintf(stderr,
-				"time-offset must not be a "
-				"negative integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'S':
-		sidoffset = atoi(optarg);
-		if (sidoffset <= 0) {
-			fprintf(stderr,
-				"srvid-offset must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'I':
-		ripoffset = atoi(optarg);
-		if (ripoffset <= 0) {
-			fprintf(stderr,
-				"ip-offset must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'x':
-		diags = optarg;
-		break;
-
-	case 'w':
-		wrapped = optarg;
-		break;
-
-	default:
-		usage();
-		exit(2);
-	}
-
-	/* adjust some global variables */
-	if (ipversion == 0)
-		ipversion = 4;
-	if (templatefile[1] != NULL) {
-		if (xidoffset[1] < 0)
-			xidoffset[1] = xidoffset[0];
-		if (rndoffset[1] < 0)
-			rndoffset[1] = rndoffset[0];
-	}
-
-	/* when required, print the internal view of the command line */
-	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-		printf("IPv%d", ipversion);
-		if (simple != 0) {
-			if (ipversion == 4)
-				printf(" DO only");
-			else
-				printf(" SA only");
-		}
-		if (rate != 0)
-			printf(" rate=%d", rate);
-		if (report != 0)
-			printf(" report=%d", report);
-		if (range != 0) {
-			if (strchr(diags, 'r') != NULL)
-				printf(" range=0..%d [0x%x]",
-				       range,
-				       (unsigned int) maxrandom);
-			else
-				printf(" range=0..%d", range);
-		}
-		if (basecnt != 0)
-			for (i = 0; i < basecnt; i++)
-				printf(" base[%d]='%s'", i, base[i]);
-		if (gotnumreq != 0)
-			printf(" num-request=%d,%d", numreq[0], numreq[1]);
-		if (period != 0)
-			printf(" test-period=%d", period);
-		printf(" drop-time=%g,%g", losttime[0], losttime[1]);
-		if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
-			printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
-		if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
-			printf(" max-drop=%2.2f%%,%2.2f%%",
-			       maxpdrop[0], maxpdrop[1]);
-		if (preload != 0)
-			printf(" preload=%d", preload);
-		printf(" aggressivity=%d", aggressivity);
-		if (localport != 0)
-			printf(" local-port=%d", localport);
-		if (seeded)
-			printf(" seed=%u", seed);
-		if (isbroadcast != 0)
-			printf(" broadcast");
-		if (rapidcommit != 0)
-			printf(" rapid-commit");
-		if (usefirst != 0)
-			printf(" use-first");
-		if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
-			printf(" template-file='%s'", templatefile[0]);
-		else if (templatefile[1] != NULL)
-			printf(" template-file='%s','%s'",
-			       templatefile[0], templatefile[1]);
-		if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
-			printf(" xid-offset=%d", xidoffset[0]);
-		else if (xidoffset[1] >= 0)
-			printf(" xid-offset=%d,%d",
-			       xidoffset[0], xidoffset[1]);
-		if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
-			printf(" xid-offset=%d", rndoffset[0]);
-		else if (rndoffset[1] >= 0)
-			printf(" xid-offset=%d,%d",
-			       rndoffset[0], rndoffset[1]);
-		if (elpoffset >= 0)
-			printf(" time-offset=%d", elpoffset);
-		if (sidoffset >= 0)
-			printf(" srvid-offset=%d", sidoffset);
-		if (ripoffset >= 0)
-			printf(" ip-offset=%d", ripoffset);
-		printf(" diagnotic-selectors='%s'", diags);
-		if (wrapped != NULL)
-			printf(" wrapped='%s'", wrapped);
-		printf("\n");
-	}
-
-	/* check DHCPv4 only options */
-	if ((ipversion != 4) && (isbroadcast != 0)) {
-		fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
-		usage();
-		exit(2);
-	}
-
-	/* check DHCPv6 only options */
-	if ((ipversion != 6) && (rapidcommit != 0)) {
-		fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
-		usage();
-		exit(2);
-	}
-
-	/* check 4-packet (aka not simple) mode options */
-	if ((simple != 0) && (numreq[1] != 0)) {
-		fprintf(stderr,
-			"second -n<num-request> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (losttime[1] != 1.)) {
-		fprintf(stderr,
-			"second -d<drop-time> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) &&
-	    ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
-		fprintf(stderr,
-			"second -D<max-drop> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (usefirst != 0)) {
-		fprintf(stderr,
-			"-1 is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (templatefile[1] != NULL)) {
-		fprintf(stderr,
-			"second -T<template-file> is not "
-			"compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (xidoffset[1] >= 0)) {
-		fprintf(stderr,
-			"second -X<xid-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (rndoffset[1] >= 0)) {
-		fprintf(stderr,
-			"second -O<random-offset is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (elpoffset >= 0)) {
-		fprintf(stderr,
-			"-E<time-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (sidoffset >= 0)) {
-		fprintf(stderr,
-			"-S<srvid-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (ripoffset >= 0)) {
-		fprintf(stderr,
-			"-I<ip-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-
-
-	/* check simple mode options */
-	if ((simple == 0) && (rapidcommit != 0)) {
-		fprintf(stderr, "-i must be set to use -c\n");
-		usage();
-		exit(2);
-	}
-
-	/* check rate '-r' options */
-	if ((rate == 0) && (report != 0)) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -t<report>\n");
-		usage();
-		exit(2);
-	}
-	if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -n<num-request>\n");
-		usage();
-		exit(2);
-	}
-	if ((rate == 0) && (period != 0)) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -p<test-period>\n");
-		usage();
-		exit(2);
-	}
-	if ((rate == 0) &&
-	    ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
-	     (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -D<max-drop>\n");
-		usage();
-		exit(2);
-	}
-
-	/* check (first) template file options */
-	if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
-		fprintf(stderr,
-			"-T<template-file> must be set to "
-			"use -X<xid-offset>\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
-		fprintf(stderr,
-			"-T<template-file> must be set to "
-			"use -O<random-offset>\n");
-		usage();
-		exit(2);
-	}
-
-	/* check (second) template file options */
-	if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
-		fprintf(stderr,
-			"second/request -T<template-file> must be set to "
-			"use -E<time-offset>\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
-		fprintf(stderr,
-			"second/request -T<template-file> must be set to "
-			"use -S<srvid-offset>\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
-		fprintf(stderr,
-			"second/request -T<template-file> must be set to "
-			"use -I<ip-offset>\n");
-		usage();
-		exit(2);
-	}
-
-	/* check various template file(s) and other condition(s) options */
-	if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
-		fprintf(stderr,
-			"-O<random-offset> must be set when "
-			"-T<template-file> and -R<range> are used\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] != NULL) && (sidoffset < 0)) {
-		fprintf(stderr,
-			"-S<srvid-offset> must be set when second "
-			"-T<template-file> is used\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] != NULL) && (ripoffset < 0)) {
-		fprintf(stderr,
-			"-I<ip-offset> must be set when second "
-			"-T<template-file> is used\n");
-		usage();
-		exit(2);
-	}
-
-	/* get the server argument */
-	if (optind < argc - 1) {
-		fprintf(stderr, "extra arguments?\n");
-		usage();
-		exit(2);
-	}
-	if (optind == argc - 1) {
-		servername = argv[optind];
-		/* decode special cases */
-		if ((ipversion == 4) &&
-		    (strcmp(servername, "all") == 0)) {
-			flags = AI_NUMERICHOST;
-			isbroadcast = 1;
-			servername = "255.255.255.255";
-		} else if ((ipversion == 6) &&
-			   (strcmp(servername, "all") == 0)) {
-			flags = AI_NUMERICHOST;
-			servername = "FF02::1:2";
-		} else if ((ipversion == 6) &&
-			   (strcmp(servername, "servers") == 0)) {
-			flags = AI_NUMERICHOST;
-			servername = "FF05::1:3";
-		}
-	}
-
-	/* handle the local '-l' address/interface */
-	if (localname != NULL) {
-		/* given */
-		getlocaladdr();
-		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-			if (isinterface)
-				printf("interface='%s'\n", localname);
-			else
-				printf("local-addr='%s'\n", localname);
-		}
-		/* get the not given server from it */
-		if (servername == NULL) {
-			if (isinterface && (ipversion == 4)) {
-				flags = AI_NUMERICHOST;
-				isbroadcast = 1;
-				servername = "255.255.255.255";
-			} else if (isinterface && (ipversion == 6)) {
-				flags = AI_NUMERICHOST;
-				servername = "FF02::1:2";
-			} else {
-				fprintf(stderr,
-					"without an interface "
-					"server is required\n");
-				usage();
-				exit(2);
-			}
-		}
-	} else if (servername == NULL) {
-		fprintf(stderr, "without an interface server is required\n");
-		usage();
-		exit(2);
-	}
-	/* get the server socket address */
-	getserveraddr(flags);
-	/* finish local/server socket address stuff and print it */
-	if ((diags != NULL) && (strchr(diags, 'a') != NULL))
-		printf("server='%s'\n", servername);
-	if (localname == NULL)
-		getlocal();
-	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-		char addr[NI_MAXHOST];
-
-		ret = getnameinfo((struct sockaddr *) &localaddr,
-				  sizeof(localaddr),
-				  addr,
-				  NI_MAXHOST,
-				  NULL,
-				  0,
-				  NI_NUMERICHOST);
-		if (ret != 0) {
-			fprintf(stderr,
-				"can't get the local address: %s\n",
-				gai_strerror(ret));
-			exit(1);
-		}
-		printf("local address='%s'\n", addr);
-	}
-
-	/* initialize exchange structures */
-	inits();
-
-	/* get the socket descriptor and template(s) */
-	if (ipversion == 4) {
-		getsock4();
-		if (templatefile[0] == NULL)
-			build_template_discover4();
-		else
-			get_template_discover4();
-		if (simple == 0) {
-			if (templatefile[1] == NULL)
-				build_template_request4();
-			else
-				get_template_request4();
-		}
-	} else {
-		getsock6();
-		if (duid_prefix != NULL) {
-			if (templatefile[0] == NULL)
-				build_template_solicit6();
-			else
-				get_template_solicit6();
-			if (simple == 0) {
-				if (templatefile[1] == NULL)
-					build_template_request6();
-				else
-					get_template_request6();
-			}
-		}
-	}
-	/* sanity check */
-	if ((unsigned) sock > FD_SETSIZE) {
-		fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
-		exit(1);
-	}
-	/* make the socket descriptor not blocking */
-	flags = fcntl(sock, F_GETFL, 0);
-	if (flags < 0) {
-		perror("fcntl(F_GETFL)");
-		exit(1);
-	}
-	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
-		perror("fcntl(F_SETFL)");
-		exit(1);
-	}
-
-	/* wrapped start */
-	if (wrapped != NULL) {
-		pid_t pid;
-
-		(void) signal(SIGCHLD, reapchild);
-		pid = fork();
-		if (pid < 0) {
-			perror("fork");
-			exit(1);
-		} else if (pid == 0)
-			(void) execlp(wrapped, "start", (char *) NULL);
-	}
-
-	/* boot is done! */
-	if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
-		perror("clock_gettime(boot)");
-		exit(1);
-	}
-
-	/* compute the next intermediate reporting date */
-	if (report != 0) {
-		dreport.tv_sec = boot.tv_sec + report;
-		dreport.tv_nsec = boot.tv_nsec;
-	}
-
-	/* compute the DUID (the current date is needed) */
-	if ((ipversion == 6) && (duid_prefix == NULL)) {
-		uint32_t curdate;
-
-		duid_length = 14;
-		duid_prefix = (uint8_t *) malloc(duid_length);
-		if (duid_prefix == NULL) {
-			perror("malloc(duid)");
-			exit(1);
-		}
-		duid_prefix[0] = DHCP6_DUID_LLT >> 8;
-		duid_prefix[1] = DHCP6_DUID_LLT;
-		duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
-		duid_prefix[3] = DHCP_HTYPE_ETHER;
-		curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
-		memcpy(duid_prefix + 4, &curdate, 4);
-		memcpy(duid_prefix + 8, mac_prefix, 6);
-		/* the DUID is in template(s) */
-		if (templatefile[0] == NULL)
-			build_template_solicit6();
-		else
-			get_template_solicit6();
-		if (simple == 0) {
-			if (templatefile[1] == NULL)
-				build_template_request6();
-			else
-				get_template_request6();
-		}
-	}
-		
-	/* seed the random generator */
-	if (seeded == 0)
-		seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
-	srandom(seed);
-
-	/* preload the server with at least one packet */
-	compsend = preload + 1;
-	for (i = 0; i <= preload; i++) {
-		if (ipversion == 4)
-			ret = send4();
-		else
-			ret = send6();
-		if (ret < 0) {
-			/* failure at the first packet is fatal */
-			if (i == 0) {
-				fprintf(stderr,
-					"initial send failed: %s\n",
-					strerror(-ret));
-				exit(1);
-			}
-			if ((errno == EAGAIN) ||
-			    (errno == EWOULDBLOCK) ||
-			    (errno == ENOBUFS) ||
-			    (errno == ENOMEM))
-				locallimit++;
-			fprintf(stderr, "preload send: %s\n", strerror(-ret));
-			break;
-		}
-	}
-
-	/* required only before the interrupted flag check */
-	(void) signal(SIGINT, interrupt);
-
-	/* main loop */
-	for (;;) {
-		struct timespec now, ts;
-		fd_set rfds;
-
-		/* immediate loop exit conditions */
-		if (interrupted) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("interrupted\n");
-			break;
-		}
-		if (fatal) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("got a fatal error\n");
-			break;
-		}
-
-		/* get the date and use it */
-		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-			perror("clock_gettime(now)");
-			fatal = 1;
-			continue;
-		}
-		if ((period != 0) &&
-		    ((boot.tv_sec + period < now.tv_sec) ||
-		     ((boot.tv_sec + period == now.tv_sec) &&
-		      (boot.tv_nsec < now.tv_nsec)))) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached test-period\n");
-			break;
-		}
-		if ((report != 0) &&
-		    ((dreport.tv_sec < now.tv_sec) ||
-		     ((dreport.tv_sec == now.tv_sec) &&
-		      (dreport.tv_nsec < now.tv_nsec))))
-			reporting();
-
-		/* compute the delay for the next send */
-		due = last;
-		if (rate == 1)
-			due.tv_sec += 1;
-		else if (rate != 0)
-			due.tv_nsec += 1010000000 / rate;
-		else
-			due.tv_nsec += 1;
-		while (due.tv_nsec >= 1000000000) {
-			due.tv_sec += 1;
-			due.tv_nsec -= 1000000000;
-		}
-		ts = due;
-		ts.tv_sec -= now.tv_sec;
-		ts.tv_nsec -= now.tv_nsec;
-		while (ts.tv_nsec < 0) {
-			ts.tv_sec -= 1;
-			ts.tv_nsec += 1000000000;
-		}
-		/* the send was already due? */
-		if (ts.tv_sec < 0) {
-			ts.tv_sec = ts.tv_nsec = 0;
-			latesent++;
-		}
-
-		/* pselect() */
-		FD_ZERO(&rfds);
-		FD_SET(sock, &rfds);
-		ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
-		if (ret < 0) {
-			if (errno == EINTR)
-				continue;
-			perror("pselect");
-			fatal = 1;
-			continue;
-		}
-
-		/* packet(s) to receive */
-		while (ret == 1) {
-			if (ipversion == 4)
-				receive4();
-			else
-				receive6();
-			if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
-				ret = 0;
-			else
-				multrcvd++;
-		}
-		if (fatal)
-			continue;
-
-		/* check receive loop exit conditions */
-		if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached num-request0\n");
-			break;
-		}
-		if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached num-request2\n");
-			break;
-		}
-		if ((maxdrop[0] != 0) &&
-		    ((int) (xscount0 - xrcount0) > maxdrop[0])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop%s (absolute)\n",
-				       simple != 0 ? "" : "0");
-			break;
-		}
-		if ((maxdrop[1] != 0) &&
-		    ((int) (xscount2 - xrcount2) > maxdrop[1])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop2 (absolute)\n");
-			break;
-		}
-		if ((maxpdrop[0] != 0.) &&
-		    (xscount0 > 10) &&
-		    (((100. * (xscount0 - xrcount0)) / xscount0)
-			> maxpdrop[0])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop%s (percent)\n",
-				       simple != 0 ? "" : "0");
-			break;
-		}
-		if ((maxpdrop[1] != 0.) &&
-		    (xscount2 > 10) &&
-		    (((100. * (xscount2 - xrcount2)) / xscount2)
-			> maxpdrop[1])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop2 (percent)\n");
-			break;
-		}
-
-		/* compute how many packets to send */
-		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-			perror("clock_gettime(now2)");
-			fatal = 1;
-			continue;
-		}
-		if ((now.tv_sec > due.tv_sec) ||
-		    ((now.tv_sec == due.tv_sec) &&
-		     (now.tv_nsec >= due.tv_nsec))) {
-			double tosend;
-
-			if (rate != 0) {
-				tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
-				tosend += now.tv_sec - due.tv_sec;
-				tosend *= rate;
-				tosend += 1;
-				if (tosend > (double) aggressivity)
-					i = aggressivity;
-				else
-					i = (int) tosend;
-			} else
-				i = aggressivity;
-			compsend += i;
-			/* send packets */
-			for (;;) {
-				if (ipversion == 4)
-					ret = send4();
-				else
-					ret = send6();
-				if (ret < 0) {
-					if ((errno == EAGAIN) ||
-					    (errno == EWOULDBLOCK) ||
-					    (errno == ENOBUFS) ||
-					    (errno == ENOMEM))
-						locallimit++;
-					fprintf(stderr,
-						"send: %s\n", strerror(-ret));
-					break;
-				}
-				i--;
-				if (i == 0)
-					break;
-				/* check for late packets to receive */
-				if (recv(sock, ibuf, sizeof(ibuf),
-					 MSG_PEEK) > 0) {
-					latercvd++;
-					if (ipversion == 4)
-						receive4();
-					else
-						receive6();
-				}
-			}
-		} else
-			/* there was no packet to send */
-			shortwait++;
-	}
-
-	/* after main loop: finished */
-	if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
-		perror("clock_gettime(finished)");
-
-	/* wrapped stop */
-	if (wrapped != NULL) {
-		pid_t pid;
-
-		pid = fork();
-		if (pid == 0)
-			(void) execlp(wrapped, "stop", (char *) NULL);
-	}
-
-	/* main statictics */
-	if (xscount2 == 0)
-		printf("sent: %llu, received: %llu (drops: %lld)\n",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xrcount0,
-		       (long long) (xscount0 - xrcount0));
-	else
-		printf("sent: %llu/%llu, received: %llu/%llu "
-		       "(drops: %lld/%lld)\n",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xscount2,
-		       (unsigned long long) xrcount0,
-		       (unsigned long long) xrcount2,
-		       (long long) (xscount0 - xrcount0),
-		       (long long) (xscount2 - xrcount2));
-	printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
-	       (unsigned long long) tooshort,
-	       (unsigned long long) orphans,
-	       (unsigned long long) locallimit);
-
-	/* print the rate */
-	if (finished.tv_sec != 0) {
-		double dall, erate;
-
-		dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
-		dall += finished.tv_sec - boot.tv_sec;
-		erate = xrcount0 / dall;
-		if (rate != 0)
-			printf("rate: %f (expected %d)\n", erate, rate);
-		else
-			printf("rate: %f\n", erate);
-	}
-
-	/* rate processing instrumentation */
-	if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
-		printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
-		       "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
-		       (unsigned long long) latesent,
-		       (unsigned long long) compsend,
-		       (unsigned long long) shortwait,
-		       (unsigned long long) multrcvd,
-		       (unsigned long long) latercvd,
-		       (unsigned long long) collected[0],
-		       (unsigned long long) collected[1]);
-	}
-
-	/* round-time trip statistics */
-	if (xrcount2 != 0) {
-		double avg0, avg2, stddev0, stddev2;
-		
-		avg0 = dsum0 / xrcount0;
-		avg2 = dsum2 / xrcount2;
-		stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
-		stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
-		printf("RTT0: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
-		       dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
-		printf("RTT2: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
-		       dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
-	} else if (xrcount0 != 0) {
-		double avg, stddev;
-		
-		avg = dsum0 / xrcount0;
-		stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
-		printf("RTT%s: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
-		       simple != 0 ? "" : "0",
-		       dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
-	}
-
-	/* (first) server-ID option content */
-	if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd0)) {
-		struct exchange *x;
-		size_t n;
-
-		printf("server-id: ");
-		x = ISC_TAILQ_FIRST(&xrcvd0);
-		if (ipversion == 4)
-			n = 2;
-		else
-			n = 4;
-		for (; n < x->sidlen; n++)
-			printf("%02hhx", x->sid[n]);
-		printf("\n");
-	}
-
-	/* all time-stamps */
-	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd0)) {
-		struct exchange *x;
-
-		printf("\n\n");
-		ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
-			printf("%ld.%09ld %ld.%09ld\n",
-			       (long) x->ts0.tv_sec, x->ts0.tv_nsec,
-			       (long) x->ts1.tv_sec, x->ts1.tv_nsec);
-
-	}
-	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd2)) {
-		struct exchange *x;
-
-		printf("--\n");
-		ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
-			printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
-			       (long) x->ts0.tv_sec, x->ts0.tv_nsec,
-			       (long) x->ts1.tv_sec, x->ts1.tv_nsec,
-			       (long) x->ts2.tv_sec, x->ts2.tv_nsec,
-			       (long) x->ts3.tv_sec, x->ts3.tv_nsec);
-
-	}
-	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd0))
-		printf("\n\n");
-
-	/* template(s) */
-	if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
-		size_t n;
-
-		if (ipversion == 4) {
-			printf("length = %zu\n", length_discover4);
-			printf("xid offset = %d\n", DHCP_OFF_XID);
-			printf("xid length = 4\n");
-			printf("random offset = %zu\n", random_discover4);
-			printf("content:\n");
-			for (n = 0; n < length_discover4; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_discover4[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			if ((n & 15) != 15)
-				printf("\n");
-			if (simple != 0)
-				goto doneT;
-			printf("--\n");
-			printf("length = %zu\n", length_request4);
-			printf("xid offset = %d\n", DHCP_OFF_XID);
-			printf("xid length = 4\n");
-			printf("random offset = %zu\n", random_request4);
-			if (elapsed_request4 > 0)
-				printf("secs offset = %zu\n",
-				       elapsed_request4);
-			printf("server-id offset = %zu\n", serverid_request4);
-			printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
-			printf("content:\n");
-			printf("requested-ip-address offset = %zu\n",
-			       reqaddr_request4);
-			printf("requested-ip-address length = %d\n", 4);
-			for (n = 0; n < length_request4; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_request4[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			printf("\n");
-		} else {
-			printf("length = %zu\n", length_solicit6);
-			printf("xid offset = %d\n", DHCP6_OFF_XID);
-			printf("xid length = 3\n");
-			printf("random offset = %zu\n", random_solicit6);
-			for (n = 0; n < length_solicit6; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_solicit6[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			if ((n & 15) != 15)
-				printf("\n");
-			if (simple != 0)
-				goto doneT;
-			printf("--\n");
-			printf("length = %zu\n", length_request6);
-			printf("xid offset = %d\n", DHCP_OFF_XID);
-			printf("xid length = 4\n");
-			printf("random offset = %zu\n", random_request6);
-			if (elapsed_request6 > 0)
-				printf("secs offset = %zu\n",
-				       elapsed_request6);
-			printf("server-id offset = %zu\n", serverid_request6);
-			printf("content:\n");
-			printf("requested-ip-address offset = %zu\n",
-			       reqaddr_request6);
-			for (n = 0; n < length_request6; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_request6[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			printf("\n");
-		}
-	}
-    doneT:
-
-	/* compute the exit code (and exit) */
-	if (fatal)
-		exit(1);
-	else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
-		exit(0);
-	else
-		exit(3);
-}
-
-#endif  /* HAVE_GETIFADDRS */
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index a8dfa8b..0c2b68c 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -73,14 +73,19 @@ public:
         /// \brief Increment operator.
         const CustomCounter& operator++() {
             ++counter_;
-            return(*this);
+            return (*this);
         }
 
         /// \brief Increment operator.
         const CustomCounter& operator++(int) {
             CustomCounter& this_counter(*this);
             operator++();
-            return(this_counter);
+            return (this_counter);
+        }
+
+        const CustomCounter& operator+=(int val) {
+            counter_ += val;
+            return (*this);
         }
 
         /// \brief Return counter value.
@@ -251,24 +256,30 @@ public:
         /// \brief Constructor
         ///
         /// \param xchg_type exchange type
+        /// \param drop_time maximum time elapsed before packet is
+        /// assumed dropped. Negative value disables it.
         /// \param archive_enabled if true packets archive mode is enabled.
         /// In this mode all packets are stored throughout the test execution.
-        ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled)
+        ExchangeStats(const ExchangeType xchg_type,
+                      const double drop_time,
+                      const bool archive_enabled)
             : xchg_type_(xchg_type),
-            sent_packets_(),
-            rcvd_packets_(),
-            archived_packets_(),
-            archive_enabled_(archive_enabled),
-            min_delay_(std::numeric_limits<double>::max()),
-            max_delay_(0.),
-            sum_delay_(0.),
-            sum_delay_squared_(0.),
-            orphans_(0),
-            unordered_lookup_size_sum_(0),
-            unordered_lookups_(0),
-            ordered_lookups_(0),
-            sent_packets_num_(0),
-            rcvd_packets_num_(0)
+              sent_packets_(),
+              rcvd_packets_(),
+              archived_packets_(),
+              archive_enabled_(archive_enabled),
+              drop_time_(drop_time),
+              min_delay_(std::numeric_limits<double>::max()),
+              max_delay_(0.),
+              sum_delay_(0.),
+              sum_delay_squared_(0.),
+              orphans_(0),
+              collected_(0),
+              unordered_lookup_size_sum_(0),
+              unordered_lookups_(0),
+              ordered_lookups_(0),
+              sent_packets_num_(0),
+              rcvd_packets_num_(0)
         {
             next_sent_ = sent_packets_.begin();
         }
@@ -370,6 +381,8 @@ public:
         /// not found
         boost::shared_ptr<T>
         matchPackets(const boost::shared_ptr<T>& rcvd_packet) {
+            using namespace boost::posix_time;
+
             if (!rcvd_packet) {
                 isc_throw(BadValue, "Received packet is null");
             }
@@ -432,6 +445,20 @@ public:
                             sent_packets_.template project<0>(it);
                         break;
                     }
+                    ptime now = microsec_clock::universal_time();
+                    ptime packet_time = (*it)->getTimestamp();
+                    time_period packet_period(packet_time, now);
+                    if (!packet_period.is_null()) {
+                        double period_fractional =
+                            packet_period.length().total_seconds() +
+                            (static_cast<double>(packet_period.length().fractional_seconds())
+                             / packet_period.length().ticks_per_second());
+                        if (drop_time_ > 0 &&
+                            (period_fractional > drop_time_)) {
+                            eraseSent(sent_packets_.template project<0>(it));
+                            ++collected_;
+                        }
+                    }
                 }
             }
 
@@ -510,6 +537,17 @@ public:
         /// \return number of orphant received packets.
         uint64_t getOrphans() const { return(orphans_); }
 
+        /// \brief Return number of garbage collected packets.
+        ///
+        /// Method returns number of garbage collected timed out
+        /// packets. Packet is assumed timed out when duration
+        /// between sending it to server and receiving server's
+        /// response is greater than value specified with -d<value>
+        /// command line argument.
+        ///
+        /// \return number of garbage collected packets.
+        uint64_t getCollectedNum() const { return(collected_); }
+
         /// \brief Return average unordered lookup set size.
         ///
         /// Method returns average unordered lookup set size.
@@ -603,9 +641,10 @@ public:
                      << "avg delay: " << getAvgDelay() * 1e3 << " ms" << endl
                      << "max delay: " << getMaxDelay() * 1e3 << " ms" << endl
                      << "std deviation: " << getStdDevDelay() * 1e3 << " ms"
-                     << endl;
+                     << endl
+                     << "collected packets: " << getCollectedNum() << endl;
             } catch (const Exception& e) {
-                cout << "Unavailable! No packets received." << endl;
+                cout << "Delay summary unavailable! No packets received." << endl;
             }
         }
 
@@ -644,7 +683,7 @@ public:
                     idx.equal_range(hashTransid(rcvd_packet));
                 for (PktListTransidHashIterator it_archived = p.first;
                      it_archived != p.second;
-                     ++it) {
+                     ++it_archived) {
                     if ((*it_archived)->getTransid() ==
                         rcvd_packet->getTransid()) {
                         boost::shared_ptr<T> sent_packet = *it_archived;
@@ -733,6 +772,10 @@ public:
         /// to keep all packets archived throughout the test.
         bool archive_enabled_;
 
+        /// Maxmimum time elapsed between sending and receiving packet
+        /// before packet is assumed dropped.
+        double drop_time_;
+
         double min_delay_;             ///< Minimum delay between sent
                                        ///< and received packets.
         double max_delay_;             ///< Maximum delay between sent
@@ -744,6 +787,8 @@ public:
 
         uint64_t orphans_;   ///< Number of orphant received packets.
 
+        uint64_t collected_; ///< Number of garbage collected packets.
+
         /// Sum of unordered lookup sets. Needed to calculate mean size of
         /// lookup set. It is desired that number of unordered lookups is
         /// minimal for performance reasons. Tracking number of lookups and
@@ -786,7 +831,6 @@ public:
     /// archive mode is enabled.
     StatsMgr(const bool archive_enabled = false) :
         exchanges_(),
-        custom_counters_(),
         archive_enabled_(archive_enabled),
         boot_time_(boost::posix_time::microsec_clock::universal_time()) {
     }
@@ -798,13 +842,18 @@ public:
     /// type.
     ///
     /// \param xchg_type exchange type.
+    /// \param drop_time maximum time elapsed before packet is
+    /// assumed dropped. Negative value disables it.
     /// \throw isc::BadValue if exchange of specified type exists.
-    void addExchangeStats(const ExchangeType xchg_type) {
+    void addExchangeStats(const ExchangeType xchg_type,
+                          const double drop_time = -1) {
         if (exchanges_.find(xchg_type) != exchanges_.end()) {
             isc_throw(BadValue, "Exchange of specified type already added.");
         }
         exchanges_[xchg_type] =
-            ExchangeStatsPtr(new ExchangeStats(xchg_type, archive_enabled_));
+            ExchangeStatsPtr(new ExchangeStats(xchg_type,
+                                               drop_time,
+                                               archive_enabled_));
     }
 
     /// \brief Add named custom uint64 counter.
@@ -824,6 +873,20 @@ public:
             CustomCounterPtr(new CustomCounter(long_name));
     }
 
+    /// \brief Check if any packet drops occured.
+    ///
+    // \return true, if packet drops occured.
+    bool droppedPackets() const {
+        for (ExchangesMapIterator it = exchanges_.begin();
+             it != exchanges_.end();
+             ++it) {
+            if (it->second->getDroppedPacketsNum() > 0) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
     /// \brief Return specified counter.
     ///
     /// Method returns specified counter.
@@ -844,11 +907,14 @@ public:
     ///
     /// Increement counter value by one.
     ///
-    /// \param counter_key key poitinh to the counter in the counters map.
+    /// \param counter_key key poiting to the counter in the counters map.
+    /// \param value value to increment counter by.
     /// \return pointer to specified counter after incrementation.
-    const CustomCounter& incrementCounter(const std::string& counter_key) {
+    const CustomCounter& incrementCounter(const std::string& counter_key,
+                                          const uint64_t value = 1) {
         CustomCounterPtr counter = getCounter(counter_key);
-        return(++(*counter));
+        *counter += value;
+        return (*counter);
     }
 
     /// \brief Adds new packet to the sent packets list.
@@ -1041,6 +1107,22 @@ public:
         return(xchg_stats->getDroppedPacketsNum());
     }
 
+    /// \brief Return number of garbage collected packets.
+    ///
+    /// Method returns number of garbage collected timed out
+    /// packets. Packet is assumed timed out when duration
+    /// between sending it to server and receiving server's
+    /// response is greater than value specified with -d<value>
+    /// command line argument.
+    ///
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return number of garbage collected packets.
+    uint64_t getCollectedNum(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return(xchg_stats->getCollectedNum());
+    }
+
+
     /// \brief Get time period since the start of test.
     ///
     /// Calculate dna return period since the test start. This
@@ -1128,7 +1210,7 @@ public:
             stream_rcvd << sep << it->second->getRcvdPacketsNum();
             stream_drops << sep << it->second->getDroppedPacketsNum();
         }
-        std::cout << "sent: " << stream_sent.str() 
+        std::cout << "sent: " << stream_sent.str()
                   << "; received: " << stream_rcvd.str()
                   << "; drops: " << stream_drops.str()
                   << std::endl;
diff --git a/tests/tools/perfdhcp/templates/Makefile.am b/tests/tools/perfdhcp/templates/Makefile.am
index 1eee19c..33930a7 100644
--- a/tests/tools/perfdhcp/templates/Makefile.am
+++ b/tests/tools/perfdhcp/templates/Makefile.am
@@ -1,8 +1,8 @@
 SUBDIRS = .
 
-# The test1.hex and test2.hex are created by the TestControl.PacketTemplates
+# The test[1-5].hex are created by the TestControl.PacketTemplates
 # unit tests and have to be removed.
-CLEANFILES = test1.hex test2.hex
+CLEANFILES = test1.hex test2.hex test3.hex test4.hex test5.hex
 
 perfdhcpdir = $(pkgdatadir)
 perfdhcp_DATA = discover-example.hex request4-example.hex
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index c154cf9..9376972 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <unistd.h>
 #include <signal.h>
+#include <sys/wait.h>
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 
@@ -378,6 +379,16 @@ TestControl::generateDuid(uint8_t& randomized) const {
     return (duid);
 }
 
+int
+TestControl::getElapsedTimeOffset() const {
+    int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
+    if (CommandOptions::instance().getElapsedTimeOffset() > 0) {
+        elp_offset = CommandOptions::instance().getElapsedTimeOffset();
+    }
+    return (elp_offset);
+}
+
 template<class T>
 uint32_t
 TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
@@ -438,6 +449,26 @@ TestControl::getNextExchangesNum() const {
     return (0);
 }
 
+int
+TestControl::getRandomOffset(const int arg_idx) const {
+    int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
+    if (CommandOptions::instance().getRandomOffset().size() > arg_idx) {
+        rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx];
+    }
+    return (rand_offset);
+}
+
+int
+TestControl::getRequestedIpOffset() const {
+    int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
+    if (CommandOptions::instance().getRequestedIpOffset() > 0) {
+        rip_offset = CommandOptions::instance().getRequestedIpOffset();
+    }
+    return (rip_offset);
+}
+
 uint64_t
 TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
     uint8_t ip_version = CommandOptions::instance().getIpVersion();
@@ -458,6 +489,16 @@ TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
             getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
 }
 
+int
+TestControl::getServerIdOffset() const {
+    int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
+    if (CommandOptions::instance().getServerIdOffset() > 0) {
+        srvid_offset = CommandOptions::instance().getServerIdOffset();
+    }
+    return (srvid_offset);
+}
+
 TestControl::TemplateBuffer
 TestControl::getTemplateBuffer(const size_t idx) const {
     if (template_buffers_.size() > idx) {
@@ -466,6 +507,24 @@ TestControl::getTemplateBuffer(const size_t idx) const {
     isc_throw(OutOfRange, "invalid buffer index");
 }
 
+int
+TestControl::getTransactionIdOffset(const int arg_idx) const {
+    int xid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
+    if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) {
+        xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx];
+    }
+    return (xid_offset);
+}
+
+void
+TestControl::handleChild(int) {
+    int status = 0;
+    while (wait3(&status, WNOHANG, NULL) > 0) {
+        // continue
+    }
+}
+
 void
 TestControl::handleInterrupt(int) {
     interrupted_ = true;
@@ -473,6 +532,8 @@ TestControl::handleInterrupt(int) {
 
 void
 TestControl::initPacketTemplates() {
+    template_packets_v4_.clear();
+    template_packets_v6_.clear();
     template_buffers_.clear();
     CommandOptions& options = CommandOptions::instance();
     std::vector<std::string> template_files = options.getTemplateFiles();
@@ -485,20 +546,28 @@ TestControl::initPacketTemplates() {
 void
 TestControl::initializeStatsMgr() {
     CommandOptions& options = CommandOptions::instance();
+    // Check if packet archive mode is required. If user
+    // requested diagnostics option -x t we have to enable
+    // it so as StatsMgr preserves all packets.
+    const bool archive_mode = testDiags('t') ? true : false;
     if (options.getIpVersion() == 4) {
         stats_mgr4_.reset();
-        stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4());
-        stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO);
+        stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4(archive_mode));
+        stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO,
+                                      options.getDropTime()[0]);
         if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
-            stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA);
+            stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
+                                          options.getDropTime()[1]);
         }
 
     } else if (options.getIpVersion() == 6) {
         stats_mgr6_.reset();
-        stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6());
-        stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA);
+        stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6(archive_mode));
+        stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA,
+                                      options.getDropTime()[0]);
         if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
-            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR);
+            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
+                                          options.getDropTime()[1]);
         }
     }
     if (testDiags('i')) {
@@ -506,12 +575,12 @@ TestControl::initializeStatsMgr() {
             stats_mgr4_->addCustomCounter("latesend", "Late sent packets");
             stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
             stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives");
-            //            stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
+            stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
         } else if (options.getIpVersion() == 6) {
             stats_mgr6_->addCustomCounter("latesend", "Late sent packets");
             stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
             stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives");
-            //            stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
+            stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
         }
     }
 }
@@ -605,6 +674,47 @@ TestControl::openSocket() const {
 }
 
 void
+TestControl::sendPackets(const TestControlSocket& socket,
+                         const uint64_t packets_num,
+                         const bool preload /* = false */) {
+    CommandOptions& options = CommandOptions::instance();
+    for (uint64_t i = packets_num; i > 0; --i) {
+        if (options.getIpVersion() == 4) {
+            // No template packets means that no -T option was specified.
+            // We have to build packets ourselfs.
+            if (template_buffers_.size() == 0) {
+                sendDiscover4(socket, preload);
+            } else {
+                // @todo add defines for packet type index that can be
+                // used to access template_buffers_.
+                sendDiscover4(socket, template_buffers_[0], preload);
+            }
+        } else {
+            // No template packets means that no -T option was specified.
+            // We have to build packets ourselfs.
+            if (template_buffers_.size() == 0) {
+                sendSolicit6(socket, preload);
+            } else {
+                // @todo add defines for packet type index that can be
+                // used to access template_buffers_.
+                sendSolicit6(socket, template_buffers_[0], preload);
+            }
+        }
+        // If we preload server we don't want to receive any packets.
+        if (!preload) {
+            uint64_t latercvd = receivePackets(socket);
+            if (testDiags('i')) {
+                if (options.getIpVersion() == 4) {
+                    stats_mgr4_->incrementCounter("latercvd", latercvd);
+                } else if (options.getIpVersion() == 6) {
+                    stats_mgr6_->incrementCounter("latercvd", latercvd);
+                }
+            }
+        }
+    }
+}
+
+void
 TestControl::printDiagnostics() const {
     CommandOptions& options = CommandOptions::instance();
     if (testDiags('a')) {
@@ -620,6 +730,75 @@ TestControl::printDiagnostics() const {
 }
 
 void
+TestControl::printTemplate(const uint8_t packet_type) const {
+    std::string hex_buf;
+    int arg_idx = 0;
+    if (CommandOptions::instance().getIpVersion() == 4) {
+        if (packet_type == DHCPREQUEST) {
+            arg_idx = 1;
+        }
+        std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
+            template_packets_v4_.find(packet_type);
+        if ((pkt_it != template_packets_v4_.end()) &&
+            pkt_it->second) {
+            const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
+            const char* out_buf_data =
+                static_cast<const char*>(out_buf.getData());
+            std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
+            hex_buf = vector2Hex(buf);
+        }
+    } else if (CommandOptions::instance().getIpVersion() == 6) {
+        if (packet_type == DHCPV6_REQUEST) {
+            arg_idx = 1;
+        }
+        std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
+            template_packets_v6_.find(packet_type);
+        if (pkt_it != template_packets_v6_.end() &&
+            pkt_it->second) {
+            const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
+            const char* out_buf_data =
+                static_cast<const char*>(out_buf.getData());
+            std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
+            hex_buf = vector2Hex(buf);
+        }
+    }
+    std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
+    std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
+    if (arg_idx > 0) {
+        std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
+        std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
+        std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
+    }
+
+    std::cout << "contents: " << std::endl;
+    int line_len = 32;
+    int i = 0;
+    while (line_len == 32) {
+        if (hex_buf.length() - i < 32) {
+            line_len = hex_buf.length() - i;
+        };
+        if (line_len > 0) {
+            std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
+                      << "   " << hex_buf.substr(i, line_len) << std::endl;
+        }
+        i += 32;
+    }
+    std::cout << std::endl;
+}
+
+void
+TestControl::printTemplates() const {
+    CommandOptions& options = CommandOptions::instance();
+    if (options.getIpVersion() == 4) {
+        printTemplate(DHCPDISCOVER);
+        printTemplate(DHCPREQUEST);
+    } else if (options.getIpVersion() == 6) {
+        printTemplate(DHCPV6_SOLICIT);
+        printTemplate(DHCPV6_REQUEST);
+    }
+}
+
+void
 TestControl::printRate() const {
     double rate = 0;
     CommandOptions& options = CommandOptions::instance();
@@ -634,8 +813,8 @@ TestControl::printRate() const {
     }
     std::cout << "***Rate statistics***" << std::endl;
     if (options.getRate() > 0) {
-        std::cout << "Rate: " << rate << ", expected rate: "
-                  << options.getRate() << std::endl << std::endl;
+        std::cout << "Rate: " << rate << " exchanges/second, expected rate: "
+                  << options.getRate() << " exchanges/second" <<  std::endl << std::endl;
     } else {
         std::cout << "Rate: " << rate << std::endl << std::endl;
     }
@@ -705,21 +884,38 @@ TestControl::readPacketTemplate(const std::string& file_name) {
     if (!temp_file.is_open()) {
         isc_throw(BadValue, "unable to open template file " << file_name);
     }
-    std::ifstream::pos_type temp_size = temp_file.tellg();
-    if (temp_size % 2 != 0) {
+    // Read template file contents.
+    std::streampos temp_size = temp_file.tellg();
+    if (temp_size == std::streampos(0)) {
         temp_file.close();
-        isc_throw(BadValue, "odd number of digits in template file");
+        isc_throw(OutOfRange, "the template file " << file_name << " is empty");
     }
     temp_file.seekg(0, ios::beg);
-    std::vector<char> hex_digits(temp_size);
-    std::vector<uint8_t> binary_stream;
-    temp_file.read(&hex_digits[0], temp_size);
+    std::vector<char> file_contents(temp_size);
+    temp_file.read(&file_contents[0], temp_size);
     temp_file.close();
-    for (int i = 0; i < hex_digits.size(); i += 2) {
-        if (!isxdigit(hex_digits[i]) || !isxdigit(hex_digits[i+1])) {
-            isc_throw(BadValue, "the '" << hex_digits[i] << hex_digits[i+1]
-                      << "' is not hexadecimal digit");
+    // Spaces are allowed so we have to strip the contents
+    // from them. In the same time we want to make sure that
+    // apart from spaces the file contains hexadecimal digits
+    // only.
+    std::vector<char> hex_digits;
+    for (int i = 0; i < file_contents.size(); ++i) {
+        if (isxdigit(file_contents[i])) {
+            hex_digits.push_back(file_contents[i]);
+        } else if (!isxdigit(file_contents[i]) &&
+                   !isspace(file_contents[i])) {
+            isc_throw(BadValue, "the '" << file_contents[i] << "' is not a"
+                      " heaxadecimal digit");
         }
+    }
+    // Expect even number of digits.
+    if (hex_digits.size() % 2 != 0) {
+        isc_throw(OutOfRange, "odd number of digits in template file");
+    } else if (hex_digits.size() == 0) {
+        isc_throw(OutOfRange, "template file " << file_name << " is empty");
+    }
+    std::vector<uint8_t> binary_stream;
+    for (int i = 0; i < hex_digits.size(); i += 2) {
         stringstream s;
         s << "0x" << hex_digits[i] << hex_digits[i+1];
         int b;
@@ -777,14 +973,20 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
     }
 }
 
-void
+uint64_t
 TestControl::receivePackets(const TestControlSocket& socket) {
     int timeout = 0;
     bool receiving = true;
     uint64_t received = 0;
     while (receiving) {
         if (CommandOptions::instance().getIpVersion() == 4) {
-            Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout);
+            Pkt4Ptr pkt4;
+            try {
+                pkt4 = IfaceMgr::instance().receive4(timeout);
+            } catch (const Exception& e) {
+                std::cerr << "Failed to receive DHCPv4 packet: "
+                          << e.what() <<  std::endl;
+            }
             if (!pkt4) {
                 receiving = false;
             } else {
@@ -796,7 +998,13 @@ TestControl::receivePackets(const TestControlSocket& socket) {
                 processReceivedPacket4(socket, pkt4);
             }
         } else if (CommandOptions::instance().getIpVersion() == 6) {
-            Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout);
+            Pkt6Ptr pkt6;
+            try {
+                pkt6 = IfaceMgr::instance().receive6(timeout);
+            } catch (const Exception& e) {
+                std::cerr << "Failed to receive DHCPv6 packet: "
+                          << e.what() << std::endl;
+            }
             if (!pkt6) {
                 receiving  = false;
             } else {
@@ -810,6 +1018,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
             }
         }
     }
+    return (received);
 }
 
 void
@@ -896,7 +1105,7 @@ TestControl::reset() {
     interrupted_ = false;
 }
 
-void
+int
 TestControl::run() {
     // Reset singleton state before test starts.
     reset();
@@ -910,14 +1119,14 @@ TestControl::run() {
         isc_throw(InvalidOperation,
                   "command options must be parsed before running a test");
     } else if (options.getIpVersion() == 4) {
-        setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator()));
+        setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator()));
     } else {
-        setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator(0x00FFFFFF)));
+        setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator(0x00FFFFFF)));
     }
 
     uint32_t clients_num = options.getClientsNum() == 0 ?
         1 : options.getClientsNum();
-    setMacAddrGenerator(NumberGeneratorPtr(new SequencialGenerator(clients_num)));
+    setMacAddrGenerator(NumberGeneratorPtr(new SequentialGenerator(clients_num)));
 
     // Diagnostics are command line options mainly.
     printDiagnostics();
@@ -941,48 +1150,20 @@ TestControl::run() {
     }
     // If user interrupts the program we will exit gracefully.
     signal(SIGINT, TestControl::handleInterrupt);
-    // Preload server with number of packets.
-    const bool do_preload = true;
-    for (int i = 0; i < options.getPreload(); ++i) {
-        if (options.getIpVersion() == 4) {
-            // No template buffer means no -T option specified.
-            // We will build packet ourselves.
-            if (template_buffers_.size() == 0) {
-                sendDiscover4(socket, do_preload);
-            } else {
-                // Pick template #0 if Discover is being sent.
-                // For Request it would be #1.
-                // @todo add defines for packet type index that can be
-                // used to access template_buffers_.
-                sendDiscover4(socket, template_buffers_[0],
-                              do_preload);
-            }
-        } else if (options.getIpVersion() == 6) {
-            // No template buffer means no -T option specified.
-            // We will build packet ourselfs.
-            if (template_buffers_.size() == 0) {
-                sendSolicit6(socket, do_preload);
-            } else {
-                // Pick template #0 if Solicit is being sent.
-                // For Request it would be #1.
-                // @todo add defines for packet type index that can be
-                // used to access template_buffers_.
-                sendSolicit6(socket, template_buffers_[0],
-                             do_preload);
-            }
-        }
+
+    // Preload server with the number of packets.
+    sendPackets(socket, options.getPreload(), true);
+
+    // Fork and run command specified with -w<wrapped-command>
+    if (!options.getWrapped().empty()) {
+        runWrapped();
     }
+
     // Initialize Statistics Manager. Release previous if any.
     initializeStatsMgr();
     for (;;) {
         // Calculate send due based on when last exchange was initiated.
         updateSendDue();
-        // If test period finished, maximum number of packet drops
-        // has been reached or test has been interrupted we have to
-        // finish the test.
-        if (checkExitConditions()) {
-            break;
-        }
         // Calculate number of packets to be sent to stay
         // catch up with rate.
         uint64_t packets_due = getNextExchangesNum();
@@ -997,30 +1178,17 @@ TestControl::run() {
         // @todo: set non-zero timeout for packets once we implement
         // microseconds timeout in IfaceMgr.
         receivePackets(socket);
-        // Send packets.
-        for (uint64_t i = packets_due; i > 0; --i) {
-            if (options.getIpVersion() == 4) {
-                // No template packets means that no -T option was specified.
-                // We have to build packets ourselfs.
-                if (template_buffers_.size() == 0) {
-                    sendDiscover4(socket);
-                } else {
-                    // @todo add defines for packet type index that can be
-                    // used to access template_buffers_.
-                    sendDiscover4(socket, template_buffers_[0]);
-                }
-            } else {
-                // No template packets means that no -T option was specified.
-                // We have to build packets ourselfs.
-                if (template_buffers_.size() == 0) {
-                    sendSolicit6(socket);
-                } else {
-                    // @todo add defines for packet type index that can be
-                    // used to access template_buffers_.
-                    sendSolicit6(socket, template_buffers_[0]);
-                }
-            }
+
+        // If test period finished, maximum number of packet drops
+        // has been reached or test has been interrupted we have to
+        // finish the test.
+        if (checkExitConditions()) {
+            break;
         }
+
+        // Initiate new DHCP packet exchanges.
+        sendPackets(socket, packets_due);
+
         // Report delay means that user requested printing number
         // of sent/received/dropped packets repeatedly.
         if (options.getReportDelay() > 0) {
@@ -1028,14 +1196,79 @@ TestControl::run() {
         }
     }
     printStats();
+
+    if (!options.getWrapped().empty()) {
+        // true means that we execute wrapped command with 'stop' argument.
+        runWrapped(true);
+    }
+
+    // Print packet timestamps
+    if (testDiags('t')) {
+        if (options.getIpVersion() == 4) {
+            stats_mgr4_->printTimestamps();
+        } else if (options.getIpVersion() == 6) {
+            stats_mgr6_->printTimestamps();
+        }
+    }
+
     // Print server id.
     if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
         std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
     }
+
     // Diagnostics flag 'e' means show exit reason.
     if (testDiags('e')) {
         std::cout << "Interrupted" << std::endl;
     }
+    // Print packet templates. Even if -T options have not been specified the
+    // dynamically build packet will be printed if at least one has been sent.
+    if (testDiags('T')) {
+        printTemplates();
+    }
+
+    int ret_code = 0;
+    // Check if any packet drops occured.
+    if (options.getIpVersion() == 4) {
+        ret_code = stats_mgr4_->droppedPackets() ? 3 : 0;
+    } else if (options.getIpVersion() == 6)  {
+        ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
+    }
+    return (ret_code);
+}
+
+void
+TestControl::runWrapped(bool do_stop /*= false */) const {
+    CommandOptions& options = CommandOptions::instance();
+    if (!options.getWrapped().empty()) {
+        pid_t pid = 0;
+        signal(SIGCHLD, handleChild);
+        pid = fork();
+        if (pid < 0) {
+            isc_throw(Unexpected, "unable to fork");
+        } else if (pid == 0) {
+            execlp(options.getWrapped().c_str(),
+                   do_stop ? "stop" : "start",
+                   NULL);
+        }
+    }
+}
+
+void
+TestControl::saveFirstPacket(const Pkt4Ptr& pkt) {
+    if (testDiags('T')) {
+        if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
+            template_packets_v4_[pkt->getType()] = pkt;
+        }
+    }
+}
+
+void
+TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
+    if (testDiags('T')) {
+        if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
+            template_packets_v6_[pkt->getType()] = pkt;
+        }
+    }
 }
 
 void
@@ -1075,6 +1308,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
         }
         stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
     }
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1084,7 +1318,6 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     // last_sent_ has to be updated for each function that initiates
     // new transaction. The packet exchange synchronization relies on this.
     last_sent_ = microsec_clock::universal_time();
-    CommandOptions& options = CommandOptions::instance();
     // Get the first argument if mulitple the same arguments specified
     // in the command line. First one refers to DISCOVER packets.
     const uint8_t arg_idx = 0;
@@ -1094,18 +1327,11 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     // Generate trasnaction id to be set for the new exchange.
     const uint32_t transid = generateTransid();
     // Get transaction id offset.
-    size_t transid_offset = DHCPV4_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
-    // Calculate randomization offset.
-    size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
+    // Get randomization offset.
     // We need to go back by HW_ETHER_LEN (MAC address length)
     // because this offset points to last octet of MAC address.
-    rand_offset -= HW_ETHER_LEN + 1;
+    size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
     // Create temporary buffer with template contents. We will
     // modify this temporary buffer but we don't want to modify
     // the original template.
@@ -1136,6 +1362,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
         stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
                                     boost::static_pointer_cast<Pkt4>(pkt4));
     }
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1149,6 +1376,8 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
                                              buf_msg_type);
     pkt4->addOption(opt_msg_type);
+    // Use first flags indicates that we want to use the server
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
@@ -1199,6 +1428,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
                   "hasn't been initialized");
     }
     stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1206,25 +1436,17 @@ TestControl::sendRequest4(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const dhcp::Pkt4Ptr& discover_pkt4,
                           const dhcp::Pkt4Ptr& offer_pkt4) {
-    CommandOptions& options = CommandOptions::instance();
     // Get the second argument if multiple the same arguments specified
     // in the command line. Second one refers to REQUEST packets.
     const uint8_t arg_idx = 1;
     // Generate new transaction id.
     const uint32_t transid = generateTransid();
     // Get transaction id offset.
-    size_t transid_offset = DHCPV4_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
     // Get the offset of MAC's last octet.
-    size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
     // We need to go back by HW_ETHER_LEN (MAC address length)
     // because this offset points to last octet of MAC address.
-    rand_offset -= HW_ETHER_LEN + 1;
+    size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
     // Create temporaru buffer from the template.
     std::vector<uint8_t> in_buf(template_buf.begin(),
                                 template_buf.end());
@@ -1244,19 +1466,15 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
 
     // Set elapsed time.
-    size_t elp_offset = 0;
-    if (options.getElapsedTimeOffset() > 0) {
-        elp_offset = options.getElapsedTimeOffset();
-    }
+    size_t elp_offset = getElapsedTimeOffset();
     uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
     pkt4->writeValueAt<uint16_t>(elp_offset,
                                  static_cast<uint16_t>(elapsed_time / 1000));
 
     // Get the actual server id offset.
-    size_t sid_offset = DHCPV4_SERVERID_OFFSET;
-    if (options.getServerIdOffset() > 0) {
-        sid_offset = options.getServerIdOffset();
-    }
+    size_t sid_offset = getServerIdOffset();
+    // Use first flags indicates that we want to use the server
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         boost::shared_ptr<LocalizedOption>
@@ -1293,10 +1511,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     }
 
     // Get the actual offset of requested ip.
-    size_t rip_offset = DHCPV4_REQUESTED_IP_OFFSET;
-    if (options.getRequestedIpOffset() > 0) {
-        rip_offset = options.getRequestedIpOffset();
-    }
+    size_t rip_offset = getRequestedIpOffset();
     // Place requested IP option at specified position (rip_offset).
     boost::shared_ptr<LocalizedOption>
         opt_requested_ip(new LocalizedOption(Option::V4,
@@ -1318,6 +1533,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     // Update packet stats.
     stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
                                 boost::static_pointer_cast<Pkt4>(pkt4));
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1337,7 +1553,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
     pkt6->addOption(opt_clientid);
 
     // Use first flags indicates that we want to use the server
-    // id captured in fisrt packet.
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
@@ -1370,40 +1586,33 @@ TestControl::sendRequest6(const TestControlSocket& socket,
                   "hasn't been initialized");
     }
     stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+    saveFirstPacket(pkt6);
 }
 
 void
 TestControl::sendRequest6(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const Pkt6Ptr& advertise_pkt6) {
-    CommandOptions& options = CommandOptions::instance();
     // Get the second argument if multiple the same arguments specified
     // in the command line. Second one refers to REQUEST packets.
     const uint8_t arg_idx = 1;
     // Generate transaction id.
     const uint32_t transid = generateTransid();
     // Get transaction id offset.
-    size_t transid_offset = DHCPV6_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
     PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
                                   transid_offset, transid));
     // Set elapsed time.
-    size_t elp_offset = DHCPV6_ELAPSED_TIME_OFFSET;
-    if (options.getElapsedTimeOffset() > 0) {
-        elp_offset = options.getElapsedTimeOffset();
-    }
+    size_t elp_offset = getElapsedTimeOffset();
     boost::shared_ptr<LocalizedOption>
         opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
                                              OptionBuffer(), elp_offset));
     pkt6->addOption(opt_elapsed_time);
 
     // Get the actual server id offset.
-    size_t sid_offset = DHCPV6_SERVERID_OFFSET;
-    if (options.getServerIdOffset() > 0) {
-        sid_offset = options.getServerIdOffset();
-    }
+    size_t sid_offset = getServerIdOffset();
+    // Use first flags indicates that we want to use the server
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         boost::shared_ptr<LocalizedOption>
@@ -1439,10 +1648,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
         isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
                   "packet");
     }
-    size_t addr_offset = DHCPV6_IA_NA_OFFSET;
-    if (options.getRequestedIpOffset() > 0) {
-        addr_offset = options.getRequestedIpOffset();
-    }
+    size_t addr_offset = getRequestedIpOffset();
     boost::shared_ptr<LocalizedOption>
         opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
     if (!opt_ia_na->valid()) {
@@ -1455,20 +1661,14 @@ TestControl::sendRequest6(const TestControlSocket& socket,
         isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
                   "packet");
     }
-    size_t srvid_offset = DHCPV6_SERVERID_OFFSET;
-    if (options.getServerIdOffset() > 0) {
-        srvid_offset = options.getServerIdOffset();
-    }
+    size_t srvid_offset = getServerIdOffset();
     boost::shared_ptr<LocalizedOption>
         opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
                                          opt_serverid_advertise->getData(),
                                          srvid_offset));
     pkt6->addOption(opt_serverid);
     // Get randomization offset.
-    size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
+    size_t rand_offset = getRandomOffset(arg_idx);
     OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
     if (!opt_clientid_advertise) {
         isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
@@ -1493,6 +1693,15 @@ TestControl::sendRequest6(const TestControlSocket& socket,
     // Update packet stats.
     stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
 
+    // When 'T' diagnostics flag is specified it means that user requested
+    // printing packet contents. It will be just one (first) packet which
+    // contents will be printed. Here we check if this packet has been already
+    // collected. If it hasn't we save this packet so as we can print its
+    // contents when test is finished.
+    if (testDiags('T') &&
+        (template_packets_v6_.find(DHCPV6_REQUEST) == template_packets_v6_.end())) {
+        template_packets_v6_[DHCPV6_REQUEST] = pkt6;
+    }
 }
 
 void
@@ -1526,6 +1735,8 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
         }
         stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
     }
+
+    saveFirstPacket(pkt6);
 }
 
 void
@@ -1533,13 +1744,9 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const bool preload /*= false*/) {
     last_sent_ = microsec_clock::universal_time();
-    CommandOptions& options = CommandOptions::instance();
     const int arg_idx = 0;
     // Get transaction id offset.
-    size_t transid_offset = DHCPV6_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
     // Generate trasnaction id to be set for the new exchange.
     const uint32_t transid = generateTransid();
     // Create packet.
@@ -1548,10 +1755,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
     if (!pkt6) {
         isc_throw(Unexpected, "failed to create SOLICIT packet");
     }
-    size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
+    size_t rand_offset = getRandomOffset(arg_idx);
     // randomized will pick number of bytes randomized so we can
     // just use part of the generated duid and substitude a few bytes
     /// in template.
@@ -1577,6 +1781,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
         // Update packet stats.
         stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
     }
+    saveFirstPacket(pkt6);
 }
 
 
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index 1b0b83c..2bd7871 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -33,15 +33,79 @@
 namespace isc {
 namespace perfdhcp {
 
+/// Default transaction id offset in the packet template.
+static const size_t DHCPV4_TRANSID_OFFSET = 4;
+/// Default offset of MAC's last octet in the packet template..
+static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
+/// Default elapsed time offset in the packet template.
+static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
+/// Default server id offset in the packet template.
+static const size_t DHCPV4_SERVERID_OFFSET = 54;
+/// Default requested ip offset in the packet template.
+static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
+/// Default DHCPV6 transaction id offset in t the packet template.
+static const size_t DHCPV6_TRANSID_OFFSET = 1;
+/// Default DHCPV6 randomization offset (last octet of DUID)
+/// in the packet template.
+static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
+/// Default DHCPV6 elapsed time offset in the packet template.
+static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
+/// Default DHCPV6 server id offset in the packet template.
+static const size_t DHCPV6_SERVERID_OFFSET = 22;
+/// Default DHCPV6 IA_NA offset in the packet template.
+static const size_t DHCPV6_IA_NA_OFFSET = 40;
+
 /// \brief Test Control class.
 ///
-/// This class is responsible for executing DHCP performance
-/// test end to end.
+/// This singleton class is used to run the performance test with
+/// with \ref TestControl::run function. This function can be executed
+/// multiple times if desired because it resets TestControl's internal
+/// state every time it is executed. Prior to running \ref TestControl::run,
+/// one must make sure to parse command line options by calling
+/// \ref CommandOptions::parse. Failing to do this will result in an exception.
+///
+/// The following major stages of the test are performed by this class:
+/// - set default transaction id and MAC address generators - the generator
+/// is an object of \ref TestControl::NumberGenerator type and it provides
+/// the custom randomization algorithms,
+/// - print command line arguments,
+/// - register option factory functions which are used to generate DHCP options
+/// being sent to a server,
+/// - create the socket for communication with a server,
+/// - read packet templates if user specified template files with '-T' command
+/// line option,
+/// - set the interrupt handler (invoked when ^C is pressed) which makes
+/// perfdhcp stop gracefully and print the test results before exiting,
+/// - executes an external command (if specified '-w' option), e.g. if user
+/// specified -w ./foo in the command line then program will execute
+/// "./foo start" at the beginning of the test and "./foo stop" when the test
+/// ends,
+/// - initialize the Statistics Manager,
+/// - executes the main loop:
+///   - calculate how many packets must be send to satisfy desired rate,
+///   - receive incoming packets from the server,
+///   - check the exit conditions - terminate the program if the exit criteria
+///   are fulfiled, e.g. reached maximum number of packet drops,
+///   - send the number of packets appropriate to satisfy the desired rate,
+///   - optionally print intermediate reports,
+/// - print statistics, e.g. achieved rate,
+/// - optionally print some diagnostics.
+///
+/// With the '-w' command line option user may specify the external application
+/// or script to be executed. This is executed twice, first when the test starts
+/// and second time when the test ends. This external script or application must
+/// accept 'start' and 'stop' arguments. The first time it is called, it is
+/// called with the argument 'start' and the second time with the argument
+/// 'stop'.
+///   
+/// The application is executed by calling fork() to fork the current perfdhcp
+/// process and then call execlp() to replace the current process image with
+/// the new one.
 ///
 /// Option factory functions are registered using
 /// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions
 /// provide a way to create options of the same type in the same way.
-///  When new option instance is needed the corresponding factory
+///  When a new option instance is needed, the corresponding factory
 /// function is called to create it. This is done by calling
 /// \ref dhcp::Option::factory with DHCP message type specified as one of
 ///  parameters. Some of the parameters passed to factory function
@@ -55,27 +119,6 @@ namespace perfdhcp {
 class TestControl : public boost::noncopyable {
 public:
 
-    /// Default transaction id offset.
-    static const size_t DHCPV4_TRANSID_OFFSET = 4;
-    /// Default offset of MAC's last octet.
-    static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
-    /// Default elapsed time offset.
-    static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
-    /// Default server id offset.
-    static const size_t DHCPV4_SERVERID_OFFSET = 54;
-    /// Default requested ip offset.
-    static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
-    /// Default DHCPV6 transaction id offset.
-    static const size_t DHCPV6_TRANSID_OFFSET = 1;
-    /// Default DHCPV6 randomization offset (last octet of DUID)
-    static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
-    /// Default DHCPV6 elapsed time offset.
-    static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
-    /// Default DHCPV6 server id offset.
-    static const size_t DHCPV6_SERVERID_OFFSET = 22;
-    /// Default DHCPV6 IA_NA offset.
-    static const size_t DHCPV6_IA_NA_OFFSET = 40;
-
     /// Statistics Manager for DHCPv4.
     typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
     /// Pointer to Statistics Manager for DHCPv4;
@@ -138,9 +181,13 @@ public:
     /// This is default numbers generator class. The member function is
     /// used to generate uint32_t values. Other generator classes should
     /// derive from this one to implement generation algorithms
-    /// (e.g. sequencial or based on random function).
+    /// (e.g. sequential or based on random function).
     class NumberGenerator {
     public:
+
+        /// \brief Destructor.
+        virtual ~NumberGenerator() { }
+
         /// \brief Generate number.
         ///
         /// \return Generate number.
@@ -150,14 +197,14 @@ public:
     /// The default generator pointer.
     typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;
 
-    /// \brief Sequencial numbers generatorc class.
-    class SequencialGenerator : public NumberGenerator {
+    /// \brief Sequential numbers generatorc class.
+    class SequentialGenerator : public NumberGenerator {
     public:
         /// \brief Constructor.
         ///
         /// \param range maximum number generated. If 0 is given then
-        /// range defaults to maximym uint32_t value.
-        SequencialGenerator(uint32_t range = 0xFFFFFFFF) :
+        /// range defaults to maximum uint32_t value.
+        SequentialGenerator(uint32_t range = 0xFFFFFFFF) :
             NumberGenerator(),
             num_(0),
             range_(range) {
@@ -166,7 +213,7 @@ public:
             }
         }
 
-        /// \brief Generate number sequencialy.
+        /// \brief Generate number sequentialy.
         ///
         /// \return generated number.
         virtual uint32_t generate() {
@@ -176,7 +223,7 @@ public:
         }
     private:
         uint32_t num_;   ///< Current number.
-        uint32_t range_; ///< Maximum number generated.
+        uint32_t range_; ///< Number of unique numbers generated.
     };
 
     /// \brief Length of the Ethernet HW address (MAC) in bytes.
@@ -199,7 +246,9 @@ public:
     ///
     /// \throw isc::InvalidOperation if command line options are not parsed.
     /// \throw isc::Unexpected if internal Test Controler error occured.
-    void run();
+    /// \return error_code, 3 if number of received packets is not equal
+    /// to number of sent packets, 0 if everything is ok.
+    int run();
 
     /// \brief Set new transaction id generator.
     ///
@@ -408,7 +457,10 @@ protected:
     /// their content and stores it in class internal buffers. Template
     /// file names are specified from the command line with -T option.
     ///
-    /// \throw isc::BadValue if any of the template files does not exist
+    /// \throw isc::BadValue if any of the template files does not exist,
+    /// contains characters other than hexadecimal digits or spaces.
+    /// \throw OutOfRange if any of the template files is empty or has
+    /// odd number of hexadecimal digits.
     void initPacketTemplates();
 
     /// \brief Initializes Statistics Manager.
@@ -492,8 +544,8 @@ protected:
     /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
     ///
     /// Method receives DHCPv4 or DHCPv6 packets from the server.
-    /// This function will call \ref receivePacket4 or
-    /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
+    /// This function will call \ref processReceivedPacket4 or
+    /// \ref processReceivedPacket6 depending if DHCPv4 or DHCPv6 packet
     /// has arrived.
     ///
     /// \warning this method does not check if provided socket is
@@ -502,7 +554,8 @@ protected:
     /// \param socket socket to be used.
     /// \throw isc::BadValue if unknown message type received.
     /// \throw isc::Unexpected if unexpected error occured.
-    void receivePackets(const TestControlSocket& socket);
+    /// \return number of received packets.
+    uint64_t receivePackets(const TestControlSocket& socket);
 
     /// \brief Register option factory functions for DHCPv4
     ///
@@ -535,6 +588,36 @@ protected:
     /// called before new test is started.
     void reset();
 
+    /// \brief Save the first DHCPv4 sent packet of the specified type.
+    ///
+    /// This method saves first packet of the specified being sent
+    /// to the server if user requested diagnostics flag 'T'. In
+    /// such case program has to print contents of selected packets
+    /// being sent to the server. It collects first packets of each
+    /// type and keeps them around until test finishes. Then they
+    /// are printed to the user. If packet of specified type has
+    /// been already stored this function perfroms no operation.
+    /// This function does not perform sanity check if packet
+    /// pointer is valid. Make sure it is before calling it.
+    ///
+    /// \param pkt packet to be stored.
+    inline void saveFirstPacket(const dhcp::Pkt4Ptr& pkt);
+
+    /// \brief Save the first DHCPv6 sent packet of the specified type.
+    ///
+    /// This method saves first packet of the specified being sent
+    /// to the server if user requested diagnostics flag 'T'. In
+    /// such case program has to print contents of selected packets
+    /// being sent to the server. It collects first packets of each
+    /// type and keeps them around until test finishes. Then they
+    /// are printed to the user. If packet of specified type has
+    /// been already stored this function perfroms no operation.
+    /// This function does not perform sainty check if packet
+    /// pointer is valid. Make sure it is before calling it.
+    ///
+    /// \param pkt packet to be stored.
+    inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt);
+
     /// \brief Send DHCPv4 DISCOVER message.
     ///
     /// Method creates and sends DHCPv4 DISCOVER message to the server
@@ -550,8 +633,10 @@ protected:
     ///
     /// \param socket socket to be used to send the message.
     /// \param preload preload mode, packets not included in statistics.
+    ///
     /// \throw isc::Unexpected if failed to create new packet instance.
     /// \throw isc::BadValue if MAC address has invalid length.
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendDiscover4(const TestControlSocket& socket,
                        const bool preload = false);
 
@@ -567,11 +652,38 @@ protected:
     /// \param socket socket to be used to send the message.
     /// \param template_buf buffer holding template packet.
     /// \param preload preload mode, packets not included in statistics.
+    ///
     /// \throw isc::OutOfRange if randomization offset is out of bounds.
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendDiscover4(const TestControlSocket& socket,
                        const std::vector<uint8_t>& template_buf,
                        const bool preload = false);
 
+    /// \brief Send number of packets to initiate new exchanges.
+    ///
+    /// Method initiates the new DHCP exchanges by sending number
+    /// of DISCOVER (DHCPv4) or SOLICIT (DHCPv6) packets. If preload
+    /// mode was requested sent packets will not be counted in
+    /// the statistics. The responses from the server will be
+    /// received and counted as orphans because corresponding sent
+    /// packets are not included in StatsMgr for match.
+    /// When preload mode is disabled and diagnostics flag 'i' is
+    /// specified then function will be trying to receive late packets
+    /// before new packets are sent to the server. Statistics of
+    /// late received packets is updated accordingly.
+    ///
+    /// \todo do not count responses in preload mode as orphans.
+    ///
+    /// \param socket socket to be used to send packets.
+    /// \param packets_num number of packets to be sent.
+    /// \param preload preload mode, packets not included in statistics.
+    /// \throw isc::Unexpected if thrown by packet sending method.
+    /// \throw isc::InvalidOperation if thrown by packet sending method.
+    /// \throw isc::OutOfRange if thrown by packet sending method.
+    void sendPackets(const TestControlSocket &socket,
+                     const uint64_t packets_num,
+                     const bool preload = false);
+
     /// \brief Send DHCPv4 REQUEST message.
     ///
     /// Method creates and sends DHCPv4 REQUEST message to the server.
@@ -581,9 +693,11 @@ protected:
     /// \param socket socket to be used to send message.
     /// \param discover_pkt4 DISCOVER packet sent.
     /// \param offer_pkt4 OFFER packet object.
+    ///
     /// \throw isc::Unexpected if unexpected error occured.
     /// \throw isc::InvalidOperation if Statistics Manager has not been
     /// initialized.
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendRequest4(const TestControlSocket& socket,
                       const dhcp::Pkt4Ptr& discover_pkt4,
                       const dhcp::Pkt4Ptr& offer_pkt4);
@@ -598,6 +712,8 @@ protected:
     /// \param template_buf buffer holding template packet.
     /// \param discover_pkt4 DISCOVER packet sent.
     /// \param offer_pkt4 OFFER packet received.
+    ///
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendRequest4(const TestControlSocket& socket,
                       const std::vector<uint8_t>& template_buf,
                       const dhcp::Pkt4Ptr& discover_pkt4,
@@ -618,6 +734,8 @@ protected:
     /// \throw isc::Unexpected if unexpected error occured.
     /// \throw isc::InvalidOperation if Statistics Manager has not been
     /// initialized.
+    ///
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendRequest6(const TestControlSocket& socket,
                       const dhcp::Pkt6Ptr& advertise_pkt6);
 
@@ -630,6 +748,8 @@ protected:
     /// \param socket socket to be used to send message.
     /// \param template_buf packet template buffer.
     /// \param advertise_pkt6 ADVERTISE packet object.
+    ///
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendRequest6(const TestControlSocket& socket,
                       const std::vector<uint8_t>& template_buf,
                       const dhcp::Pkt6Ptr& advertise_pkt6);
@@ -648,7 +768,9 @@ protected:
     ///
     /// \param socket socket to be used to send the message.
     /// \param preload mode, packets not included in statistics.
+    ///
     /// \throw isc::Unexpected if failed to create new packet instance.
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendSolicit6(const TestControlSocket& socket,
                       const bool preload = false);
 
@@ -661,6 +783,8 @@ protected:
     /// \param socket socket to be used to send the message.
     /// \param template_buf packet template buffer.
     /// \param preload mode, packets not included in statistics.
+    ///
+    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
     void sendSolicit6(const TestControlSocket& socket,
                       const std::vector<uint8_t>& template_buf,
                       const bool preload = false);
@@ -728,6 +852,34 @@ private:
     template<class T>
     uint32_t getElapsedTime(const T& pkt1, const T& pkt2);
 
+    /// \brief Return elapsed time offset in a packet.
+    ///
+    /// \return elapsed time offset in packet.
+    int getElapsedTimeOffset() const;
+
+    /// \brief Return randomization offset in a packet.
+    ///
+    /// \return randomization offset in packet.
+    int getRandomOffset(const int arg_idx) const;
+
+    /// \brief Return requested ip offset in a packet.
+    ///
+    /// \return randomization offset in a packet.
+    int getRequestedIpOffset() const;
+
+    /// \brief Return server id offset in a packet.
+    ///
+    /// \return server id offset in packet.
+    int getServerIdOffset() const;
+
+    /// \brief Return transaction id offset in a packet.
+    ///
+    /// \param arg_idx command line argument index to be used.
+    /// If multiple -X parameters specifed it points to the
+    /// one to be used.
+    /// \return transaction id offset in packet.
+    int getTransactionIdOffset(const int arg_idx) const;
+
     /// \brief Get number of received packets.
     ///
     /// Get the number of received packets from the Statistics Manager.
@@ -746,6 +898,14 @@ private:
     /// \return number of sent packets.
     uint64_t getSentPacketsNum(const ExchangeType xchg_type) const;
 
+    /// \brief Handle child signal.
+    ///
+    /// Function handles child signal by waiting for
+    /// the process to complete.
+    ///
+    /// \param sig signal (ignored)
+    static void handleChild(int sig);
+
     /// \brief Handle interrupt signal.
     ///
     /// Function sets flag indicating that program has been
@@ -759,13 +919,35 @@ private:
     /// Method prints main diagnostics data.
     void printDiagnostics() const;
 
+    /// \brief Print template information
+    ///
+    /// \param packet_type packet type.
+    void printTemplate(const uint8_t packet_type) const;
+
+    /// \brief Print templates information.
+    ///
+    /// Method prints information about data offsets
+    /// in packet templates and their contents.
+    void printTemplates() const;
+
     /// \brief Read DHCP message template from file.
     ///
     /// Method reads DHCP message template from file and
     /// converts it to binary format. Read data is appended
     /// to template_buffers_ vector.
+    ///
+    /// \param file_name name of the packet template file.
+    /// \throw isc::OutOfRange if file is empty or has odd number
+    /// of hexadecimal digits.
+    /// \throw isc::BadValue if file contains characters other than
+    /// spaces or hexadecimal digits.
     void readPacketTemplate(const std::string& file_name);
 
+    /// \brief Run wrapped command.
+    ///
+    /// \param do_stop execute wrapped command with "stop" argument.
+    void runWrapped(bool do_stop = false) const;
+
     /// \brief Convert vector in hexadecimal string.
     ///
     /// \todo Consider moving this function to src/lib/util.
@@ -794,6 +976,11 @@ private:
     /// Packet template buffers.
     TemplateBufferCollection template_buffers_;
 
+    /// First packets send. They are used at the end of the test
+    /// to print packet templates when diagnostics flag T is specifed.
+    std::map<uint8_t, dhcp::Pkt4Ptr> template_packets_v4_;
+    std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_;
+
     static bool interrupted_;  ///< Is program interrupted.
 };
 
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index 860a040..f9f6d36 100644
--- a/tests/tools/perfdhcp/tests/command_options_helper.h
+++ b/tests/tools/perfdhcp/tests/command_options_helper.h
@@ -57,10 +57,10 @@ public:
         ~ArgvPtr() {
             if (argv_ != NULL) {
                 for(int i = 0; i < argc_; ++i) {
-                    free(argv_[i]);
+                    delete[] (argv_[i]);
                     argv_[i] = NULL;
                 }
-                free(argv_);
+                delete[] (argv_);
             }
         }
 
@@ -87,13 +87,14 @@ public:
     /// parsing.
     ///
     /// \param cmdline command line provided as single string.
-    static void process(const std::string& cmdline) {
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    static bool process(const std::string& cmdline) {
         CommandOptions& opt = CommandOptions::instance();
         int argc = 0;
         char** argv = tokenizeString(cmdline, argc);
         ArgvPtr args(argv, argc);
         opt.reset();
-        opt.parse(args.getArgc(), args.getArgv());
+        return (opt.parse(args.getArgc(), args.getArgv()));
     }
 
 private:
@@ -102,6 +103,7 @@ private:
     ///
     /// \param text_to_split string to be splited.
     /// \param [out] num number of substrings returned.
+    /// \param std::bad_alloc if allocation of C-strings failed.
     /// \return array of C-strings created from split.
     static char** tokenizeString(const std::string& text_to_split, int& num) {
         char** results = NULL;
@@ -115,14 +117,13 @@ private:
 
         if (tokens.size() > 0) {
             // Allocate array of C-strings where we will store tokens
-            results = static_cast<char**>(malloc(tokens.size() * sizeof(char*)));
-            if (results == NULL) {
-                isc_throw(Unexpected, "unable to allocate array of c-strings");
-            }
+            results = new char*[tokens.size()];
             // Store tokens in C-strings array
             for (int i = 0; i < tokens.size(); ++i) {
-                char* cs = static_cast<char*>(malloc(tokens[i].length() + 1));
-                strcpy(cs, tokens[i].c_str());
+                size_t cs_size = tokens[i].length() + 1;
+                char* cs = new char[cs_size];
+                strncpy(cs, tokens[i].c_str(), cs_size);
+                assert(cs[cs_size - 1] == '\0');
                 results[i] = cs;
             }
             // Return number of tokens to calling function
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index 801b02d..360178f 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -45,10 +45,11 @@ protected:
     /// parses arguments using CommandOptions class to set
     /// its data members and de-allocates array of C-strings.
     ///
-    /// \param cmdline Command line to parse
-    /// \throws std::bad allocation if tokenization failed
-    void process(const std::string& cmdline) {
-        CommandOptionsHelper::process(cmdline);
+    /// \param cmdline Command line to parse.
+    /// \throws std::bad allocation if tokenization failed.
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    bool process(const std::string& cmdline) {
+        return (CommandOptionsHelper::process(cmdline));
     }
 
     /// \brief Check default initialized values
@@ -56,7 +57,7 @@ protected:
     /// Check if initialized values are correct
     void checkDefaults() {
         CommandOptions& opt = CommandOptions::instance();
-        process("perfdhcp 192.168.0.1");
+        EXPECT_NO_THROW(process("perfdhcp 192.168.0.1"));
         EXPECT_EQ(4, opt.getIpVersion());
         EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
         EXPECT_EQ(0, opt.getRate());
@@ -137,18 +138,31 @@ protected:
 };
 
 TEST_F(CommandOptionsTest, Defaults) {
-    process("perfdhcp all");
+    EXPECT_NO_THROW(process("perfdhcp all"));
     checkDefaults();
 }
 
+TEST_F(CommandOptionsTest, HelpVersion) {
+    // The parser is supposed to return true if 'h' or 'v' options
+    // are specified.
+    EXPECT_TRUE(process("perfdhcp -h"));
+    EXPECT_TRUE(process("perfdhcp -v"));
+    EXPECT_TRUE(process("perfdhcp -h -v"));
+    EXPECT_TRUE(process("perfdhcp -6 -l ethx -h all"));
+    EXPECT_TRUE(process("perfdhcp -l ethx -v all"));
+    // No 'h' or 'v' option specified. The false value
+    // should be returned.
+    EXPECT_FALSE(process("perfdhcp -l ethx all"));
+}
+
 TEST_F(CommandOptionsTest, UseFirst) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -1 -B -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -1 -B -l ethx all"));
     EXPECT_TRUE(opt.isUseFirst());
 }
 TEST_F(CommandOptionsTest, IpVersion) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -6 -l ethx -c -i all");
+    EXPECT_NO_THROW(process("perfdhcp -6 -l ethx -c -i all"));
     EXPECT_EQ(6, opt.getIpVersion());
     EXPECT_EQ("ethx", opt.getLocalName());
     EXPECT_TRUE(opt.isRapidCommit());
@@ -169,7 +183,7 @@ TEST_F(CommandOptionsTest, IpVersion) {
 
 TEST_F(CommandOptionsTest, Rate) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -4 -r 10 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -4 -r 10 -l ethx all"));
     EXPECT_EQ(10, opt.getRate());
 
     // Negative test cases
@@ -189,7 +203,7 @@ TEST_F(CommandOptionsTest, Rate) {
 
 TEST_F(CommandOptionsTest, ReportDelay) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -r 100 -t 17 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -r 100 -t 17 -l ethx all"));
     EXPECT_EQ(17, opt.getReportDelay());
 
     // Negative test cases
@@ -204,7 +218,7 @@ TEST_F(CommandOptionsTest, ReportDelay) {
 
 TEST_F(CommandOptionsTest, ClientsNum) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -R 200 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -R 200 -l ethx all"));
     EXPECT_EQ(200, opt.getClientsNum());
     process("perfdhcp -R 0 -l ethx all");
     EXPECT_EQ(0, opt.getClientsNum());
@@ -282,12 +296,12 @@ TEST_F(CommandOptionsTest, Base) {
 
 TEST_F(CommandOptionsTest, DropTime) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -d 12 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -d 12 all"));
     ASSERT_EQ(2, opt.getDropTime().size());
     EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
     EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
 
-    process("perfdhcp -l ethx -d 2 -d 4.7 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -d 2 -d 4.7 all"));
     ASSERT_EQ(2, opt.getDropTime().size());
     EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
     EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
@@ -302,7 +316,7 @@ TEST_F(CommandOptionsTest, DropTime) {
 
 TEST_F(CommandOptionsTest, TimeOffset) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"));
     EXPECT_EQ(4, opt.getElapsedTimeOffset());
 
     // Negative test cases
@@ -339,8 +353,8 @@ TEST_F(CommandOptionsTest, ExchangeMode) {
 
 TEST_F(CommandOptionsTest, Offsets) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
-            "-X3 -T file1.x -T file2.x all");
+    EXPECT_NO_THROW(process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
+                            "-X3 -T file1.x -T file2.x all"));
     EXPECT_EQ(2, opt.getRequestedIpOffset());
     EXPECT_EQ(5, opt.getElapsedTimeOffset());
     EXPECT_EQ(3, opt.getServerIdOffset());
@@ -363,7 +377,7 @@ TEST_F(CommandOptionsTest, Offsets) {
 
 TEST_F(CommandOptionsTest, LocalPort) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -L 2000 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -L 2000 all"));
     EXPECT_EQ(2000, opt.getLocalPort());
 
     // Negative test cases
@@ -378,7 +392,7 @@ TEST_F(CommandOptionsTest, LocalPort) {
 
 TEST_F(CommandOptionsTest, Preload) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -1 -P 3 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -1 -P 3 -l ethx all"));
     EXPECT_EQ(3, opt.getPreload());
 
     // Negative test cases
@@ -391,11 +405,11 @@ TEST_F(CommandOptionsTest, Preload) {
 
 TEST_F(CommandOptionsTest, Seed) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -6 -P 2 -s 23 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 23 -l ethx all"));
     EXPECT_EQ(23, opt.getSeed());
     EXPECT_TRUE(opt.isSeeded());
 
-    process("perfdhcp -6 -P 2 -s 0 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 0 -l ethx all"));
     EXPECT_EQ(0, opt.getSeed());
     EXPECT_FALSE(opt.isSeeded());
 
@@ -409,11 +423,11 @@ TEST_F(CommandOptionsTest, Seed) {
 
 TEST_F(CommandOptionsTest, TemplateFiles) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -T file1.x -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -T file1.x -l ethx all"));
     ASSERT_EQ(1, opt.getTemplateFiles().size());
     EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
 
-    process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"));
     ASSERT_EQ(2, opt.getTemplateFiles().size());
     EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
     EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
@@ -430,7 +444,7 @@ TEST_F(CommandOptionsTest, TemplateFiles) {
 
 TEST_F(CommandOptionsTest, Wrapped) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -B -w start -i -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -B -w start -i -l ethx all"));
     EXPECT_EQ("start", opt.getWrapped());
 
     // Negative test cases
@@ -441,7 +455,7 @@ TEST_F(CommandOptionsTest, Wrapped) {
 
 TEST_F(CommandOptionsTest, Diagnostics) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -i -x asTe all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -i -x asTe all"));
     EXPECT_EQ("asTe", opt.getDiags());
 
     // Negative test cases
@@ -467,18 +481,18 @@ TEST_F(CommandOptionsTest, Aggressivity) {
 
 TEST_F(CommandOptionsTest, MaxDrop) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -D 25 -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -r 10 all"));
     EXPECT_EQ(25, opt.getMaxDrop()[0]);
-    process("perfdhcp -D 25 -l ethx -D 15 -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -D 15 -r 10 all"));
     EXPECT_EQ(25, opt.getMaxDrop()[0]);
     EXPECT_EQ(15, opt.getMaxDrop()[1]);
 
-    process("perfdhcp -D 15% -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 15% -l ethx -r 10 all"));
     EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
-    process("perfdhcp -D 15% -D25% -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 15% -D25% -l ethx -r 10 all"));
     EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
     EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
-    process("perfdhcp -D 1% -D 99% -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 1% -D 99% -l ethx -r 10 all"));
     EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
     EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
 
@@ -498,9 +512,9 @@ TEST_F(CommandOptionsTest, MaxDrop) {
 
 TEST_F(CommandOptionsTest, NumRequest) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -n 1000 -r 10 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -n 1000 -r 10 -l ethx all"));
     EXPECT_EQ(1000, opt.getNumRequests()[0]);
-    process("perfdhcp -n 5 -r 10 -n 500 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -n 5 -r 10 -n 500 -l ethx all"));
     EXPECT_EQ(5, opt.getNumRequests()[0]);
     EXPECT_EQ(500, opt.getNumRequests()[1]);
 
@@ -517,7 +531,7 @@ TEST_F(CommandOptionsTest, NumRequest) {
 
 TEST_F(CommandOptionsTest, Period) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -p 120 -l ethx -r 100 all");
+    EXPECT_NO_THROW(process("perfdhcp -p 120 -l ethx -r 100 all"));
     EXPECT_EQ(120, opt.getPeriod());
 
     // Negative test cases
@@ -567,19 +581,19 @@ TEST_F(CommandOptionsTest, Server) {
     // set to broadcast address because 'all' was specified.
     EXPECT_TRUE(opt.isBroadcast());
     // The broadcast address is 255.255.255.255.
-    EXPECT_EQ("255.255.255.255", opt.getServerName());
+    EXPECT_EQ(DHCP_IPV4_BROADCAST_ADDRESS, opt.getServerName());
 
     // When all is specified for DHCPv6 mode we expect
     // FF02::1:2 as a server name which means All DHCP
     // servers and relay agents in local network segment
     ASSERT_NO_THROW(process("perfdhcp -6 all"));
-    EXPECT_EQ("FF02::1:2", opt.getServerName());
+    EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, opt.getServerName());
 
     // When server='servers' in DHCPv6 mode we expect
     // FF05::1:3 as server name which means All DHCP
     // servers in local network.
     ASSERT_NO_THROW(process("perfdhcp -6 servers"));
-    EXPECT_EQ("FF05::1:3", opt.getServerName());
+    EXPECT_EQ(ALL_DHCP_SERVERS, opt.getServerName());
 
     // If server name is neither 'all' nor 'servers'
     // the given argument value is expected to be
diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
index d6b3aef..8a83cac 100644
--- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
@@ -166,6 +166,7 @@ TEST_F(StatsMgrTest, Constructor) {
     EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
     EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO));
     EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
 
     EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
     EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
@@ -342,7 +343,7 @@ TEST_F(StatsMgrTest, Orphans) {
 TEST_F(StatsMgrTest, Delays) {
 
     boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5);
 
     // Send DISCOVER, wait 2s and receive OFFER. This will affect
     // counters in Stats Manager.
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index a4cde00..66f85fe 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -92,7 +92,7 @@ public:
     NakedTestControl() : TestControl() {
         uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
             1 : CommandOptions::instance().getClientsNum();
-        setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequencialGenerator(clients_num)));
+        setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequentialGenerator(clients_num)));
     };
 
 };
@@ -117,12 +117,20 @@ public:
     /// \brief Create packet template file from binary data.
     ///
     /// Function creates file containing data from the provided buffer
-    /// in hexadecimal format.
+    /// in hexadecimal format. The size parameter specifies the maximum
+    /// size of the file. If total number of hexadecimal digits resulting
+    /// from buffer size is greater than maximum file size the file is
+    /// truncated.
+    ///
     /// \param filename template file to be created.
     /// \param buffer with binary datato be stored in file.
+    /// \param size target size of the file.
+    /// \param invalid_chars inject invalid chars to the template file.
     /// \return true if file creation successful.
     bool createTemplateFile(const std::string& filename,
-                            const std::vector<uint8_t>& buf) const {
+                            const std::vector<uint8_t>& buf,
+                            const size_t size,
+                            const bool invalid_chars = false) const {
         std::ofstream temp_file;
         temp_file.open(filename.c_str(), ios::out | ios::trunc);
         if (!temp_file.is_open()) {
@@ -131,7 +139,24 @@ public:
         for (int i = 0; i < buf.size(); ++i) {
             int first_digit = buf[i] / 16;
             int second_digit = buf[i] % 16;
-            temp_file << std::hex << first_digit << second_digit << std::dec;
+            // Insert two spaces between two hexadecimal digits.
+            // Spaces are allowed in template files.
+            temp_file << std::string(2, ' ');
+            if (2 * i + 1 < size) {
+                if (!invalid_chars) {
+                    temp_file << std::hex << first_digit << second_digit << std::dec;
+                } else {
+                    temp_file << "XY";
+                }
+            } else if (2 * i < size) {
+                if (!invalid_chars) {
+                    temp_file << std::hex << first_digit;
+                } else {
+                    temp_file << "X";
+                }
+            } else {
+                break;
+            }
         }
         temp_file.close();
         return (true);
@@ -965,8 +990,9 @@ TEST_F(TestControlTest, PacketTemplates) {
     for (int i = 0; i < template2.size(); ++i) {
         template2[i] = static_cast<uint8_t>(random() % 256);
     }
-    ASSERT_TRUE(createTemplateFile(file1, template1));
-    ASSERT_TRUE(createTemplateFile(file2, template2));
+    // Size of the file is 2 times larger than binary data size.
+    ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2));
+    ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2));
     CommandOptions& options = CommandOptions::instance();
     NakedTestControl tc;
 
@@ -983,6 +1009,32 @@ TEST_F(TestControlTest, PacketTemplates) {
     ASSERT_EQ(template2.size(), buf2.size());
     EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin()));
     EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin()));
+
+    // Try to read template file with odd number of digits.
+    std::string file3("../templates/test3.hex");
+    // Size of the file is 2 times larger than binary data size and it is always
+    // even number. Substracting 1 makes file size odd.
+    ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1));
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file3 + " all")
+    );
+    EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+    // Try to read empty file.
+    std::string file4("../templates/test4.hex");
+    ASSERT_TRUE(createTemplateFile(file4, template2, 0));
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file4 + " all")
+    );
+    EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+    // Try reading file with non hexadecimal characters.
+    std::string file5("../templates/test5.hex");
+    ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true));
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file5 + " all")
+    );
+    EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
 }
 
 TEST_F(TestControlTest, RateControl) {



More information about the bind10-changes mailing list