BIND 10 trac1528, updated. 221f5649496821d19a40863e53e72685524b9ab2 Merge branch 'master' into trac1528

BIND 10 source code commits bind10-changes at lists.isc.org
Wed May 9 12:20:15 UTC 2012


The branch, trac1528 has been updated
       via  221f5649496821d19a40863e53e72685524b9ab2 (commit)
       via  028d5ab64c816945dc310a6d8b4d159e22ba9184 (commit)
       via  a40b6c665617125eeb8716b12d92d806f0342396 (commit)
       via  b22ff2de4d9421bd1ef3976967aee886c8811611 (commit)
       via  409ca483d117688be552d27d1e06616d44701c82 (commit)
       via  892f6b266acb98ad005e1c6dad6c0621c04f1d82 (commit)
       via  18675a2d6a237c775ac8bb91c26c94c7b4219095 (commit)
       via  25f0bb96ddbe5adc6538e68c2d6eb2da6bcc7c0c (commit)
       via  6c0da87a7bdbb7be59fbac47b6c5f2ef0043aecb (commit)
       via  36d930c2e66f13fadfff64689bee2547ca2ab8cd (commit)
       via  be560f4516ae9b0bbbad1740a519feed99c04170 (commit)
       via  0c1c1eb1a972e9f85f99510766290b7ed9fe06f5 (commit)
       via  b3dd1982b72e5f4710e33266310180c71203ce0d (commit)
       via  1c88a49755f6617184c45fe127f00ffec9e1b89d (commit)
       via  fbe7a931b3f74452a87449ff17792f40b8d8e08e (commit)
       via  253701eb2cfbf5830f71c3b00e2254df6b4ecdef (commit)
       via  84ce6a7875bd85a0a7c96bff18ba2c07a65120d0 (commit)
       via  6567d28ea958b0a89204f04714bbd90f70dd0d78 (commit)
       via  4846fa711c1003092a15f61be4ccd0660828a28c (commit)
       via  8c1eb198095738cac179dd2252119cfb27f47fdd (commit)
       via  fcf2f08db9ebc2198236bfa25cf73286821cba6b (commit)
       via  7d8efaf11c66fc7dfbb79060ded52f779fd523f8 (commit)
       via  0a05819f8a0404f2aaa1a0a1ae06caf40a895863 (commit)
       via  c805a1176ab52d0326771b432de744037f0a76fb (commit)
       via  255456507a465ee2cb1b94e386366f259c8e9487 (commit)
       via  491896718f86b7514c661b7964cd92673f7e037f (commit)
       via  8b0e391e3a838b2d76d87e87088f025491802c8b (commit)
       via  408043d940134c7e7c301e762888f10a4184a400 (commit)
       via  d7191f1b7ae74dc696c386576c422582800aa5b2 (commit)
       via  56083b82568a61b2fc48afd95b7e998568216894 (commit)
       via  cc0b929d0d82eab6df92a23a0a09d330938d79eb (commit)
       via  d6d267370155e3feeaa108af1d05931afac1f787 (commit)
       via  5fb18596288d2eae586cd01da0b27f329fb4e155 (commit)
       via  dfaf9712d15392caef04df6c2149744f8abace87 (commit)
       via  f894ef742226f6c9d438a21be01f6a5d0cbcc5fb (commit)
       via  3d876bb262c8eea4b3cdc60f293249068adde2c2 (commit)
       via  3a9c1b6387eb19a1749e7e9a4dec753b33da5766 (commit)
       via  37227b85bf18cf42bcc08bacedbe86bbe7bf6cfc (commit)
       via  1ff3eb19126e4c7a43a6f6b64fef46590f462fb8 (commit)
       via  156ace04dc1267c73f5e58006dc35fb0d3788c86 (commit)
       via  7b0e7e17467d2a8c31fd8a8b4ab34e16f1ad84bf (commit)
       via  bd2568ebded2746a7a6741c5b2f927392820506a (commit)
       via  5ffee903b4e48f34c363215668d3867c0a7f6c94 (commit)
       via  95947109c58b1e099ee5c4e955f739213824e49f (commit)
       via  5c8bbd7ab648b6b7c48e366e7510dedca5386f6c (commit)
       via  653847cf994c75cacd23df1ae0e5c9002cf338c4 (commit)
       via  0f18039bc751a8f498c1f832196e2ecc7b997b2a (commit)
       via  4c43c7d967b6b9fe70d8431058808926509755f5 (commit)
       via  c73ffa69fd26e03b0879a7773f7f6796f19aee2e (commit)
       via  551657702a4197ef302c567b5c0eaf2fded3e121 (commit)
       via  58757c09f3b72768da9a43f1affa7120ebbdeba8 (commit)
       via  3e6773dbdbc31ca20637455b110c2907daf2ce0c (commit)
       via  17f72c2e942afec37711c9b05c4a5351f40b4676 (commit)
       via  c016b74723d24469961682defaee0034bb8b5920 (commit)
       via  4429acb90d47320d20e9126457594a1f38191b28 (commit)
       via  2f7b1a1865ae1c8b981d5f6396ae612421be764b (commit)
       via  9a76caecbc29f35dad73a2f9874e8e3a64e4154f (commit)
       via  3de6e881f69c6462cf3da43c662cbc1a35595504 (commit)
       via  a475ef271d4606f791e5ed88d9b8eb8ed8c90ce6 (commit)
       via  3055e37cd7bdb8ada5cdf54e611343665ff93675 (commit)
       via  f9d8efd545fb5bdb5bbab14eac2111ae4e47f75f (commit)
       via  e63005a633e4f62a5e359ac26ba1704ff55a734c (commit)
       via  a3893c3664567e1ce3c724b8d478989405ddaa77 (commit)
       via  0ddeda7f0669b481c4d7ceadc9f8d76db41baed7 (commit)
       via  597e059afaa4a89e767f8f10d2a4d78223af3940 (commit)
       via  9f613bb9c9be87fbd800a1e87044070a04d75adf (commit)
       via  84a0413302fa935314dd81a352aeb6d746cbbf3a (commit)
       via  dc142a44a0a0de3d9278937bdb0d70c545e6da6e (commit)
       via  8df566b82de165047fceccf6b4eda1974f3e78e5 (commit)
       via  373ef178d799790eb5917f5fbb2b725391088b0d (commit)
       via  e0cb5ab6d32eae3df6e309ff73c6d331f0750168 (commit)
       via  fd1a4b0eac2f586c2efc98cb99235ddb1c77da8a (commit)
       via  b24b0f2312ce99cba4af8456179c712d5cc0fe9d (commit)
       via  146b5941b9f3d09537b56eb83df14355c2edde59 (commit)
       via  4cfab0d82574c3501502a9ed9b84c39112d411a8 (commit)
       via  e4fd9934de5258ad1668c4cbba408fe59427c040 (commit)
       via  bed1681e323f5c278a4271b7c948a44b0605e73b (commit)
       via  53a7c709b06e670dacf81907a7360ea030341b1d (commit)
       via  50d20d7206fad792c4eb035a9e643f4d47518505 (commit)
       via  c8e538c10d73ea56f079fcb8f3c6f99e5407bdef (commit)
       via  b1c727db35e23c3c72262af0f0a54ab8fe4b411e (commit)
       via  5b37859360df57221621144d34119fcc7e417099 (commit)
       via  6e05014aa5bd412778a0927ac1babff042e5f46f (commit)
       via  264691395b76a2a6f18b02dbd43c78f6e9de83ff (commit)
       via  438b5b183c82b04d74be71c7cc396d6393d215b6 (commit)
       via  da4d8df9ee0412d3717f1009ecccac58736313b8 (commit)
       via  672f129700dae33b701bb02069cf276238d66be3 (commit)
       via  a6cc81d79b42baf06058c25f404b8ec61de0c2d6 (commit)
       via  a9b59f8a7728418038125bfb3ed2335bf5524b3f (commit)
       via  bcb62dd7e4d8a3bca40e3725aa457074ada36134 (commit)
       via  400ff18a8c98cd0c1c393ef32e1d76bb4222dce6 (commit)
       via  1c3fe6e537bf9e7bc742f84a36efbbbb49a6365d (commit)
       via  64f69a14336846132a5588f6523c3f5aadf63382 (commit)
       via  233c7295de7021fe24b8342db5d966d3d9b354e6 (commit)
       via  52707b19511b499401a61b42eb3a03207f79b0c6 (commit)
       via  f782145ec0759ef8e595c87d4baf15de89624ea7 (commit)
       via  f93cd951e3ead684e4efbc8d2eaa523141e0cf65 (commit)
       via  e7c91fe725369cabe19bd227e06176a117955461 (commit)
       via  14a13b8494a53f2a488859ed27726f32e57781b6 (commit)
       via  47f1415387f974e7ccdf7d95d7a380827486bc22 (commit)
       via  9a4619a0a1ffdde67a58af51f3826e5c6a8da20b (commit)
       via  fb7201b9ff0aaa126ff82595b7a4c2f69ea8f4c7 (commit)
       via  bea9aaf1441dd9da91cc1197d02ea52c0d0095f1 (commit)
       via  d7cf5f81d983e95e4f8bd85637a0ae9785d94a9d (commit)
       via  91cb18e244588b8a99ddf9b12fb99986b0258fdb (commit)
       via  7aac25a59bfb9dbdbdbc4c8334204d24f7e0b131 (commit)
       via  358beb2e1e15bcbe09438719e756f8641ea929d5 (commit)
       via  d68d574b69ee627b11982276312dfa70160b072a (commit)
       via  c3be9fa408b02750d56f63ba593b9a0c92647928 (commit)
       via  c9b2b1cc3060d991f9fdc89a8d70d4364f2e9d6d (commit)
       via  0422339a5a2c934aec17869b8096f263d6cae922 (commit)
       via  4deb49cf84c399a0ca1a84fef1cd0dee4ca4d5d5 (commit)
       via  f00dd21c6c495e180feb855139d36fc7f99f23d8 (commit)
       via  95410c80370f458bf2d3908e92a69c2602d071d3 (commit)
       via  89071a214099a1de3ce7253e4f91fa4bc8d00272 (commit)
       via  36efa7d10ecc4efd39d2ce4dfffa0cbdeffa74b0 (commit)
       via  a318061e3c20ad5deeebe41884cbe3dcf9a3a413 (commit)
       via  4a7381b8b8a03c010372c7a62192bec325a40a8a (commit)
       via  211934fe44eb6ea5a74857d4312e10b6cd6a3c55 (commit)
       via  136e63b8234f46a46c4278e4ccfe9712a76ebfc9 (commit)
       via  149ac337e9a8e43896e84268a462463429061aed (commit)
       via  93f11d2a96ce4dba9308889bdb9be6be4a765b27 (commit)
       via  a7dbb75a88c71513c2ff2d0c86eb98b1f287e6f4 (commit)
       via  952cf4c1d92cf00179347abbc15fa37318c5ec90 (commit)
       via  277af902ee91a90c1ef8ebce3895ecc27d8dec2d (commit)
       via  7f108b1ad180d002ff6bc2d4be26b6a93ead8c99 (commit)
       via  a93523dfcf7d640b15d9af71596caa09362cbbfd (commit)
       via  56bd002f57d397ec4c1fffb40c499425e36b21de (commit)
       via  4653281fb863709e2ab9ca26514fe9e31cd8444d (commit)
       via  0f9b1d45d37223a5e1f7167a97c9760b52e744e9 (commit)
       via  d3fd5c7fa44e0f19a97d2dcb99ee937eaa80d704 (commit)
       via  ae46e78f9e1d4261bbf86a3dcaf7bd813851edf0 (commit)
       via  0464833becda85dca7deae50a7f4adbf9589076f (commit)
       via  2c8983c96edd2e98f285d35343a5e4958fb1971c (commit)
       via  c89bd8e38d3fcad506676876cc110c937cc3fbfe (commit)
       via  22041b8ed4dd46d27981c3e645973de62c34a25e (commit)
       via  66a9862c8ec60bd4c64f1535590ac16fae2fd088 (commit)
       via  2fd8732e2b16ad5dffcbf12345e97a05b146c592 (commit)
       via  5a3ba49ea9cdd4cde08d1855a0944f6899eaae8b (commit)
       via  efc43a67d27a14c468d3cf53df87ed3f808fd730 (commit)
       via  d833b85d17a2958890faa25927c1d1ed3443279c (commit)
       via  3e27e1508a29251494a907a6b00258900a5200b2 (commit)
       via  412304add5726cd16f91c010471fd895d65c2a6d (commit)
       via  a2da847353f3fd79feb1a4b902faebd74b31fa64 (commit)
       via  8b8dbb612ba7f55ae49d113297a8e119c51f492d (commit)
       via  837533a24607e825f2dc72f7fb45665c4e8aec99 (commit)
       via  c545fba97414013e52eda55f2d61ba23f9e076e9 (commit)
       via  2e25759b8ff76ff81d75201bc4c8ef6eb243d144 (commit)
       via  0a9dad718d84b7de43478df6804da1da25e5dab9 (commit)
       via  ff240f0bc7b13b7932054f435b7159f788eaf912 (commit)
       via  7544617e812757e1d1f02ebf9d1c44e1fd01a4df (commit)
       via  c537d66e97d1a10a544baeba5e5490bd061f1590 (commit)
       via  cf96b2ad155c63f0ffceaf69b634e5f772d22770 (commit)
       via  92c00e5601f7816113eab6be8a165cca0fe6956e (commit)
       via  8cdf67b6ff82596aa51e56fd5eccbd75763e9011 (commit)
       via  69bcd5348b07321645cbd749dc964d96bf654076 (commit)
       via  a83554ce6da992e5d060ceb12baa6948c6e4bf33 (commit)
       via  9cc9ffd9f10433f85a6b056d7face686b5522a13 (commit)
       via  c3b46d0b46f88f759c7a4738040e6edaf6845d5b (commit)
       via  034bc4e02eb482913c2e2c0892d5cf10e5525aae (commit)
       via  b303b434d956c7b5842fc0ca5c43f4aa69351a34 (commit)
       via  b7f87132783143fab9f8f6ecc0977fe220f2e58e (commit)
       via  22aa6eec949ced4a40a599019a275c7e732cbb70 (commit)
       via  b23c36ef2e61cb4133f8c2708aa8cf85e4809d61 (commit)
       via  a17f42a08fa2d0fbd2de357d90dbbfeaaba32335 (commit)
       via  0d2ebae0cc3b6119ae0cc2f7fa509a5792290ecf (commit)
       via  325b81328b624d467c674a7bf74e73a271892d4a (commit)
       via  be50e95723a83adcb63e9622eaada199f3f17463 (commit)
       via  31de885ba0409f54d9a1615eff5a4b03ed420393 (commit)
       via  29264296474f103f8ece3e299171f835b81d9e9a (commit)
       via  65e64487c28d3688d65ed625cdb1531ce5691095 (commit)
       via  4108ca766cb9bce434bfbb786436b7c08adb87a4 (commit)
       via  de9ccf5df44c42526b2cd8e7f078d6073729c35a (commit)
       via  d78d0dbcec53a339b7789b181954768d181c2a1b (commit)
       via  be9562a0d38bdb2ba5d3e8456dd13a0cbc19b48e (commit)
       via  5149a8149791f6f828704a2aa63178ca98707ebc (commit)
       via  78c6b5331109e94c7c5217c500162106b587ae59 (commit)
       via  ffbfe8fb2978b60c8309b3d7b7d006c98778b304 (commit)
       via  fa7b9c53b18d220062ed252abbc241e8871e8927 (commit)
       via  61fa7a41089bf18f39ae6415fa92c6c2bca6a026 (commit)
       via  0bc5c68ef6c5811eb93227e1d8d44f7762a3f7b2 (commit)
       via  7efc7ddb385ef56c5df5a23474a10900bbe36cf9 (commit)
       via  fcdc7a7b7fbf23338d1c7165e6f3a8ffe4a20459 (commit)
       via  e0bcbb981c3b22f3380fcee592ae7cc2dadd0c00 (commit)
       via  57512ac60b298b3873b9ee74d46f4f56ab746494 (commit)
       via  00a36e752802df3cc683023d256687bf222e256a (commit)
       via  3057455dd008c612f64fd265a957bdf9c0b58679 (commit)
       via  65193e8384b25f0092177adb91a8ee6c2679d2db (commit)
       via  5dcf1dadd19d9b251fa6dbefefbc98f16fb62c66 (commit)
       via  7c75154f52853de902bfd7c880b6e16fe3a79c0f (commit)
       via  d127560b02816a7ba12869d19ca51a30e695ff35 (commit)
       via  793772f302013324ee4964e5b2c3439c0eed2221 (commit)
       via  be2b8d67e266598e0fba9e658042986c6833d220 (commit)
       via  6cd82ad2a63dfc9bcd15dabdf650da242eaf924b (commit)
       via  493f952e6937adf7045732a2f7e0c4a4313fc0a5 (commit)
       via  9b6993002b4ba9019551e50613c8a2c6c7ff9fec (commit)
       via  d8b8e46b853f13d3e9ef2857f0fce424f1876ef5 (commit)
       via  7116ee3c764180aad581e52b36dc67124b7d72f0 (commit)
       via  fb2317550507bb357cba2eee89c3a469f1a89803 (commit)
       via  291d0cbfdc964c1d60542edbe9f442cc383657cd (commit)
       via  52e971851f0c7ee8f45c511d810497e3c038dc71 (commit)
       via  07274f662a772c856f0bf80213b246e689582409 (commit)
       via  c3bc4e02519d15e27b8e32291bda1a59ed08f42b (commit)
       via  b77375be27718eea1619f4e4fdb4899a29eea18e (commit)
       via  05793b5a18793908b17190008382a27e133e5979 (commit)
       via  186bacfc7ba324647f6689fd627c8d9d2d724c0c (commit)
       via  058af3dc4b9f1e03d46c549f3ea848fb1a5c7960 (commit)
       via  c47c4c3541a5a9ed7a77f47610d8f14c29295969 (commit)
       via  7130e28820b2e9e603f64546deebf12b410b897d (commit)
       via  a29df11575ac9b1aa036ab212b49f4706ba1e607 (commit)
       via  56338ac70f174880ff1ca73bda0afe73dad2e7d4 (commit)
       via  2d99288b3400b01e3eb1402717a182f5e828c7b7 (commit)
       via  52ee8a28742a20415742651cbdf7982387050641 (commit)
       via  33f9ea32c67a6e5e7df816432c98f3c9772b0b0f (commit)
       via  ed4c07d55c90e871d0c2b8ce571273cf83740e66 (commit)
       via  283053c1a3dd96ff3811f25361f900dd7c9d97ef (commit)
       via  f173bdf07ec3f4d099176dcc6b7f878b218fb93a (commit)
       via  7ce82914391a42fde56946b284f386bf0f3fe169 (commit)
       via  5c9b7307a56c8b578be2b5b1ad73799b899a2e93 (commit)
       via  77b918b70668c7755f3b7fb8335f5fc1f9f119a6 (commit)
       via  0c0e8a5f4ddafbd7724545c08bc813c70f360faa (commit)
       via  4eeff0e79de122645ffd3bf117a1486147fc9541 (commit)
       via  a98fc15799b0d8898ab3b5071ba55dd8935d45dc (commit)
       via  94077743ff440dfbcfd4a723f3fd676acbfbefe4 (commit)
       via  b407617f16ae9d672ef0e7812a8eb9e18509b6e6 (commit)
       via  76f364b152f174c57982f294f97274a212684121 (commit)
       via  6d7a20689e5ac2dd8139c5d6cece17b5d1a4f178 (commit)
       via  d12134b3a9f135f9ae4317587eef31f45e6c0454 (commit)
       via  55cecba6f3c43d725fc7c1614e5b47bc7729d5ec (commit)
       via  7ef140c77fa2ddd4194ffdc344781a55232d68b3 (commit)
       via  7a0dc75cee48aceeb218147331ddc81a05376358 (commit)
       via  61446fd4004c89a7d568988776a0fc2c1b67046a (commit)
       via  9ad569a0f568d9faf5f213f1403dce51cbcf08b8 (commit)
       via  9b060a79e29c1691d1e84da5d6a656e99f2ec8a2 (commit)
       via  501c5f296f27b565b6e2229e62f8f19754818abf (commit)
       via  99adadb0496046011060ec22e6834dfede9f1ce3 (commit)
       via  e267a6928ace0651795f188f43a6a5a85c479d44 (commit)
       via  08d13321640259a5036053852ff0c8731b54239c (commit)
       via  7610a7bacea56b07ae06975ee771d010846addd2 (commit)
       via  0996aa92a103e7af59bf1ae9c501f5feb2a5894c (commit)
       via  f1f0bc00441057e7050241415ee0367a09c35032 (commit)
       via  fb9b8cd9f1200dcd6d7146a45fdbfd2c7675be56 (commit)
       via  16a1358aa2960039e192ac66d504150a55b374fe (commit)
       via  c02b9f4c1f0970cd50c5486f739dd750046ae97b (commit)
       via  aaf4fb3d70f5868ba9c6fa16c490abd7ed5035d3 (commit)
       via  2397c86b17e71f1472a867dfa1fb4e7223cb811c (commit)
       via  2fa92e23966279921c57bd1fa67ec79e56b328ed (commit)
       via  9858d6dd9af2640765266d6b99056e93dc368277 (commit)
       via  654f0cbd1850a78758194cf3dcc7d5a90d37e495 (commit)
       via  dcdd4e10f22f795754f91805b741988ef8a389c2 (commit)
       via  68308bd95eb0a3d52f048790cd16611b5fdc7ee9 (commit)
       via  d5531c9856c5bb85f63d2e3168fc8b08c9700418 (commit)
       via  ea528f50fddfb83618aa338bdd4607791fd788ef (commit)
       via  b4466188150a50872bc3c426242bc7bba4c5f38d (commit)
       via  4df912c903ef7469b0034f1750de4cf0711e9e2d (commit)
       via  81300648c7274effe356e4ba421cdf3cdab8d316 (commit)
       via  97389bad9bf82cd32329407557aed4ea669401f4 (commit)
       via  8f3e82a987df1e5abc1ab2d4f0785ca3f9ab97ff (commit)
       via  44d2850a0d3e96ad2b405aadb222a975c0462826 (commit)
       via  e663cfecd5385a0d2e27c301f089e1f60d3e9a28 (commit)
       via  efb79bb638d7c88d3f46c9486d7f29f2d2e6e8df (commit)
       via  2645b194340a5a7fe315505f6d4cab874573c2ae (commit)
       via  00bc99c5a2c8412994dd67b0e01318abb2bde893 (commit)
       via  f48ddb14605e0ba0bf226ab074dc881e4a782a9b (commit)
       via  cf16578b3ecf2da1b38a724107dfaa802b03339c (commit)
       via  92cc4a7b436ebad4536979bddbcea21ffa0b118b (commit)
       via  ed1f3349ee00294fbeb42760a53a2694f9dc02eb (commit)
       via  635e3dae01973485a2d81487fb684d40c8db930a (commit)
       via  ceaa247d89ac7d97594572bc17f005144c5efb8d (commit)
       via  dddc267e555071b02a90623758f947d18c46356c (commit)
       via  d5e250eff9f9c7996f16eebfc6f4bb7e0dd02aee (commit)
       via  923c859c0ff6fcfc3d0c268cd20d493b71e66d29 (commit)
       via  7f720276658e694a1edbb8ce105b04a8dcf1d36c (commit)
       via  8644866497053f91ada4e99abe444d7876ed00ff (commit)
       via  36ec15a2721f408c1868001f8dfa4c358f2f0ec3 (commit)
       via  c30d4ccd7f07819b60695d11dd5f946b61e8fe17 (commit)
       via  8a33648413f84e7a7be65db8890d217d91f7c8f6 (commit)
       via  49c83cc4d393dd69453f4fc43cd7d801e03300ef (commit)
       via  f4489fa39cb50a2d0a80925ac6709d4c2beffc29 (commit)
       via  350e65820efee213ef09122b88cdfdb7f4ab38b0 (commit)
       via  8ae80ada66174a7748d0ac5a02d9fbe6f443303f (commit)
       via  fe9349ae3a6f283089af635b2453f3b7a6c66ac6 (commit)
       via  d9ede029ea8224a62edbdb2dc890a14e068a870c (commit)
       via  16b92d014e713933d591ebde9cbc4540044ce8fe (commit)
       via  365b5c443aa170831bc1d6a40b0e3323192fb532 (commit)
       via  fa6a9dbe36ffc55bcadf54a523bedcfb7118474c (commit)
       via  3b09268518e4e90032218083bcfebf7821be7bd5 (commit)
       via  daf2b85fc21361544855324799f2d3af777044e4 (commit)
       via  098da24dddad497810aa2787f54126488bb1095c (commit)
       via  69e76e0edb5a28773bd8e29d82902c4dbbb412ad (commit)
       via  9e77d650e31a106518ad263092f6a7e2a69d22a1 (commit)
       via  1eb1ca43bb8682d49c2396395919d8445a28c012 (commit)
       via  a3f1538ebc5aa4b0a6ee49fee142646ff3bfee2c (commit)
       via  297646b5cd8cee65d80ef74d07cc7a3b4890d555 (commit)
       via  cc73f7e20f8534b0986bcc9a41e99511109546d7 (commit)
       via  1c45a4e0bc84ab2d3e006ceb63ce18b9c32adf53 (commit)
       via  80c297d9ae634e44c510884ede894c6737941820 (commit)
       via  7a74c2d58e76b8fcc9aea301bd4ea42a0269aca8 (commit)
       via  bbe403303f87ef541114aceaa0aba02d2df714b4 (commit)
       via  698534e5564fb14a384d1cf28eda058453dcb1a5 (commit)
       via  947f08c755bd91255b2f8034a65b99445f13b024 (commit)
       via  4df83ec7d1b92d7fb8c6fd2c0906694683da8c30 (commit)
       via  9ee14145b2879b044fdf0c32678838ee67343094 (commit)
       via  6e57b76919731d8f5a15d21d2c008336362f44c9 (commit)
       via  a29f99f27df0d39ad097b393dff0ef12f6e89388 (commit)
       via  7733a51887b6370c92cfb02a2446b10d15e224dc (commit)
       via  66300a3c4769a48b765f70e2d0dbf8bbb714435b (commit)
       via  952cb2ceeb8e23145a18839d42b12641e520ecdb (commit)
       via  318ecd4e65de22d957ed55f571e868ceb3152f97 (commit)
       via  9a56eb7ebbefcaa29ea623bbf8d7102b6970ff70 (commit)
       via  ead53d5c0f105d4f60d451734d66c8f202cbbd15 (commit)
       via  c31cfdf8449030f874406f4efad754ba7eb786e6 (commit)
       via  cd6faa250e082808a86d9ccb1f11ea07ab81a618 (commit)
       via  5893305969aec78850e2462859b3bf4b7a157057 (commit)
       via  419665026524f1a1b46efba377d41bd1f7f806d0 (commit)
       via  ad0ce258df14fa88a299ef37238d4c2527f273c8 (commit)
       via  fa4aa9c87c599a985bb19b78ae3f2d1d4ab9bb63 (commit)
       via  70515e4857db49a5d2cec93f00839ce2cf1ea52d (commit)
       via  65b293e59954c95c245f44691f5c82fdcd6579c9 (commit)
       via  532aa507e57aaa8c406cd4cc537058de6fcb5a88 (commit)
       via  94793e41d922cb10e35e0ff146b19c38ace415b1 (commit)
       via  c1a0b11d84b6953c0c53bfe26d7c1a946bb772fb (commit)
       via  737c4eb744e7edcd77410a11000b49e17ca3af8b (commit)
       via  1a4d0ae65b2c1012611f4c15c5e7a29d65339104 (commit)
       via  f95e202d7e972f92ebf5ee9ca11c4ad846cd1524 (commit)
       via  6ac392ce798a63a024a2890e3838df8a15a233d0 (commit)
       via  376c01fc1b6114e6717a9f515999dd1bf67db39a (commit)
       via  9d192aa44e2cb1495e78b70ad5af23c21acfe34d (commit)
       via  88a80126ce73428584f8f36657cb159e581c700e (commit)
       via  77eddb37c51caf6efb688a27aa60ec15fd0d3535 (commit)
       via  252fc353bab9ae9eac99cc5c54b66b6e493d91b3 (commit)
       via  1a46994e855e3a2a0b5b407df94e52ada3efee7e (commit)
       via  c80eae98a8dd78a2c0c711f642a02a4b24a3a819 (commit)
       via  ae9eae29057da3aa8c585b86b662967801ed8bb2 (commit)
       via  48cd72d455b5bd1298485f58fc7dcf82c7e4c3cf (commit)
       via  06280cc6b480f6976f25ccf8b9cedbdf7aa00e34 (commit)
       via  af354fd9eb8390cb388348ad66a4d8269b945ac3 (commit)
       via  48cfa00bb988cfeebab4e6ac7ae440cd685137e5 (commit)
       via  16ad60fe0d9ba8cf53fc6a190ad25d92ece8e8bd (commit)
       via  9aa4dd78f5cc00acb5ecc4fdc5ba1beb423aa40d (commit)
       via  61cdd0f353e20153706af04ac884a4375386648c (commit)
       via  eee654360f42459c1930f83e78f901cd4e9170f7 (commit)
       via  6cce379ad4e562fae0e899b2134f59f947d54f3e (commit)
       via  8ef09a5063e52e52e1192f81f001c186a32aa18d (commit)
       via  5b0ea09b5f533d2cc8c572693d882061b5d55e44 (commit)
       via  52f1b58737e7c7e276cc7ee7daabf2f2e2d7f2a8 (commit)
       via  6736c51047cfe2ac2c4678299b5b768ba3585db0 (commit)
       via  3104e910b9a4753d2bf4025d59f0f1a13f01e519 (commit)
       via  01d11e9fe77f1ea869aa1a0e800a965dcc638050 (commit)
       via  a3ccc5c6476526739e363d3aafbd2e497cee069b (commit)
       via  07403ec675bf43e0936aea2ab5d4f5d903770f13 (commit)
       via  ce81fab6415ae7f50ed29c201162e912d41eb50d (commit)
       via  061f5a1ae10248ac161b9c5a489eb0405b2c8426 (commit)
       via  9f4292fa5d27c5569384ca57b7878be69cbf5499 (commit)
       via  5a4cf2dd5915ed8f499570faa1005462396b233b (commit)
       via  fe5549bb7e546732e338db24abb7ad571f34f668 (commit)
       via  63e4fc15cc2c66b07168bb15e2e6af464c235a3d (commit)
       via  2df61b4c21596ca9b1f9e2e2364d6ed352c2eca0 (commit)
       via  6a9dbeff7d28a8ef91ceddff5dbcdef9a7d1452e (commit)
       via  5ef450d431113ab2aa349d6200f7f69f55e975f4 (commit)
       via  f8c0d29e25d71f77314ffc997e678c7b24c34ca5 (commit)
       via  34da803fd804c6c0fa7acb90ff46780df69d5898 (commit)
       via  6db3556488ae8a75f5250a175f3507cc609ada19 (commit)
       via  f07fcf3c8e3f28d97772994f26520ac20436eba2 (commit)
       via  c9337b1576d127bfdf11ed0e87db9c6cc1f98a09 (commit)
       via  342f5b2ae66112e9416da86436b7932ea8e6f9c4 (commit)
       via  f7ae819d576cd95231c89c269654e23afd16aeff (commit)
       via  5b0ef5ce8137711c67bc9ce0e4561b7adc2ebcc2 (commit)
       via  3a3f1942d99e404b9e8a7b83f6688fb8b8518afb (commit)
       via  434d8db8dfcd23a87b8e798e5702e91f0bbbdcf6 (commit)
       via  58283fa48ac4c232e20edb144de0dad791f429cb (commit)
       via  8a3564c8893c5b9df451ff29d58f65604740b310 (commit)
       via  435436f1402c345fec712d923848f934999f3107 (commit)
       via  98483c34555c48143e6bfcdab63a9ec68a6fb86a (commit)
       via  99afc8ab28a7748f9dda301da98e64a2b0b5e04c (commit)
       via  724baae9fa3e9cb0e6e0835ae3dafc83fd30b84f (commit)
       via  93e12f75baf215fdc8434461a2d5428e73f06860 (commit)
       via  9d84626f72a70b3b71330c6fa31d767109a67d9b (commit)
       via  da214e3e13d2ee898a409df35cff9f208654503b (commit)
       via  418a6dc9ee0369d13d14bfa825229c0e35a694e4 (commit)
       via  8ab7817d7c346d35a7be2a1d6bfe51aed4fef15c (commit)
       via  9e5da4f42072e2bee454178eb1422103504a40dc (commit)
       via  78bb8f4b9676d6345f3fdd1e5cc89039806a9aba (commit)
       via  a8486dda670bf22c27c7cb2ee95b84cc53c1fc08 (commit)
       via  73a6349b16ce1c95ff6216fdbff9551d574988d9 (commit)
       via  5f7f84779f66576555728e70ea75383945e0700a (commit)
       via  eb3cc6a30fc6ddf4124a0051d44bcfede286520e (commit)
       via  59464e665cd90e476e6c45843151ab6209a8517d (commit)
       via  29f876f89516fb0696d85e760bb9cb15e7e398b2 (commit)
       via  17ab7fae73ebcf624a0c719b16f45b9f16db1419 (commit)
       via  21b1adb387dab476f88c301ec1538fb6be87ea1e (commit)
       via  a2004cbca71aef99c8f640c4d7f06944c63b4c7a (commit)
       via  49ba2cf8ac63246f389ab5e8ea3b3d081dba9adf (commit)
       via  423640029ecf6f138eb8b160fc40ca5afef44e4a (commit)
       via  52b36c921ee59ec69deefb6123cbdb1b91dc3bc7 (commit)
       via  b0a028bf381e5467acca8d66f37e778d569e331d (commit)
       via  46cd22e159df5387c036e285e8398e9f1320e2b4 (commit)
       via  ad40537d9f91fcf489d3e9a313a73385ed5fb241 (commit)
       via  6353f50a59f621f96a3ccf0003e5ecd527dcd07c (commit)
       via  d2661eddfcbca6532d6cbe18954d345ab8566345 (commit)
       via  a02f49d16d40c8b38514af00b62001225f9f7a6d (commit)
       via  774aee5f844849e58608ca86bec27e731c3eac5b (commit)
       via  194ef6477663b3c8671724bb6ab7b0bb13ae0b09 (commit)
       via  631e9f192cf33fb58c0a945ef57592fe7813eaa8 (commit)
       via  c6fc5883c41ed20f77ebaea543057521f76fe4d9 (commit)
       via  b3388dfb13dffdd653139673eceef34824eab072 (commit)
       via  774554f46b20ca5ec2ef6c6d5e608114f14e2102 (commit)
       via  91abb7aed95fb3cf15d25a971af2fa0b952e9570 (commit)
       via  e3f4b290d17a68db728166cdffcbe93517966e8b (commit)
       via  7d2867d02fc20295ae0622c987a25e3119d3ca7f (commit)
       via  f027496530a43f7b5eb5beb840266a9bc5aaffec (commit)
       via  0e9213727bb177ede9253fa8d0c1e1316487e33b (commit)
       via  5e5a33213b60d89e146cd5e47d65f3f9833a9297 (commit)
       via  4e80da3959ae51851ce68294bd59fed429977f4d (commit)
       via  d01a04e0f57552e78fe19beb99789888fecab7f4 (commit)
       via  deafd46a146bf83ea3af9076c9ec808cfd1c168b (commit)
       via  a7254a232bedb445f4c7dd47b8f623a94f7056b1 (commit)
       via  90df5f971e7988a7a024ce95fc83dd9dea3c9b6f (commit)
       via  0161c02ad4d593dc6a7ad5b352cf7db4e70b3b48 (commit)
       via  d98c6ee0863c784e79204242a3d868d4aedc3d5a (commit)
       via  6600a2c2981b2c37ab42c325e905d4a70a415342 (commit)
       via  bec26c6137c9b0a59a3a8ca0f55a17cfcb8a23de (commit)
       via  91071d03e1192378c50012f9e820674d891ed4af (commit)
       via  da7f88d1c170826babe93e93b1b32ade5a75cd7e (commit)
       via  f20a049cb0e6b29464acec69d26a46f26b7e2170 (commit)
       via  4fbbae439b8086e83e60f1708f40a83de61beaa8 (commit)
       via  bbeaed010f51dd88fc28743d4a9194c9d19c2421 (commit)
       via  012688738d646844e69080484a1fb92d11a4f693 (commit)
       via  b77baca56ffb1b9016698c00ae0a1496d603d197 (commit)
       via  46f994f729d0f554ad4f58abc82acbc7b526ffc5 (commit)
       via  22a8be53ada3f90b8aa226f08081d579b377784a (commit)
       via  5e70d900e46fb148acfebf8c2aa068159e58d91b (commit)
       via  ab560987671d76c85ce36a09bfc66cf5eb9398dd (commit)
       via  b91bb9719ff759417ce8f6412ca5f8be57a2e19c (commit)
       via  b41d5a89cc84139a08dc48de7ac08bf602d1c580 (commit)
       via  2c0a8fc3bfa0fb8f5f6cb2df504b326741996025 (commit)
       via  b204a39d5b9003f991104c2bd6896013f19a05d0 (commit)
       via  0f487a998063a01ea16e8be048076f53b11afd02 (commit)
       via  dfd2aeefef39f064183c84c23451637247e32399 (commit)
       via  1c57fb2350d9b8440ae4cf50a94be7c61cc462b0 (commit)
       via  8c55200c178773691ace2785240bfc65f4e351a9 (commit)
       via  ae40f56b7e12776479161c7d7a2d6616fae09850 (commit)
       via  dd8d9d4ab35db98d5269dd9c0728a5af6e748f1a (commit)
       via  868ecf071728924a23e3f0d3d8967cde5fbf8c30 (commit)
       via  475284be04f902de8e20e1756bbfeb74d7f63779 (commit)
       via  0eaac383a129e72f6a0d1236cbdf725520c63bf0 (commit)
       via  953d4bcf1e60b63785216f523ce5cd9929ced8e7 (commit)
       via  50795e21557b68a31f0c497f6de311d428b5d88a (commit)
       via  46f937392ece1d17476f44ca7d06869c28a3da72 (commit)
       via  3dae42840853b9ff8b4e580c794a29e8e2f30030 (commit)
       via  7713b147008a03d871a30d3072bdc1085f11b942 (commit)
       via  04e0ef7fe56b4c9c65b71bba010147af4506675d (commit)
       via  fda8ee0de5fc6416661134a261fb1a5a1569f93e (commit)
       via  43b5b823592d258002b741a5f88f9c3f60783c8a (commit)
       via  c5a9c69b1e925a9f3846f65f64f7f61077e44d3a (commit)
       via  3c89c290c7296591b773caf0c1a626bba40eb588 (commit)
       via  29e01b55a5acd72e651c2b2bd5cc63ffe8b21da8 (commit)
       via  1448f69f1c4e070f05502d67ae4297c248e7658a (commit)
       via  e00a4d69a403758e012f0f2924b40bc79e84e23d (commit)
       via  51b3f091d6a750ad4a65825c4f36bb7c05dddbcb (commit)
       via  652ae9e92f8c08cab84f603b8b6119afa23c8441 (commit)
       via  ba9dcb416a6170c8b4174f5353e3ff9b12a4bf66 (commit)
       via  c6d6f765fa9faf89c5159374f3ad60066e7e4ccc (commit)
       via  927397a7699daae124085d2ddbdfba9af5c06f8c (commit)
       via  54c1a257f7c93d1249f8bc7bdfb69d72c323d1d4 (commit)
       via  2482bf23634f5fd5db0037eec6799bf824a70a40 (commit)
       via  40d727bf4c011c28575fb099fa8370591afa4a2a (commit)
       via  d3e876aacdad6468ff7cba157f5ab31400b1a474 (commit)
       via  af816ab1e0c767c3ab001555d5882bf086b506e7 (commit)
       via  6f8e187c1bc8671be1e6da370f192d58eda3995c (commit)
       via  e70bd3ba8f491df4570c137a9d7561557b9f1574 (commit)
       via  a6d9e2bc94cb26c34521f3f793da354fa2cb1f9b (commit)
       via  4ad96ab57e9fbe8ce9c0bb4032f4c731d41d2371 (commit)
       via  847525d5ac7902c5eb90a5ecdaedf34ae3e73366 (commit)
       via  fd5a4de5485f23e9044965f7082b0078f4330113 (commit)
       via  826ac1a139f2c14e94d4b3477950daa02efc98ea (commit)
       via  c0f704fc0969565af020e1b51db5aaac63d68d52 (commit)
       via  5110dd59de80220e35c709850edd1ca8c311355c (commit)
       via  23f9c3670b544c5f8105958ff148aeba050bc1b4 (commit)
       via  a06071b1dc4e1e4d6c6977f099ea3c9673e96509 (commit)
       via  24b2851dc08da2e9d63a072c1c8b4550ef919996 (commit)
       via  e17fed2f2bf0061fe942cc8ae9c31f44f0d6e303 (commit)
       via  6784b5dbb0e599f370fb5965acf7038ab214b107 (commit)
       via  f2594b4779f799cfcc724999f4162f8e2860a8bc (commit)
       via  f6c1593309be9e02485a90788f02194ffe036d1c (commit)
       via  874cd35c928b9d2d0f53011a5d51fc92a8e9b95c (commit)
       via  33fc0787924af12936ef147c48f6140de0aeb9fd (commit)
       via  15aea6b5feb747f6e61ce5d7b2e7110c71c1ca7b (commit)
       via  766e74511e7c99d3d224e15b5f8a8afb5d154ea5 (commit)
       via  228fe4a17c5da83de7ea31735d2f283d0638d928 (commit)
       via  d576a1526ed1a9bc3275fa966768ce465692386a (commit)
       via  6ea0b1d62e7b8b6596209291aa6c8b34b8e73191 (commit)
       via  9c4fea2673759af91aa6dc3e9f10fe74e7275684 (commit)
       via  beb36d9729b675c3fccc8c6aafe406f7f7b7d221 (commit)
       via  d85eda60ea4f2304859ff935f998585dae2f4854 (commit)
       via  b09579f0af1644cd441ceca5deb2b6e1c40119bc (commit)
       via  4de96e9a3231ed79cb9d5549c00079df4dd7ceba (commit)
       via  181d405a1e606d4581d84b7fb110875c6561387a (commit)
       via  79af4f917ed615538c88ef4df43f8884a0d3e5bc (commit)
       via  ff926e066594778c68772d77c3ecfe271fe3079c (commit)
       via  0acc164c6a3be5eb97ba3963264bdfa5211d6b9c (commit)
       via  c96ac865ac20c4e80b3206a00c15fa998cb85bfd (commit)
       via  da4d1f0f461a2ffd46fd6d09b78d4bce47df6816 (commit)
       via  3dfd3b25e2ed142ce86126c411e78194d890679f (commit)
       via  7b7252d0442b7cc618094784d1b243d09309791f (commit)
       via  e0cee3754d5c1e2654223bb87e88567ad8befdce (commit)
       via  538350b7db14e063f716b040a5b0f0ca2aa35278 (commit)
       via  bd2da89767c713fab31eb24cf5840cfe9b925d64 (commit)
       via  7e7b44723b80637d14bbbb714b69fe4ef7ea1f9a (commit)
       via  8db9dee3621661108c12faedf26bfaf9381c13bb (commit)
       via  02cb1de4a0e0d3c12bc29aa7686a9015dadc637d (commit)
       via  2e06048a76a016e90baef2d7a6b218457f97e3a1 (commit)
       via  438bcd6747bf177dfd6355c77c06d28560a1369a (commit)
       via  21c1954e0e26413c1110f68d2b7c730ae79c3d44 (commit)
       via  abd717cfc9b0b01bfe6a5d651a2d34ffaf61d055 (commit)
       via  c2b8d8433c9a4ec5fb6817a104b6a6309d413f7e (commit)
       via  37960ee83cb33be5cd683fb0d458343752caa01a (commit)
       via  8f36290ceaae7abb6ee5b5affdf0c7bb31e5bea5 (commit)
       via  e80de6f13a404687f4ed896e548fc614dc953e6e (commit)
       via  823f30307bae21675418262a1b423b3bfbbdca93 (commit)
       via  8e4c96040527d952da60338f7cf061f976780543 (commit)
       via  b713d234a982a804aa58839ef17ea5bb54fdf017 (commit)
       via  3285353a660e881ec2b645e1bc10d94e5020f357 (commit)
       via  f46b788569366fefbfaaf6ec0d57bf1861b1b2ba (commit)
       via  20e1bd7772429ac6d6fc2ec993da36985ce75984 (commit)
       via  c4e3eaef7ed29851b7cf3f0714043cfbee6f5852 (commit)
       via  c8c69b2a7b47c67655985c506a6467acbc471a05 (commit)
       via  ce65bd4fa0bc59f262277d7bedb2a649becd1fb8 (commit)
       via  489ef518a4a386d7c86534f15db8621210c8ce9d (commit)
       via  a7f2ef92fd007ffc44e2e2572d0390efe8fcf901 (commit)
       via  e0c470b595d05c8c075732bd090fcb02b2af996e (commit)
       via  69d99618a538d70a3626a2aa403b9aa5d0977b03 (commit)
       via  fde67b3671c8bbb24f12dce63cc14fd8ffc599a4 (commit)
       via  c387dc0811456b30f4c4e12a9973e246dab385e1 (commit)
       via  154021bfa4d4e8f1cd6bf701fd3f50ea1d12665c (commit)
       via  3441ed2e58f5b467928ca1d3145e158144674644 (commit)
       via  582bcd66dbd8d39f48aef952902f797260280637 (commit)
       via  5c61cd71a9423cf90166c057512f6cc168adcfb8 (commit)
       via  4065c72003e29f7ceec60c3551dcae6f6a73b1f5 (commit)
       via  65d2a83510c3abf2f2cc3fb082ce56c99be32dfb (commit)
       via  b24200598f331f9b46805e04b92c23dd7018f2d4 (commit)
       via  b28f331a0299014773151ec3bd652f3e59557e5d (commit)
       via  b842ace68bb3cebe88c05c72c11dd25d5ebcf9d3 (commit)
       via  af8054f481486c2d9ff364ef8b97aa3aed97958e (commit)
       via  5a75cef501889781df4a90919bf48873c7f00ced (commit)
       via  c0eb91022adc9d00b60902c6345a9ae8b319b856 (commit)
       via  0f1e080b028eb0ef89f88ba41ffc8e701888a8f6 (commit)
       via  9a25a7d76337270c5a555bd98cba95a2fb7d7295 (commit)
       via  c71fd9915c82c618d865780815bfec5aaf6c19a0 (commit)
       via  2bb6957f057d02e70338d1c8586d53bbda7f7d33 (commit)
       via  8e3c36663ec9ee59b710e97a1f6716fb59df1d51 (commit)
       via  12f57073eb9f5ff5d11035852e253d25071debab (commit)
       via  61992b3bdb4d128971e459da4e6d741a48ad01d1 (commit)
       via  d05c01185756886ce13c49610a813eeb4288a04f (commit)
       via  1f945d96f610d2961699f0f7227b82600df7f06e (commit)
       via  579ba09beadc10c63ff523ccab6a21f112f364c5 (commit)
       via  6fde5a92a9e7610e4bd6612221239fe686c3c004 (commit)
       via  373eda27fc0e591356a7311e2ad560bb9441e64f (commit)
       via  09d97fb51ee5d9d00077a52b3678f94c158a365b (commit)
       via  50a11ee4e494c9a57d43cdfb2af953d6c8565e47 (commit)
       via  1303709492895c08fac466f75069a2cafafb1de5 (commit)
       via  94f99cad8539239a93877a1774fbadf89238c181 (commit)
       via  3ca2c002d807fa1f8e7dbf6731ac4f1796f4e5a0 (commit)
       via  572cd0d4d2866708af03a0413cac704b98e5538a (commit)
       via  77ab7a1390765dead65be7b3a75bfafdee1f6abc (commit)
       via  3d8ac71299d439906a11820e4ef914eb18b9d1d3 (commit)
       via  1e62bf975ba54ebee2e5ddc2a2cc26ca5c254f44 (commit)
       via  1ddc5eccbe9fc1784a3447c38f708a389a45d83f (commit)
       via  9c8dd4effe803bedeb2660c30f72a3ac4ecb308d (commit)
       via  5852324e1ad021881339e6ef4cb62611bdfc1b5a (commit)
       via  5845ab9615b27fe8efa38b38622fd12a42350e04 (commit)
       via  0bb258f8610620191d75cfd5d2308b6fc558c280 (commit)
       via  bc67fb7ccb6d655fd46272c1dfd89a0e58677035 (commit)
       via  113f736e002db510897feb1e6e55600d622f9d26 (commit)
       via  c6bb0438548d5a7eaa6b2e7cc27f801f7c156cd8 (commit)
       via  aa72029e21dc74db7eb7a354ef9f921227e53a6b (commit)
       via  941381b6afa208e485a1de772783029b3ed04abe (commit)
       via  5c681eddd0ab1819dc7fadd692cadf98991a4d64 (commit)
       via  8173e046080fe51963a37ba89107a8871a0b5353 (commit)
       via  758d1d155f95a65702969866454ccfdfd90a7336 (commit)
       via  ddf22289ac033aa2ffdaf8e348c4a05f2d1d6951 (commit)
       via  b35f3264ece9358619c3831aa6fe6bf74a14c8c9 (commit)
       via  e9a7f0106db4f30af9f64e663d39db262850f153 (commit)
       via  8b2c30923f169c6b747158740a85be6d68a1d05c (commit)
       via  699068ef1467867fbbb86cfebc20e823c5128100 (commit)
       via  69aae7b1a831c5393785759efa3bcdcd238f599c (commit)
       via  28ec5cffac56ead6c2c79ffd31427c46dde1b061 (commit)
       via  06d963e3098ea6ead0eae1b54f178222b9fff479 (commit)
       via  ac2c63f07c78ba94fb8b76ce1051a5810b97a99a (commit)
       via  02db7759b655b9eb3e5d4e750e5f5bf212e1157f (commit)
       via  1007c99c026d5d7f51a95061d25f59d621441b39 (commit)
       via  216b2e18c9811ec25dc99ae9b320140033e26a36 (commit)
       via  64eef65d27bf5310afa53cac418c18bc8a989b31 (commit)
       via  1af6565d9d5f789c82ececbf83ca725cf3208b3f (commit)
       via  1c1127916ee5c550c729d3d128c1b6bad100cab9 (commit)
       via  83474d8f558bebb4be0b0f75e843fab993ee3713 (commit)
       via  961f28cdfb0f5cc1f5be9613d5f60a7801c92b0a (commit)
       via  20fb475bd5cf9051c6f92163e2c427bb782db60b (commit)
       via  78efcc84de61e1a4aa6715ed29a4343c5482dae1 (commit)
       via  adb839e3c26c64dc820508f5befe61f806d97fa2 (commit)
       via  1b4f658815fa755130791c8a668dd5aa423cb48f (commit)
       via  9c15821896107f88730d33d364b1bc0aa82dc5f2 (commit)
       via  cf5904a4ad2685c8923f4fbee1987132595908db (commit)
       via  3d402d65a8aa0f271190b40f5e141c4e5ee7b31d (commit)
       via  0b1b0da6ffc60991c0ccad85695631dec02db4da (commit)
       via  55d44fc94c8e0ddae7b9b383e7e29b9514ce23ff (commit)
       via  b8d64503f7fcbf832c4ca6bdd0dbd55678baf88c (commit)
       via  9a2a86f3f47b60ff017ce1a040941d0c145cfe16 (commit)
       via  896dd6912cefe2190fc12f58b2ce291f17599b8e (commit)
       via  795efe06b737662b662ad549b1afa881537790df (commit)
       via  a8c1ced88d2e49827f5082555e335e209fd32ef8 (commit)
       via  2cc4d39e5c74092c2fda2d9be3682b432243099b (commit)
       via  ae873233be8b119b0495b98d843fada53949ab58 (commit)
       via  62a3bb1472df7765c40525dfe9c07ab949fa4a72 (commit)
       via  3422adaa26b0e1c24d6e230694975a4986cb0965 (commit)
       via  812cea250215b93bd4567873910e2449f5b559ec (commit)
       via  fec011eb0defae05d4f0803467c841d2f27aee29 (commit)
       via  a8e53be7039ad50d8587c0972244029ff3533b6e (commit)
       via  a167e7085865d77a1a9311a5cfae067da988d5ac (commit)
       via  292fc936f6bfbcf84e56e67b52a16368a726d32f (commit)
       via  ede52d8098375352ae87a124619ad78f01fa2e28 (commit)
       via  4dbcf3b666bf27ae2c3018007e163235e5c326e2 (commit)
       via  b469e6036af3a8d4b12dded6597ea08abb2196bb (commit)
       via  869ed54d546cbc8d019993e3c3b81b8a14dd0128 (commit)
       via  5cdde563b1ff647a83729c768f688188ac90bfd4 (commit)
       via  5a045dfb928e3a2d5702bea2e803a6f6ac5b030d (commit)
       via  3d8de45a64068a289bc8eebca3b21c308047cdc8 (commit)
       via  ec745dbffae8c60801738225ecb3c71b9d758091 (commit)
       via  cebaf1622cb9e73257b9876bfe863a3102803766 (commit)
       via  9ef6f17654ebfc0f103f8563a21db07dd141806f (commit)
       via  8cff3a658f4c21b8e83042e475491dbdcd404983 (commit)
       via  8a0d8d026e4489913560819c438299560db77724 (commit)
       via  abca9b6b8de3fcb0c6d99df48793eb975aea0e5e (commit)
       via  ea5ac57d508a17611cfae9d9ea1c238f59d52c51 (commit)
       via  75b062049db49cade952510e66324761a8ba09b6 (commit)
       via  38278a2b5a74a76708d89862f8e9eee56bd84ab1 (commit)
       via  bf8e9e937a158253853013a6f316573d3b53e3ee (commit)
       via  a2d27b3d05763fa267d6f19cff7468ac86063a38 (commit)
       via  0f0ab5b692e5670b0ff6774d0003a5f676a79dcc (commit)
       via  b87b288e07b97629fa80b36b0cfa0833727b785a (commit)
       via  a979801041c9a650f2b24a4c39498df80d72b75a (commit)
       via  97bca3e1ce5cb69be9fb6fcbc9bf976fb05e3a2d (commit)
       via  27ec89e237865a6f9d92639256291acade7af69f (commit)
       via  004375eb0955a2eb1fbfa5d5988cdc5b10ae441f (commit)
       via  6c506ce59394354abe02a650579ec0517867a3a4 (commit)
       via  8efd8d022529b4d5178329bee08a6909d5823c04 (commit)
       via  f64dbc690b23b7354c8c67ca0add3b2ae07fcd0c (commit)
       via  d4935b95254646ab389e43517bf3cf61373d23e0 (commit)
       via  08ac200f2f3e15548a81b4c252b72fc8ac72edbe (commit)
       via  b78153aae59d37e4c2807c4d19c4b548be13adc0 (commit)
       via  4bb8254e151217f9ac5c29743d57b239c8f11d1c (commit)
       via  0b55e11e55f2422e2aa1f920f48debac92d826af (commit)
       via  c3962c4f4d5f08e3ff194132a6308dbede212997 (commit)
       via  91a89cb8cac804441d957430593c43f40645a44d (commit)
       via  8123c01dbd4f71cdff0ba73a45343ff51de0908e (commit)
       via  914212e06d69ae4e24ac673b050dd60a7eadfbae (commit)
       via  b8ccce76cae1fd13b07b0c4ce155a5d95d76de12 (commit)
       via  5043998b29c17dd31a06cdfa9bcd73eb2832127b (commit)
       via  6b1caed1a4161932e68ef52f067d07d822d85c94 (commit)
       via  2b01d944b6a137f95d47673ea8367315289c205d (commit)
       via  c0c59aceac2afeec9ae3e2711b21e177046b9db3 (commit)
       via  460facf264d8a653a959013ce6d7c25875695bc3 (commit)
       via  ee3042d8a998de0a2a272003f53957a1af500901 (commit)
       via  b1fb64710cddf53857625d7a871f82775e20d93b (commit)
       via  6e7eee31fa6b4884490778ce4148eb346fa4bf28 (commit)
       via  cd8b0868f1b4bd63f4bc9b661f0afca448e57797 (commit)
       via  c3374ed01863bf7643cddd90f4ef47d338df6225 (commit)
       via  1389eb8fa945be3ffd35d0daff3bcbf17da15549 (commit)
       via  55fdccd716864e9f33675546d3b6fcce18027473 (commit)
       via  5434c74a0bf1a4435af9879572143a7b9574f713 (commit)
       via  eabc47cf4ee5ff9e3cc92a2e2b7c5b9481fa7bca (commit)
       via  2b13ceb6879f7c02662a0973d5c37333b3b3d660 (commit)
       via  84a7f88f41fd49749deda05aaef7e978bd0fb90f (commit)
       via  dd95b7f18281fd4f06d5f13b5a99ca9e083f774e (commit)
       via  62411740bf363a984e453df37b95bbbdebc443e4 (commit)
       via  79b80617cf3f9c979b5f541d1cb82274631f5fdc (commit)
       via  ae7b2a915adb90a32070ab3cca4f589b449ed435 (commit)
       via  4f93a3a8366f971078dc9c8fe4c846c447a7a45d (commit)
       via  af7d66f37a3784fae54021afeba046383984ffc9 (commit)
       via  70c188de44d2705f6944e37c3e1513572fb5d3d9 (commit)
       via  3e344c14a192be56666978933dd4ca0af3cd6a86 (commit)
       via  485f7966f583f54cf2694f054de6accea9c19364 (commit)
       via  0c1908c1225e8933ac6f2e093f63ef12d3dfc6ee (commit)
       via  71032a09e3cc7e34133378b426ad83d13ca3e0ac (commit)
       via  42991798446e79e8e5e60641430d3d5a8cc0c1bd (commit)
       via  fe8e4b87d5b493fa2cf41f1e8dce1b5809c27679 (commit)
       via  99c9c5e589d0359c115265864302b8dfefb210db (commit)
       via  ead42e5f5189b6fbd72823d6931eddff10409d9a (commit)
       via  3adabc6a1ece55ee01bc5b0d6aee8aebca2510d2 (commit)
       via  1cd0d0e4fc9324bbe7f8593478e2396d06337b1e (commit)
       via  1030e6751920c754fa9d02360ef95342541b85b6 (commit)
       via  638db2f91b681d050081a15e701011630c421f06 (commit)
       via  14d4b5dd3ef4cb9cee473e70aaeaa5d63dac6113 (commit)
       via  321b22346c57951b3e9758b471c4a1c95f5bb5be (commit)
       via  abfbe328e4a378f63a4eb5b3fb95949a38d21557 (commit)
       via  2e940ea65d5b9f371c26352afd9e66719c38a6b9 (commit)
       via  4fd6efcde0f9cb8d7d1513530324a389e32a978d (commit)
       via  4a82ece4c4e353ab8e3ceb01fd8e0f4824ac6bbf (commit)
       via  b8960ab85c717fe70ad282e0052ac0858c5b57f7 (commit)
       via  f537c7e12fb7b25801408f93132ed33410edae76 (commit)
       via  737c22c1d763e638bd958a5507bb2012267c8a22 (commit)
       via  bfe0f613e15f8c732462f43677f846610e496e08 (commit)
       via  4e7ace34dbde7f41d474fc93a0bee27361b9651d (commit)
       via  21a0d06de02275f51776d1177c7b64ababc2489a (commit)
       via  81d738596851cc2f0d4e475c9c26e7e41c8bc1f7 (commit)
       via  d7fb4b7244e60a034e21873d5bd32f7148ccd973 (commit)
       via  d9ae23e5bc7ef47d29953bf54f2735d8f6f5f531 (commit)
       via  5e17c750847213e462b9ca7537358d3d97975f61 (commit)
       via  736fb87e53b0d57241c4e414ed5de2f3eacc6e6e (commit)
       via  64caf0ab35936f4df289032579783a63a5f26fee (commit)
       via  4cee65dd891d8dac00680084680fa57d89bea82d (commit)
       via  7eda3c0ee0795ed21cb4fb44ff20f905de1cf800 (commit)
       via  beed5d700bd2ef38453f3a60e1bc2d4785c3bc68 (commit)
       via  f0b94ec2fb650ddf64d8d51dabb427056465747a (commit)
       via  1296fe25ed708996f9aca9d3ae063dd80d444d9d (commit)
       via  ba84010ff254a3159eaac71db89a6c128d163736 (commit)
       via  d31372d7b6961c777c325350a4f415988a567715 (commit)
       via  2903127df129af292b510a1c423e274ddc91ed2b (commit)
       via  5a8474bf5e3b8e10959c76b573d3f0e1af03ebf1 (commit)
       via  cdbcf6b6134197bb28a3ee1c5460e78f9e89cdb5 (commit)
       via  d5ec40dace9fddaaec9873cfca2d670e8d35650a (commit)
       via  56824afc873d66a300f2dbde46e906c163e2d492 (commit)
       via  fc609f2e7f47c724e2f7d713f4c3c41616751e76 (commit)
       via  681c927f0d8150bbf3f71fa8c689b7712b834aa7 (commit)
       via  03f1a5ea8f070b8b92704fc780cb858f6af04da8 (commit)
       via  bfb21aba64298f219218cf58ae9b6024c1ed7005 (commit)
       via  516e15b88a729bc28ec04ee2713aa6b352f38875 (commit)
       via  510292e639de813a6f8b02ee3e36726fae1da5d8 (commit)
       via  717c10a44cc2b1826933a51bb17868f7456e686d (commit)
       via  a0a39d4b6e67fe575206ae191ddadc2f6f29e35c (commit)
       via  cb40afcbf13f2f456f5e671c755db9457c04e012 (commit)
       via  97fa3578b37fce3897f78bfd59990ec9928822b9 (commit)
       via  b5740c6b3962a55e46325b3c8b14c9d64cf0d845 (commit)
       via  bc65585535ce775f6a5cf8dae25dfbda98a489ae (commit)
       via  a2a9e24d6a5744ab6b916c8e94ab0b77137417f2 (commit)
       via  b3933db8909e3040dec00fffd670b0963e264506 (commit)
       via  b587cb1fb68c3392af63abfdb4c91055dab6060f (commit)
       via  95af6c2e54fb45fa5af1a2d3650376ccadd32c7b (commit)
       via  dc0963ad902d475f08d4f1bebab4e5114c013922 (commit)
       via  d08e8c4fca2bdcbe3fd573c1872c8e9347a71fb0 (commit)
       via  2730ac6f20e6512c9c72cf22e0fe223f9bf66b09 (commit)
       via  c12cdc5ffe704bca024a84223774871e5049f90f (commit)
       via  13fe9c1bdcc2e0a1c8947a328429e6f301c6fb3e (commit)
       via  55b948ebbc47f5ab7a66eff50083921d9eb740bf (commit)
       via  d908fc0e44923c9e9196e137f4b8cc6a1a285568 (commit)
       via  5b7155e4ac83a12410038ea4bcba080689378d73 (commit)
       via  ba1bfc79c7079e9c0886d542229c917d305695d9 (commit)
       via  b33929ed5df7c8f482d095e96e667d4a03180c78 (commit)
       via  cb28f3c4199610fb06c8939f5b5d95b99ae7a071 (commit)
       via  64611b969652b074976c72867bc78ca43f8e51be (commit)
       via  6f31530ff7e74757980eb2d01b82852636991672 (commit)
       via  29fab3811a8bbae6bc28198f7aec4378b338a03c (commit)
       via  1633572f050616e4fc41502fa2bcbfd70ea594ef (commit)
       via  70778ebed8f5d0af186460600facc563865bafe8 (commit)
       via  ef1eba02e4cf550e48e7318702cff6d67c1ec82e (commit)
       via  184bd43c0451a18f9d7d792050c9089f8d329a1c (commit)
       via  b2e7f7e47b9ac2dc93ca57c63635255a9a50fbe7 (commit)
       via  de4b10ee8d53f5c9537ba98ad401f84d008efd69 (commit)
       via  9a5e82f81a296fb59dfe75c7f47fe91471fcb13c (commit)
       via  1d3d7c590668be4503e70c2d3f2ee1da955874ac (commit)
       via  3e88cb09890b3d05c82c56be350b1b76325dff15 (commit)
       via  ac299353640a32e77ad0e3f630d1a6bdbfdbbb06 (commit)
       via  118f1f9d41fed38bb12d46f41d50dd2fd7367a80 (commit)
       via  ff863e03a8cbdb0c971a101af01b604dc13d1457 (commit)
       via  ee3e3d95c2554eed560ae6fea8f24864d0f32074 (commit)
       via  d2e2cb97e821844dc430ef1e47d13d618c06d9fb (commit)
       via  6b3dabe34a4fafac4a91fd0b953b49dfb846b713 (commit)
       via  4bcec5ffb6b84ccc0e3b6598567d1e0b67b95976 (commit)
       via  9e89c7638a259fd103316f2b7f9be539b2cf3dce (commit)
       via  1202e2c13c17c4e3013c635f9e4ef8629c9c7b69 (commit)
       via  eddbaf44858226fafadfad360eba55a79f69f085 (commit)
       via  56f14dee951465a712f2fee1d4dcf36d7b87a4b7 (commit)
       via  ea85b4abd01332057c8a8a50e729baf444baaaca (commit)
       via  d0111f488addd8cf0f4a7217ce09a91e68cc04cc (commit)
       via  d93639f9d862eee755aecbde69e37f40543f7109 (commit)
       via  48b2af87d906f6280ab477950362e76f73b23482 (commit)
       via  c271fcc053b0ff9b2e7273e992591549258c18b7 (commit)
       via  03f679c989087d9d4a5e403bf37b2c99ad7c5ecd (commit)
       via  dd519159e091b7cc977b47692a45800157ed65ea (commit)
       via  103b3ba8ad4891afa8f808fd9b1187983737a0b6 (commit)
       via  567c9411dc8574817c4155ac1215a2a8f08fd192 (commit)
       via  9c4d0cadf4adc802cc41a2610dc2c30b25aad728 (commit)
       via  a74802e8424747421511511f78a344de22105c1d (commit)
       via  fbe90b8b726e59ec1cddd5338850c7f744340759 (commit)
       via  bb4cc9928f968cd9e72503116a83c1fbe8f07361 (commit)
       via  fa862e7d1208c8cd0526b9512b9a1979366252ba (commit)
       via  9cabc799f2bf9a3579dae7f1f5d5467c8bb1aa40 (commit)
       via  75ff883128aa4a2e4bd003342a2d84c216418e00 (commit)
       via  3e173f46ee0d807f752ffb88d908c7ca601d0c3a (commit)
       via  548aac884cccb2c18420f3a14d1736a904c096c6 (commit)
       via  e85922d37b1f4b43db76b41286268af4d8649288 (commit)
       via  c0a44c70c4a2e0dce3792bbb7f9ca15938128633 (commit)
       via  5900df9c41a9ab0837cd25667cf6a371bd7e850f (commit)
       via  f88766fd3085974acaa0c3e7930f8295374a8467 (commit)
       via  dbfaad4a2ce1f682267373b323a293e365bc4cc0 (commit)
       via  fbc79ee7d2f1a79f6388939beccaaf50dde0865e (commit)
       via  e5d53f4bb27024759f8712af254998c9dfa48f7a (commit)
       via  11b5709d5a2e18ed0f83c291ec684a46d94609ff (commit)
       via  055622f347c9e5beb43a5145c82a4f4a54e89d4b (commit)
       via  a9d6dd7cab00acd5379309d94ecb8b088ca3d295 (commit)
       via  a756c801315d320a855f312da4cad370e52371e3 (commit)
       via  e202628c6950839aaa6d318ea7a08b4f596499c0 (commit)
       via  776afd136b2675b928605368199af7dc130607d1 (commit)
       via  7b92c815b458b3fb4011fede59b787391bec7058 (commit)
       via  a7f978695cb84f9be3dd4ca4ff819c85d4477746 (commit)
       via  4724623b5fd7748dac9fd291e6661ab94780fbb6 (commit)
       via  36601beb98e8f746156ec430cc54ee9f03db2fe7 (commit)
       via  c257c2e0301a0605bd038e52e4df72734ccd92dc (commit)
       via  62c19c0fdfd14b00dc515852731211f951c75201 (commit)
       via  39b898c33b05f9936f9c822dfd0f90bfa2089369 (commit)
       via  2d0b7b1cbf42fd7a92e09b8b3d37752829ce65b3 (commit)
       via  f6bd9f9c413e93070c2eddc6d84d6e77dd789454 (commit)
       via  045edb53054c1e4acd985430f6b21b605348e959 (commit)
       via  ed3ab3da850807c3df2a6fc991702086bc65c008 (commit)
       via  c983a484207a8af30fbe90dcf2b2b4078ae8ff09 (commit)
       via  1c1308a191d8e151abb28acbb22db033764acb67 (commit)
       via  107995cf3a709e71baab30a82eafa6d7850750fc (commit)
       via  abb524e9809b09307c95c82a09d0d57bea652401 (commit)
       via  6ebbb5d27d5a52d5f5e5caf89d3514a5135f07c7 (commit)
       via  24347dec2bc47e3112a2b8103a8570ffc4a8ade5 (commit)
       via  b98523c1260637cb33436964dc18e9763622a242 (commit)
       via  3b53272a6f77fa03e34f298ec06ec31de7cd32e1 (commit)
       via  c9f943875b76aba6204609dcd70eac4a5fb60b4e (commit)
       via  3f6b00419fb5d447a4e2e84be58397632a716abe (commit)
       via  afa40133222b39078a914b0bf5780be013b8b992 (commit)
       via  b6c08e3ac581afd21e033808bdcf7c96f83f0ef4 (commit)
       via  c2d13346d9c16ba018a4f154c6862c3f9b208a82 (commit)
       via  c8fe7f2e869938bee1b3283c55c99f9487b6995b (commit)
       via  b5ae5d8fe31c721fe63f6cfa7ef880cf27ffbb06 (commit)
       via  bdb2771f51482995116460fb6594ba69b13e725e (commit)
       via  8684a411d7718a71ad9fb616f56b26436c4f03e5 (commit)
       via  555c57669226bca31e961412fde174f04db876ab (commit)
       via  04f9d8ebe54788a4f4f3aa7be7d50347a26565c0 (commit)
       via  90e30a79b4fdfe52a70fbc72f9f84a8dd1968506 (commit)
       via  bb52e879ee7b68c162b5ad36648b9f2c0fc18f19 (commit)
       via  d125d7da8b990b238993f59353e8a11832785706 (commit)
       via  3b8892835a95f7b17f93f5f3ca36c14da1a82363 (commit)
       via  ff013364643f9bfa736b2d23fec39ac35872d6ad (commit)
       via  c5ba45e293726f7642f6e081de16bc3b2daff98b (commit)
       via  280742f34c2471024aebdf2736edf5570f1f3da2 (commit)
       via  79e99b7285b93325862f80746d3a4d1b5938ca4d (commit)
       via  5a7953933a49a0ddd4ee1feaddc908cd2285522d (commit)
       via  064dd51b725493270dec9f1cd2c2b7164135ee6b (commit)
       via  d2b5da0a8146343bd6f72f3a145fc90be7888d18 (commit)
       via  56b495e4bd8aeffed8642bfb6bb4802ba39a6277 (commit)
       via  429c88cfe0483e0c67f4625481995c2510485792 (commit)
       via  ffd2c98603eb6db553d4cf18d2fd7ac29a62080f (commit)
       via  16b39f3c2422a324963b67e136b07619209f869e (commit)
       via  d0fc36b1522db78541bbcc560f5f05cbefd4c5bc (commit)
       via  5ba4c6e1f349f890200322b2e2d2ff0e5b179944 (commit)
       via  fceb2a853bf88760d5d5dec2a3e6f169799415ea (commit)
       via  30d7686cb6e2fa64866c983e0cfb7b8fabedc7a2 (commit)
       via  04aa12f01ec871e625fdb8ee1a07c387ab0a8f2a (commit)
       via  4d472746171f67ac492234ef46a3bba43f55d5de (commit)
       via  cf00216570a36d2e3e688b197deea781bfbe7d8d (commit)
       via  e4b2c2633ebb3859286e9a4c19e97e17bcac41b3 (commit)
       via  54c6617096e184520c918d308ccb31eb422046ec (commit)
       via  883ef5b0d7466d080916f98af4ea6a94258ca655 (commit)
       via  82bb5bc1cd3385d1bd0362c10308afb04a0e6914 (commit)
       via  b995540a1bd00fab2ca883c965edc954080be84c (commit)
       via  38edb7e80589f08524b2753a7f29f1b6570ef4d6 (commit)
       via  6f771b28eea25c693fe93a0e2379af924464a562 (commit)
       via  389ceb4af859b59d18db14ef25a2bd3c2dd3ddd7 (commit)
       via  d18c04987a47c89aa3038d2bb0e99aa40b2f4e51 (commit)
       via  dad96d1c2aac267f2a11b9bdb7c83ce33c1a3f34 (commit)
       via  dbdf5296c3e98beb234ea1a161b004bed5e17a8a (commit)
       via  8f5187d167219ac263ef940eb33923ef8a86e87b (commit)
       via  6b3f5f252791b1c4b92589deaa3810e5e1eb867f (commit)
       via  a72ef46cca91d1718b2830e9d827040e6742d643 (commit)
       via  a11e3b2165821e9ca039196b12d70025845a5d22 (commit)
       via  b0d241f85f89f1d352f9b7a521b24d80107ffc20 (commit)
       via  fcbe40140a5e21ecf3ff078206afe1d68e945462 (commit)
       via  07dd87ca1a14058c926497ec1c6ac61ffd3a41f2 (commit)
       via  4085d32d3df744aaa7fe333032f978edbfc293c3 (commit)
       via  462e2807bed92a21e8e2c070e49bab118323de6b (commit)
       via  89f1ae8c2f2bb8829ae249414e9d8464b74b54be (commit)
       via  a9d3e3d62464b76c467c5cd8c0c229a75b5df5fc (commit)
       via  fb9060e5dccc2bd43ede4da3e0fe0733aa9cc6fa (commit)
       via  0169ac7327f43e82979102b0fbd87bf004dece16 (commit)
       via  464cada643779ceff4b3886aa5ccfec6605e2e92 (commit)
       via  73b4d32109e8103c50f73dcd0c171930c3d9b322 (commit)
       via  cb1ca47492ba2c493f88a45725f91d1b53251023 (commit)
       via  9d56a4097505b05e6a8e69ebb967a30416e39b41 (commit)
       via  324be1acbdb7d432f4204d9cd6ed28e71a4fe7cf (commit)
       via  6ca1f0da6b1e7dc488e5ff57b9ef041f4322ab67 (commit)
       via  766508d03d1304dc4d66b814a7a21d160b3404c2 (commit)
       via  a4789bb27d209a54652bc2d7c6bf0992613c18df (commit)
       via  bc4fc1865977c94d4ad6eddf0cf96a7881a71536 (commit)
       via  f43af5ae2a1a904d1fa091d227f6d5cb1c580fa3 (commit)
       via  5339fd0dc7b0a00f2e80754e7a1aed146440cff9 (commit)
       via  ccc2fb769ced7cef416b55d9074591022b8a673b (commit)
       via  69360a12e78e7567f88dcd3079c9a56be534ea7d (commit)
       via  38fdfa7720fd8e4c07aeb6aad4b7ac492d364b88 (commit)
       via  71898b1a575fdefa01c657290b99ce7ba4693054 (commit)
       via  920106980b5a66212397f5f699f2aac9e6d69cbc (commit)
       via  c430386cb0a163ffabc477d864635b4a21f2ef59 (commit)
       via  900180e27ecac721a7ea7a5c3cff62230082a391 (commit)
       via  6ec73d5de3e72a8d6adbb7e3bc353b68584b344f (commit)
       via  d6d42bcdfcc80b450b89937ffd5ba39b071b3f37 (commit)
       via  0c0161c6e555e2c8b33b3291043727bc07324dc2 (commit)
       via  6dc12fe4b4f751a8692bb2f577d178de458cbe31 (commit)
       via  c8f1422e69ad8cce8538c4f25a92ff225b4c2bb8 (commit)
       via  e07c5a1459d94b1c7c24debdbc05f23a5c79b4f0 (commit)
       via  ce39e53bff7967573672e7172ec9d18b8bac4bab (commit)
       via  f161f37c16c1ce2b84ca467b6f6302e0d7d7a462 (commit)
       via  9e60ca625be2c1875e65e360fd2b06c9ee8c4a57 (commit)
       via  d1f64edacac52f7016b36381d93bec0275dde2cb (commit)
       via  f6d6f8d947ffe15b7aa0d1fe73adea1a1963f774 (commit)
       via  7ac5487d208f8b4cb9c3e2955bd7d1f03b2ad942 (commit)
       via  d7dc63b8a899e4e4c1be30e6f24ad113e02c6582 (commit)
       via  bd0a50e9b37f2e8c0030e905ef1889729df3ad5c (commit)
       via  51146871aa0303636eff16193b66f61c80b9b7f8 (commit)
       via  2ec4a397f113081c96807b3138549bd4bf1e2edf (commit)
       via  5b301ea5a3aa71a5f82f0924713a7360c5f32f35 (commit)
       via  d121f511463ae2c469ec5ddeac1eb1ad60ca4cd9 (commit)
       via  18eda2228413b1bd5764ad23dd5ab45a2d9c1809 (commit)
       via  eed7f24a8ee8ac1647a2998e38ce4766e48adb72 (commit)
       via  19ba70c7cc3da462c70e8c4f74b321b8daad0100 (commit)
       via  ecd7dbe0f231c04d19eba3cd02ff513a23a9003a (commit)
       via  bcf5e635365f60d79e5b7d28b9280482fbe1529a (commit)
       via  7626330333e85f0f0e3d63530681b3708932e7a5 (commit)
       via  f472a687b70982f906bcc96900fdbc3ee8d36ff6 (commit)
       via  aa0318899f62fc3069572bb18bc6dd071bb8448d (commit)
       via  5758984e174f25954af41dcf07979e2dfca12763 (commit)
       via  11fc8fbb59146320ba5ce8a86d08f937878250b3 (commit)
       via  5f4eb187db9895ceb2297711b6eef6fdcc520625 (commit)
       via  0255bfb9925710dc04d1bf88ad138c33ed7e7530 (commit)
       via  4fea1ab53d06b5deaa87def247818f67839a9c9e (commit)
       via  719a5941f529e8139cc2cf970d8903adf0741043 (commit)
       via  ac91cd885b563c71193cca2a33d6d11aa4a25927 (commit)
       via  73506bcbd64a043f7e66c193a15b2d09a0a47bf0 (commit)
       via  4006d91c9555c8dffab444aaa6e83f523ad5b338 (commit)
       via  526f8713fb6e13456f7008b5cd075f6fbdb89161 (commit)
       via  f8cecc0e0625c40be6a44f24b8bcd0420e19946b (commit)
       via  c540444d6220133977db22de4c0adc116afd24a6 (commit)
       via  66d14dd2dff194e69a12db0c89176c399ac5ce0b (commit)
       via  30f55bf6510a4463d511a90c0b7deaeb046f80f2 (commit)
       via  38742cb1561683410ab0f61388dbccd57e9f0f3c (commit)
       via  451fd576615b47baa616d49ccaccedfae5595a76 (commit)
       via  a265a99016e8fe7943e1b6e8d4f2a69c0b5fb391 (commit)
       via  6129c3537cc81af4561269d5652321ac2250ab93 (commit)
       via  097a376ed65be53dbfe4cd1f2f46df288fb9d6ae (commit)
       via  a0f1ca23cd394e0d94845ecc08c35e56bf313b01 (commit)
       via  e494212d02fa6efcb437f966708992de8b02cb74 (commit)
       via  4b7786c01f7cc727ae9e36b3808bf727022e562e (commit)
       via  ec38722482e323b9e2674dd0331c074a4f1b9a61 (commit)
       via  219324e66e5bf33fde5cd53dc19f82fb1d442fc8 (commit)
       via  78c0ce22b39230347bc652f7903aac57d2b4c577 (commit)
       via  1b9ab32f79fe18b25a378808192cc2f8f5f6c718 (commit)
       via  0dc861f4c32cfd154904819d5c4b8dff71fdc47c (commit)
       via  029db306a1acc7c7856cb9da3e18fda5cdaf630f (commit)
       via  803c513e70af98eb71d4fcd954ae1714b6f1c968 (commit)
       via  f59f3629b17d25cf0f8920582af2e5e1ca5283c1 (commit)
       via  2e14ad5917f64c67866f5b49a87515c5c6479ef3 (commit)
       via  03effee3f28d0b3958f5e7e745b30cb47f6e0e74 (commit)
       via  ac03fb060596dbebbb012d091292e4c9690f1c88 (commit)
      from  67a08ea405d64a5767215d57b936b0a6b0f7b802 (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 221f5649496821d19a40863e53e72685524b9ab2
Merge: 67a08ea405d64a5767215d57b936b0a6b0f7b802 028d5ab64c816945dc310a6d8b4d159e22ba9184
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Wed May 9 14:19:48 2012 +0200

    Merge branch 'master' into trac1528
    
    Conflicts:
    	ChangeLog
    	src/lib/dhcp/iface_mgr_linux.cc

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

Summary of changes:
 .gitignore                                         |   31 +
 COPYING                                            |    6 +
 ChangeLog                                          |  369 +++++-
 Makefile.am                                        |    9 +-
 compatcheck/.gitignore                             |    1 +
 compatcheck/Makefile.am                            |   12 +-
 compatcheck/sqlite3-difftbl-check.py.in            |   60 -
 configure.ac                                       |  370 +++--
 depcomp                                            |  630 ---------
 dns++.pc.in                                        |   11 +
 doc/.gitignore                                     |    2 +
 doc/Doxyfile                                       |   25 +-
 doc/Makefile.am                                    |   13 +
 doc/devel/01-dns.dox                               |   14 +
 doc/devel/02-dhcp.dox                              |  117 ++
 doc/devel/mainpage.dox                             |   36 +
 doc/guide/bind10-guide.html                        |  306 +++--
 doc/guide/bind10-guide.txt                         |  600 +++++----
 doc/guide/bind10-guide.xml                         |  302 ++++-
 doc/guide/bind10-messages.html                     |  150 ++-
 doc/guide/bind10-messages.xml                      |  564 +++++++-
 doc/images/isc-logo.png                            |  Bin 0 -> 12595 bytes
 ext/LICENSE_1_0.txt                                |   23 +
 ext/asio/asio/detail/impl/kqueue_reactor.ipp       |    2 +
 ext/asio/asio/detail/impl/socket_ops.ipp           |   36 +-
 install-sh                                         |  520 --------
 m4macros/.gitignore                                |    5 +
 missing                                            |  376 ------
 src/bin/Makefile.am                                |    2 +-
 src/bin/auth/.gitignore                            |    7 +
 src/bin/auth/auth.spec.pre.in                      |    4 +
 src/bin/auth/auth_config.cc                        |   68 +-
 src/bin/auth/auth_messages.mes                     |   45 +-
 src/bin/auth/auth_srv.cc                           |  261 +++--
 src/bin/auth/auth_srv.h                            |   15 +-
 src/bin/auth/b10-auth.8                            |   20 +-
 src/bin/auth/b10-auth.xml                          |   21 +-
 src/bin/auth/benchmarks/.gitignore                 |    1 +
 src/bin/auth/benchmarks/query_bench.cc             |   24 +-
 src/bin/auth/command.cc                            |   96 ++-
 src/bin/auth/query.cc                              |  603 ++++-----
 src/bin/auth/query.h                               |  288 ++++-
 src/bin/auth/statistics.cc                         |    6 +-
 src/bin/auth/tests/.gitignore                      |    1 +
 src/bin/auth/tests/Makefile.am                     |   17 +-
 src/bin/auth/tests/auth_srv_unittest.cc            |  464 ++++++-
 src/bin/auth/tests/command_unittest.cc             |  156 ++-
 src/bin/auth/tests/common_unittest.cc              |    2 +-
 src/bin/auth/tests/config_syntax_unittest.cc       |   71 +
 src/bin/auth/tests/config_unittest.cc              |   91 ++-
 src/bin/auth/tests/datasrc_util.cc                 |   77 ++
 src/bin/auth/tests/datasrc_util.h                  |   58 +
 src/bin/auth/tests/query_unittest.cc               |  707 +++++++----
 src/bin/auth/tests/statistics_unittest.cc          |    2 +
 src/bin/auth/tests/testdata/example.sqlite3        |  Bin 11264 -> 15360 bytes
 src/bin/bind10/.gitignore                          |    3 +
 src/bin/bind10/bind10.8                            |   61 +-
 src/bin/bind10/bind10.xml                          |  107 +-
 src/bin/bind10/bind10_messages.mes                 |    2 +-
 src/bin/bind10/bind10_src.py.in                    |   66 +-
 src/bin/bind10/bob.spec                            |    8 -
 src/bin/bind10/tests/.gitignore                    |    1 +
 src/bin/bind10/tests/bind10_test.py.in             |   46 +-
 src/bin/bindctl/.gitignore                         |    3 +
 src/bin/bindctl/Makefile.am                        |    2 +-
 src/bin/bindctl/bindcmd.py                         |  120 ++-
 src/bin/bindctl/bindctl_main.py.in                 |    2 +
 src/bin/bindctl/command_sets.py                    |   95 ++
 src/bin/bindctl/moduleinfo.py                      |   39 +-
 src/bin/bindctl/tests/.gitignore                   |    1 +
 src/bin/bindctl/tests/bindctl_test.py              |   13 +-
 src/bin/cfgmgr/.gitignore                          |    2 +
 src/bin/cfgmgr/b10-cfgmgr.py.in                    |    7 +-
 src/bin/cfgmgr/tests/.gitignore                    |    1 +
 src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in         |   15 +-
 src/bin/cmdctl/.gitignore                          |    5 +
 src/bin/cmdctl/b10-cmdctl.8                        |   33 +-
 src/bin/cmdctl/b10-cmdctl.xml                      |   50 +-
 src/bin/cmdctl/tests/.gitignore                    |    1 +
 src/bin/dbutil/.gitignore                          |    3 +
 src/bin/dbutil/Makefile.am                         |   39 +
 src/bin/dbutil/b10-dbutil.8                        |   92 ++
 src/bin/dbutil/b10-dbutil.xml                      |  192 +++
 src/bin/dbutil/dbutil.py.in                        |  608 +++++++++
 src/bin/dbutil/dbutil_messages.mes                 |  114 ++
 src/bin/dbutil/run_dbutil.sh.in                    |   40 +
 src/bin/dbutil/tests/.gitignore                    |    2 +
 src/bin/dbutil/tests/Makefile.am                   |    6 +
 src/bin/dbutil/tests/dbutil_test.sh.in             |  481 +++++++
 src/bin/dbutil/tests/testdata/Makefile.am          |   12 +
 src/bin/dbutil/tests/testdata/README               |   41 +
 src/bin/dbutil/tests/testdata/corrupt.sqlite3      |  Bin 0 -> 215040 bytes
 src/bin/dbutil/tests/testdata/empty_schema.sqlite3 |  Bin 0 -> 215040 bytes
 src/bin/dbutil/tests/testdata/empty_v1.sqlite3     |  Bin 0 -> 215040 bytes
 .../dbutil/tests/testdata/empty_version.sqlite3    |  Bin 0 -> 13312 bytes
 src/bin/dbutil/tests/testdata/invalid_v1.sqlite3   |  Bin 0 -> 215040 bytes
 src/bin/dbutil/tests/testdata/new_v1.sqlite3       |  Bin 0 -> 215040 bytes
 src/bin/dbutil/tests/testdata/no_schema.sqlite3    |  Bin 0 -> 2048 bytes
 src/bin/dbutil/tests/testdata/old_v1.sqlite3       |  Bin 0 -> 215040 bytes
 .../dbutil/tests/testdata/too_many_version.sqlite3 |  Bin 0 -> 13312 bytes
 src/bin/dbutil/tests/testdata/v2_0.sqlite3         |  Bin 0 -> 13312 bytes
 src/bin/ddns/.gitignore                            |    2 +
 src/bin/ddns/b10-ddns.8                            |   12 +-
 src/bin/ddns/b10-ddns.xml                          |   13 +-
 src/bin/dhcp4/.gitignore                           |    3 +
 src/bin/dhcp4/Makefile.am                          |    2 +-
 src/bin/dhcp4/dhcp4_srv.cc                         |   59 +-
 src/bin/dhcp4/dhcp4_srv.h                          |   35 +-
 src/bin/dhcp4/tests/.gitignore                     |    1 +
 src/bin/dhcp6/Makefile.am                          |    2 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |  203 ++--
 src/bin/dhcp6/dhcp6_srv.h                          |   46 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |   92 ++-
 src/bin/dhcp6/tests/dhcp6_test.py                  |    3 +
 src/bin/host/.gitignore                            |    1 +
 src/bin/host/host.cc                               |    7 +-
 src/bin/loadzone/.gitignore                        |    3 +
 src/bin/loadzone/b10-loadzone.8                    |    6 +-
 src/bin/loadzone/b10-loadzone.xml                  |    4 +-
 src/bin/loadzone/tests/correct/.gitignore          |    1 +
 src/bin/loadzone/tests/error/.gitignore            |    1 +
 src/bin/msgq/.gitignore                            |    3 +
 src/bin/msgq/tests/.gitignore                      |    1 +
 src/bin/resolver/.gitignore                        |    7 +
 src/bin/resolver/b10-resolver.8                    |   10 +-
 src/bin/resolver/b10-resolver.xml                  |   11 +-
 src/bin/resolver/main.cc                           |    3 +-
 src/bin/resolver/resolver.cc                       |   20 +-
 src/bin/resolver/resolver.h                        |    6 +-
 src/bin/resolver/resolver_messages.mes             |    2 +-
 src/bin/resolver/tests/.gitignore                  |    1 +
 src/bin/resolver/tests/resolver_config_unittest.cc |   14 +-
 src/bin/sockcreator/.gitignore                     |    1 +
 src/bin/sockcreator/sockcreator.cc                 |   61 +-
 src/bin/sockcreator/sockcreator.h                  |   15 +-
 src/bin/sockcreator/tests/.gitignore               |    1 +
 src/bin/sockcreator/tests/sockcreator_tests.cc     |   81 +-
 src/bin/stats/.gitignore                           |    4 +
 src/bin/stats/b10-stats-httpd.8                    |   23 +-
 src/bin/stats/b10-stats-httpd.xml                  |   17 +-
 src/bin/stats/b10-stats.8                          |   54 +-
 src/bin/stats/b10-stats.xml                        |   62 +-
 src/bin/stats/stats.py.in                          |  123 ++-
 src/bin/stats/stats.spec                           |    7 +
 src/bin/stats/stats_httpd.py.in                    |    7 +-
 src/bin/stats/tests/b10-stats-httpd_test.py        |   18 +
 src/bin/stats/tests/b10-stats_test.py              |  179 +++
 src/bin/stats/tests/test_utils.py                  |   15 +-
 src/bin/tests/.gitignore                           |    1 +
 src/bin/usermgr/.gitignore                         |    3 +
 src/bin/xfrin/.gitignore                           |    3 +
 src/bin/xfrin/tests/.gitignore                     |    1 +
 src/bin/xfrin/tests/testdata/example.com.sqlite3   |  Bin 12288 -> 15360 bytes
 src/bin/xfrin/tests/xfrin_test.py                  |  263 ++++-
 src/bin/xfrin/xfrin.py.in                          |  199 +++-
 src/bin/xfrin/xfrin_messages.mes                   |  292 +++--
 src/bin/xfrout/.gitignore                          |    5 +
 src/bin/xfrout/b10-xfrout.8                        |   65 +-
 src/bin/xfrout/b10-xfrout.xml                      |   72 +-
 src/bin/xfrout/tests/.gitignore                    |    2 +
 src/bin/xfrout/tests/testdata/test.sqlite3         |  Bin 12288 -> 15360 bytes
 src/bin/xfrout/tests/xfrout_test.py.in             |   50 +-
 src/bin/xfrout/xfrout.py.in                        |   43 +-
 src/bin/xfrout/xfrout.spec.pre.in                  |   55 +-
 src/bin/xfrout/xfrout_messages.mes                 |    4 +
 src/bin/zonemgr/.gitignore                         |    5 +
 src/bin/zonemgr/b10-zonemgr.8                      |   10 +-
 src/bin/zonemgr/b10-zonemgr.xml                    |   11 +-
 src/bin/zonemgr/tests/.gitignore                   |    2 +
 src/bin/zonemgr/tests/Makefile.am                  |    1 +
 src/bin/zonemgr/tests/zonemgr_test.py              |   33 +-
 src/bin/zonemgr/zonemgr.py.in                      |    6 +
 src/bin/zonemgr/zonemgr_messages.mes               |   12 +-
 src/cppcheck-suppress.lst                          |   16 +-
 src/lib/acl/ip_check.cc                            |    1 +
 src/lib/acl/tests/.gitignore                       |    1 +
 src/lib/asiodns/.gitignore                         |    2 +
 src/lib/asiodns/Makefile.am                        |    1 +
 src/lib/asiodns/dns_server.h                       |   16 -
 src/lib/asiodns/dns_service.cc                     |  171 +--
 src/lib/asiodns/dns_service.h                      |  127 ++-
 src/lib/asiodns/io_fetch.cc                        |    9 +-
 src/lib/asiodns/sync_udp_server.cc                 |  193 +++
 src/lib/asiodns/sync_udp_server.h                  |  148 ++
 src/lib/asiodns/tcp_server.cc                      |   24 +-
 src/lib/asiodns/tcp_server.h                       |    9 -
 src/lib/asiodns/tests/.gitignore                   |    1 +
 src/lib/asiodns/tests/Makefile.am                  |    2 +-
 src/lib/asiodns/tests/dns_server_unittest.cc       |  115 +-
 src/lib/asiodns/tests/dns_service_unittest.cc      |  232 ++++
 src/lib/asiodns/tests/io_fetch_unittest.cc         |   27 +-
 src/lib/asiodns/tests/io_service_unittest.cc       |  118 --
 src/lib/asiodns/udp_server.cc                      |   13 +-
 src/lib/asiodns/udp_server.h                       |   23 -
 src/lib/asiolink/io_address.cc                     |    4 +-
 src/lib/asiolink/io_service.cc                     |    2 +-
 src/lib/asiolink/tests/.gitignore                  |    1 +
 src/lib/bench/benchmark.h                          |   52 +-
 src/lib/bench/benchmark_util.cc                    |    9 +-
 src/lib/bench/example/.gitignore                   |    1 +
 src/lib/bench/example/search_bench.cc              |    4 +-
 src/lib/bench/tests/.gitignore                     |    1 +
 src/lib/bench/tests/benchmark_unittest.cc          |   18 +-
 src/lib/cache/.gitignore                           |    2 +
 src/lib/cache/tests/.gitignore                     |    1 +
 src/lib/cache/tests/Makefile.am                    |    5 -
 src/lib/cache/tests/negative_cache_unittest.cc     |   52 +-
 src/lib/cc/.gitignore                              |    4 +
 src/lib/cc/cc_messages.mes                         |    2 +-
 src/lib/cc/data.cc                                 |   99 ++-
 src/lib/cc/session.cc                              |    2 +-
 src/lib/cc/tests/.gitignore                        |    2 +
 src/lib/cc/tests/Makefile.am                       |    7 +-
 src/lib/cc/tests/data_unittests.cc                 |   53 +-
 src/lib/config/.gitignore                          |    2 +
 src/lib/config/config_data.h                       |    2 +-
 src/lib/config/module_spec.cc                      |   22 +-
 src/lib/config/module_spec.h                       |   12 +-
 src/lib/config/tests/.gitignore                    |    2 +
 src/lib/config/tests/testdata/.gitignore           |    1 +
 src/lib/cryptolink/tests/.gitignore                |    1 +
 src/lib/datasrc/.gitignore                         |    4 +
 src/lib/datasrc/Makefile.am                        |   16 +-
 src/lib/datasrc/data_source.cc                     |    3 +-
 src/lib/datasrc/database.cc                        |  698 +++++++---
 src/lib/datasrc/database.h                         |  373 +++++-
 src/lib/datasrc/datasrc_config.h.pre.in            |    2 +-
 src/lib/datasrc/datasrc_messages.mes               |   66 +-
 src/lib/datasrc/factory.h                          |    2 +-
 src/lib/datasrc/memory_datasrc.cc                  | 1402 ++++++++++++++------
 src/lib/datasrc/memory_datasrc.h                   |   50 +-
 src/lib/datasrc/memory_datasrc_link.cc             |  173 +++
 src/lib/datasrc/rbnode_rrset.h                     |  228 ++++
 src/lib/datasrc/rbtree.h                           |   11 +-
 src/lib/datasrc/sqlite3_accessor.cc                |  604 ++++++---
 src/lib/datasrc/sqlite3_accessor.h                 |   35 +-
 src/lib/datasrc/sqlite3_accessor_link.cc           |  107 ++
 src/lib/datasrc/sqlite3_datasrc.cc                 |  196 ++-
 src/lib/datasrc/sqlite3_datasrc.h                  |    6 +
 src/lib/datasrc/static_datasrc.cc                  |    2 +
 src/lib/datasrc/tests/.gitignore                   |    4 +
 src/lib/datasrc/tests/Makefile.am                  |   65 +-
 src/lib/datasrc/tests/database_unittest.cc         |  948 +++++++++++---
 src/lib/datasrc/tests/datasrc_unittest.cc          |    3 +-
 src/lib/datasrc/tests/faked_nsec3.cc               |  209 +++
 src/lib/datasrc/tests/faked_nsec3.h                |   88 ++
 src/lib/datasrc/tests/memory_datasrc_unittest.cc   |  432 ++++---
 src/lib/datasrc/tests/rbnode_rrset_unittest.cc     |  276 ++++
 src/lib/datasrc/tests/sqlite3_accessor_unittest.cc |  427 ++++++-
 src/lib/datasrc/tests/sqlite3_unittest.cc          |   15 +
 src/lib/datasrc/tests/static_unittest.cc           |    2 +
 src/lib/datasrc/tests/test_client.cc               |   92 ++
 src/lib/datasrc/tests/test_client.h                |   71 +
 src/lib/datasrc/tests/testdata/.gitignore          |    1 +
 src/lib/datasrc/tests/testdata/contexttest.zone    |   81 ++
 src/lib/datasrc/tests/testdata/diffs.sqlite3       |  Bin 16384 -> 20480 bytes
 src/lib/datasrc/tests/testdata/example.org.sqlite3 |  Bin 14336 -> 16384 bytes
 .../datasrc/tests/testdata/example2.com.sqlite3    |  Bin 14336 -> 16384 bytes
 .../tests/testdata/new_minor_schema.sqlite3        |  Bin 0 -> 2048 bytes
 src/lib/datasrc/tests/testdata/newschema.sqlite3   |  Bin 0 -> 2048 bytes
 src/lib/datasrc/tests/testdata/oldschema.sqlite3   |  Bin 0 -> 2048 bytes
 .../{dns => datasrc}/tests/testdata/rrset_toWire1  |    0 
 src/lib/datasrc/tests/testdata/rrset_toWire2       |   26 +
 src/lib/datasrc/tests/testdata/rwtest.sqlite3      |  Bin 13312 -> 0 bytes
 src/lib/datasrc/tests/testdata/test-root.sqlite3   |  Bin 17408 -> 22528 bytes
 src/lib/datasrc/tests/testdata/test.sqlite3        |  Bin 44032 -> 70656 bytes
 .../datasrc/tests/testdata/test.sqlite3.nodiffs    |  Bin 43008 -> 0 bytes
 .../datasrc/tests/zone_finder_context_unittest.cc  |  413 ++++++
 src/lib/datasrc/zone.h                             |  282 +++-
 src/lib/datasrc/zone_finder_context.cc             |  102 ++
 src/lib/dhcp/dhcp6.h                               |    9 +
 src/lib/dhcp/iface_mgr.cc                          |  447 +++----
 src/lib/dhcp/iface_mgr.h                           |   62 +-
 src/lib/dhcp/iface_mgr_bsd.cc                      |    9 +
 src/lib/dhcp/iface_mgr_linux.cc                    |   62 +-
 src/lib/dhcp/libdhcp++.cc                          |  142 +-
 src/lib/dhcp/libdhcp++.h                           |   69 +-
 src/lib/dhcp/option.cc                             |  148 +--
 src/lib/dhcp/option.h                              |  166 +--
 src/lib/dhcp/option4_addrlst.cc                    |   14 +-
 src/lib/dhcp/option4_addrlst.h                     |   15 +-
 src/lib/dhcp/option6_addrlst.cc                    |   81 +-
 src/lib/dhcp/option6_addrlst.h                     |   53 +-
 src/lib/dhcp/option6_ia.cc                         |   93 +-
 src/lib/dhcp/option6_ia.h                          |   64 +-
 src/lib/dhcp/option6_iaaddr.cc                     |   79 +-
 src/lib/dhcp/option6_iaaddr.h                      |   52 +-
 src/lib/dhcp/pkt4.h                                |  101 +-
 src/lib/dhcp/pkt6.cc                               |  140 +--
 src/lib/dhcp/pkt6.h                                |  198 ++-
 src/lib/dhcp/tests/.gitignore                      |    1 +
 src/lib/dhcp/tests/iface_mgr_unittest.cc           |   63 +-
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |   67 +-
 src/lib/dhcp/tests/option6_addrlst_unittest.cc     |   52 +-
 src/lib/dhcp/tests/option6_ia_unittest.cc          |  132 +--
 src/lib/dhcp/tests/option6_iaaddr_unittest.cc      |   91 +-
 src/lib/dhcp/tests/option_unittest.cc              |  226 ++--
 src/lib/dhcp/tests/pkt6_unittest.cc                |  162 ++--
 src/lib/dns/.gitignore                             |    6 +
 src/lib/dns/Makefile.am                            |   11 +-
 src/lib/dns/benchmarks/.gitignore                  |    2 +
 src/lib/dns/benchmarks/Makefile.am                 |   10 +-
 src/lib/dns/benchmarks/message_renderer_bench.cc   |  176 +++
 src/lib/dns/benchmarks/oldmessagerenderer.cc       |  278 ++++
 src/lib/dns/benchmarks/oldmessagerenderer.h        |   55 +
 src/lib/dns/benchmarks/rdatarender_bench.cc        |    3 +-
 src/lib/dns/labelsequence.cc                       |  114 ++
 src/lib/dns/labelsequence.h                        |  177 +++
 src/lib/dns/masterload.cc                          |   33 +-
 src/lib/dns/messagerenderer.cc                     |  353 ++++--
 src/lib/dns/messagerenderer.h                      |  104 +-
 src/lib/dns/name.cc                                |   55 +-
 src/lib/dns/name.h                                 |   39 +-
 src/lib/dns/name_internal.h                        |   43 +
 src/lib/dns/python/Makefile.am                     |    2 +-
 src/lib/dns/python/message_python.cc               |    3 +-
 src/lib/dns/python/messagerenderer_python.cc       |    6 +-
 src/lib/dns/rdata/generic/sshfp_44.cc              |  164 +++
 src/lib/dns/rdata/generic/sshfp_44.h               |   58 +
 src/lib/dns/rdata/template.cc                      |    1 +
 src/lib/dns/rdatafields.cc                         |    6 +-
 src/lib/dns/rrset.cc                               |   10 +
 src/lib/dns/rrset.h                                |    8 +
 src/lib/dns/tests/.gitignore                       |    1 +
 src/lib/dns/tests/Makefile.am                      |    2 +
 src/lib/dns/tests/edns_unittest.cc                 |    4 +-
 src/lib/dns/tests/labelsequence_unittest.cc        |  348 +++++
 src/lib/dns/tests/masterload_unittest.cc           |   98 ++
 src/lib/dns/tests/message_unittest.cc              |   11 +-
 src/lib/dns/tests/messagerenderer_unittest.cc      |  118 ++-
 src/lib/dns/tests/name_unittest.cc                 |   69 +-
 src/lib/dns/tests/question_unittest.cc             |    6 +-
 src/lib/dns/tests/rdata_afsdb_unittest.cc          |    2 +-
 src/lib/dns/tests/rdata_cname_unittest.cc          |    4 +-
 src/lib/dns/tests/rdata_dhcid_unittest.cc          |    1 +
 src/lib/dns/tests/rdata_dname_unittest.cc          |    4 +-
 src/lib/dns/tests/rdata_dnskey_unittest.cc         |    4 +-
 src/lib/dns/tests/rdata_ds_like_unittest.cc        |    4 +-
 src/lib/dns/tests/rdata_hinfo_unittest.cc          |    5 +-
 src/lib/dns/tests/rdata_in_a_unittest.cc           |    3 +-
 src/lib/dns/tests/rdata_in_aaaa_unittest.cc        |    3 +-
 src/lib/dns/tests/rdata_minfo_unittest.cc          |    8 +-
 src/lib/dns/tests/rdata_mx_unittest.cc             |    7 +-
 src/lib/dns/tests/rdata_naptr_unittest.cc          |    5 +-
 src/lib/dns/tests/rdata_ns_unittest.cc             |    4 +-
 .../dns/tests/rdata_nsec3param_like_unittest.cc    |    2 +-
 src/lib/dns/tests/rdata_nsec3param_unittest.cc     |    4 +-
 src/lib/dns/tests/rdata_nsec_unittest.cc           |    4 +-
 src/lib/dns/tests/rdata_nsecbitmap_unittest.cc     |    2 +-
 src/lib/dns/tests/rdata_ptr_unittest.cc            |    4 +-
 src/lib/dns/tests/rdata_rp_unittest.cc             |    2 +-
 src/lib/dns/tests/rdata_soa_unittest.cc            |    4 +-
 src/lib/dns/tests/rdata_srv_unittest.cc            |    4 +-
 src/lib/dns/tests/rdata_sshfp_unittest.cc          |   94 ++
 src/lib/dns/tests/rdata_unittest.cc                |    6 +-
 src/lib/dns/tests/rdatafields_unittest.cc          |    5 +-
 src/lib/dns/tests/rrclass_unittest.cc              |    4 +-
 src/lib/dns/tests/rrset_unittest.cc                |   27 +-
 src/lib/dns/tests/rrttl_unittest.cc                |   10 +-
 src/lib/dns/tests/rrtype_unittest.cc               |    4 +-
 src/lib/dns/tests/testdata/.gitignore              |  117 ++
 src/lib/dns/tests/testdata/Makefile.am             |    3 +
 src/lib/dns/tests/testdata/rdata_mx_fromWire       |    2 +-
 src/lib/dns/tests/testdata/rdata_sshfp_fromWire    |    4 +
 .../dns/tests/testdata/rdata_sshfp_fromWire1.spec  |    6 +
 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2   |    4 +
 .../dns/tests/testdata/rdata_sshfp_fromWire2.spec  |    7 +
 src/lib/dns/tests/tsig_unittest.cc                 |    3 +-
 src/lib/dns/tests/tsigrecord_unittest.cc           |    2 +-
 src/lib/dns/tests/unittest_util.cc                 |   18 +
 src/lib/dns/tests/unittest_util.h                  |   16 +
 src/lib/exceptions/Makefile.am                     |    2 +-
 src/lib/exceptions/exceptions.h                    |   32 +
 src/lib/exceptions/tests/.gitignore                |    1 +
 src/lib/log/compiler/.gitignore                    |    1 +
 src/lib/log/compiler/Makefile.am                   |    1 +
 src/lib/log/compiler/message.cc                    |   50 +-
 src/lib/log/log_formatter.cc                       |   36 +-
 src/lib/log/log_formatter.h                        |   22 +
 src/lib/log/logger.h                               |   43 +-
 src/lib/log/logger_manager.cc                      |    4 +
 src/lib/log/logger_manager_impl.cc                 |    4 +-
 src/lib/log/message_exception.h                    |   24 +-
 src/lib/log/message_initializer.cc                 |   71 +-
 src/lib/log/message_initializer.h                  |   56 +-
 src/lib/log/message_reader.cc                      |   40 +-
 src/lib/log/message_reader.h                       |    2 +-
 src/lib/log/tests/.gitignore                       |   11 +
 src/lib/log/tests/Makefile.am                      |   99 +-
 src/lib/log/tests/log_formatter_unittest.cc        |   64 +-
 src/lib/log/tests/logger_example.cc                |    2 +-
 src/lib/log/tests/logger_manager_unittest.cc       |   10 +-
 src/lib/log/tests/logger_support_unittest.cc       |    2 +-
 src/lib/log/tests/logger_unittest.cc               |   35 +-
 .../log/tests/message_initializer_1_unittest.cc    |   79 ++
 .../log/tests/message_initializer_1a_unittest.cc   |   37 +
 .../log/tests/message_initializer_2_unittest.cc    |   52 +
 src/lib/log/tests/message_initializer_unittest.cc  |   70 -
 .../log/tests/message_initializer_unittest_2.cc    |   39 -
 src/lib/log/tests/run_initializer_unittests.cc     |   24 +
 src/lib/nsas/.gitignore                            |    2 +
 src/lib/nsas/glue_hints.cc                         |    4 +-
 src/lib/nsas/hash.cc                               |    5 +-
 src/lib/nsas/hash_table.h                          |    2 +-
 src/lib/nsas/tests/.gitignore                      |    1 +
 src/lib/nsas/tests/Makefile.am                     |    5 -
 src/lib/nsas/tests/nameserver_entry_unittest.cc    |    4 +-
 src/lib/nsas/zone_entry.cc                         |    2 +-
 src/lib/python/.gitignore                          |    1 +
 src/lib/python/Makefile.am                         |    2 +-
 src/lib/python/isc/Makefile.am                     |    2 +-
 src/lib/python/isc/acl/Makefile.am                 |    4 +-
 src/lib/python/isc/acl/tests/dns_test.py           |    2 +-
 src/lib/python/isc/bind10/component.py             |   28 +-
 src/lib/python/isc/cc/session.py                   |   25 +-
 src/lib/python/isc/cc/tests/.gitignore             |    1 +
 src/lib/python/isc/cc/tests/session_test.py        |   31 +-
 src/lib/python/isc/config/Makefile.am              |    1 +
 src/lib/python/isc/config/ccsession.py             |   93 +-
 src/lib/python/isc/config/cfgmgr.py                |   31 +-
 src/lib/python/isc/config/cfgmgr_messages.mes      |    6 +-
 src/lib/python/isc/config/config_data.py           |   56 +-
 src/lib/python/isc/config/tests/.gitignore         |    1 +
 src/lib/python/isc/config/tests/ccsession_test.py  |  323 ++++-
 src/lib/python/isc/config/tests/cfgmgr_test.py     |   57 +-
 .../python/isc/config/tests/config_data_test.py    |   83 ++-
 src/lib/python/isc/datasrc/Makefile.am             |    2 +-
 src/lib/python/isc/datasrc/datasrc.cc              |    3 +
 src/lib/python/isc/datasrc/finder_inc.cc           |    7 +-
 src/lib/python/isc/datasrc/finder_python.cc        |   27 +-
 src/lib/python/isc/datasrc/sqlite3_ds.py           |   49 +-
 src/lib/python/isc/datasrc/tests/.gitignore        |    1 +
 src/lib/python/isc/datasrc/tests/Makefile.am       |    8 +-
 src/lib/python/isc/datasrc/tests/datasrc_test.py   |   24 +-
 .../python/isc/datasrc/tests/sqlite3_ds_test.py    |  124 +--
 .../isc/datasrc/tests/testdata/example.com.sqlite3 |  Bin 44032 -> 70656 bytes
 .../tests/testdata/new_minor_schema.sqlite3        |  Bin 0 -> 2048 bytes
 .../isc/datasrc/tests/testdata/newschema.sqlite3   |  Bin 0 -> 2048 bytes
 .../isc/datasrc/tests/testdata/oldschema.sqlite3   |  Bin 0 -> 2048 bytes
 .../datasrc/tests/testdata/test.sqlite3.nodiffs    |  Bin 43008 -> 0 bytes
 src/lib/python/isc/log/Makefile.am                 |   11 +-
 src/lib/python/isc/log/tests/.gitignore            |    1 +
 src/lib/python/isc/log_messages/Makefile.am        |    4 +
 src/lib/python/isc/log_messages/dbutil_messages.py |    1 +
 .../isc/log_messages/server_common_messages.py     |    1 +
 src/lib/python/isc/log_messages/work/.gitignore    |    2 +
 src/lib/python/isc/log_messages/work/Makefile.am   |    2 +-
 src/lib/python/isc/notify/notify_out.py            |   37 +-
 src/lib/python/isc/notify/tests/.gitignore         |    1 +
 src/lib/python/isc/notify/tests/notify_out_test.py |   29 +-
 .../isc/notify/tests/testdata/brokentest.sqlite3   |  Bin 11264 -> 15360 bytes
 .../python/isc/notify/tests/testdata/test.sqlite3  |  Bin 13312 -> 19456 bytes
 src/lib/python/isc/server_common/Makefile.am       |   24 +
 .../isc/{bind10 => server_common}/__init__.py      |    0 
 .../isc/server_common/server_common_messages.mes   |   36 +
 src/lib/python/isc/server_common/tests/Makefile.am |   24 +
 .../isc/server_common/tests/tsig_keyring_test.py   |  193 +++
 src/lib/python/isc/server_common/tsig_keyring.py   |  121 ++
 src/lib/python/isc/util/cio/Makefile.am            |    2 +-
 src/lib/resolve/.gitignore                         |    2 +
 src/lib/resolve/Makefile.am                        |    1 +
 src/lib/resolve/recursive_query.cc                 |    4 +-
 src/lib/resolve/recursive_query.h                  |    4 +-
 src/lib/resolve/tests/.gitignore                   |    1 +
 src/lib/resolve/tests/recursive_query_unittest.cc  |  248 +++--
 .../resolve/tests/recursive_query_unittest_2.cc    |   17 +-
 .../resolve/tests/recursive_query_unittest_3.cc    |   17 +-
 src/lib/server_common/.gitignore                   |    2 +
 src/lib/server_common/portconfig.cc                |   36 +-
 src/lib/server_common/portconfig.h                 |   86 +-
 src/lib/server_common/tests/.gitignore             |    2 +
 src/lib/server_common/tests/client_unittest.cc     |    1 +
 src/lib/server_common/tests/portconfig_unittest.cc |   49 +-
 src/lib/statistics/tests/.gitignore                |    1 +
 src/lib/testutils/dnsmessage_test.cc               |   29 +
 src/lib/testutils/dnsmessage_test.h                |   58 +-
 src/lib/testutils/mockups.h                        |   57 +
 src/lib/testutils/portconfig.h                     |    6 +-
 src/lib/testutils/socket_request.h                 |   19 +-
 src/lib/testutils/srv_test.cc                      |    3 +-
 src/lib/testutils/srv_test.h                       |    5 +-
 src/lib/testutils/testdata/.gitignore              |   15 +
 src/lib/testutils/testdata/Makefile.am             |    1 +
 src/lib/testutils/testdata/auth_test.sqlite3       |  Bin 0 -> 16384 bytes
 src/lib/testutils/testdata/example.sqlite3         |  Bin 11264 -> 15360 bytes
 .../testutils/testdata/rfc5155-example.zone.signed |    4 +-
 src/lib/testutils/testdata/rwtest.sqlite3          |  Bin 0 -> 16384 bytes
 src/lib/util/buffer.h                              |   57 +-
 src/lib/util/io/Makefile.am                        |    2 +-
 src/lib/util/io/fd_share.cc                        |   17 +-
 src/lib/util/io/pktinfo_utilities.h                |   51 +
 src/lib/util/io/sockaddr_util.h                    |    2 +-
 src/lib/util/locks.h                               |   58 +-
 src/lib/util/python/.gitignore                     |    2 +
 src/lib/util/python/gen_wiredata.py.in             |   23 +
 src/lib/util/pyunittests/Makefile.am               |    2 +-
 src/lib/util/range_utilities.h                     |   68 +
 src/lib/util/tests/.gitignore                      |    1 +
 src/lib/util/tests/Makefile.am                     |    1 +
 src/lib/util/tests/buffer_unittest.cc              |   41 +-
 src/lib/util/tests/range_utilities_unittest.cc     |   55 +
 src/lib/util/unittests/Makefile.am                 |    1 +
 src/lib/util/unittests/resource.cc                 |   35 +
 src/lib/util/unittests/resource.h                  |   39 +
 src/lib/xfr/tests/.gitignore                       |    1 +
 tests/lettuce/.gitignore                           |    2 +
 tests/lettuce/README                               |   20 +-
 tests/lettuce/README.tutorial                      |    4 +-
 tests/lettuce/configurations/.gitignore            |    2 +
 .../configurations/bindctl/bindctl.config.orig     |   22 +
 .../configurations/bindctl_commands.config.orig    |   34 +
 tests/lettuce/configurations/default.config        |   16 +
 .../lettuce/configurations/example.org.config.orig |    8 +-
 .../configurations/example.org.inmem.config        |    8 +
 tests/lettuce/configurations/example2.org.config   |   10 +-
 .../inmemory_over_sqlite3/secondary.conf           |   32 +
 .../configurations/ixfr-out/testset1-config.db     |   12 +-
 .../configurations/multi_instance/.gitignore       |    1 +
 .../multi_instance/multi_auth.config.orig          |   24 +
 tests/lettuce/configurations/no_db_file.config     |   14 +
 .../lettuce/configurations/nsec3/nsec3_auth.config |    1 +
 .../nsec3}/rfc5155-example.zone.signed             |    0 
 tests/lettuce/configurations/resolver/.gitignore   |    1 +
 .../resolver/resolver_basic.config.orig            |    2 +-
 .../configurations/xfrin/retransfer_master.conf    |   12 +-
 .../configurations/xfrin/retransfer_slave.conf     |   10 +-
 tests/lettuce/data/.gitignore                      |    2 +
 tests/lettuce/data/commands/bad_command            |    9 +
 tests/lettuce/data/commands/directives             |   19 +
 AUTHORS => tests/lettuce/data/commands/empty       |    0 
 tests/lettuce/data/commands/nested                 |    2 +
 tests/lettuce/data/commands/nested1                |    2 +
 tests/lettuce/data/empty_db.sqlite3                |  Bin 11264 -> 14336 bytes
 tests/lettuce/data/example.org                     |   12 +
 tests/lettuce/data/example.org.sqlite3             |  Bin 14336 -> 15360 bytes
 tests/lettuce/data/ixfr-out/.gitignore             |    1 +
 tests/lettuce/data/ixfr-out/zones.slite3           |  Bin 246784 -> 0 bytes
 tests/lettuce/data/ixfr-out/zones.sqlite3          |  Bin 0 -> 468992 bytes
 tests/lettuce/features/bindctl_commands.feature    |  186 +++-
 tests/lettuce/features/default.feature             |   21 +
 tests/lettuce/features/example.feature             |   64 +-
 .../lettuce/features/inmemory_over_sqlite3.feature |   10 +
 tests/lettuce/features/ixfr_out_bind10.feature     |   18 +-
 tests/lettuce/features/multi_instance.feature      |   59 +
 tests/lettuce/features/nsec3_auth.feature          |  466 +++++++
 tests/lettuce/features/queries.feature             |  124 ++
 tests/lettuce/features/resolver_basic.feature      |   12 +-
 tests/lettuce/features/terrain/.gitignore          |    1 +
 tests/lettuce/features/terrain/bind10_control.py   |  121 ++-
 tests/lettuce/features/terrain/querying.py         |   83 +-
 tests/lettuce/features/terrain/steps.py            |   18 +-
 tests/lettuce/features/terrain/terrain.py          |   60 +-
 tests/lettuce/features/terrain/transfer.py         |    4 +-
 tests/lettuce/features/xfrin_bind10.feature        |   24 +-
 tests/lettuce/run_lettuce.sh                       |   25 +
 tests/lettuce/setup_intree_bind10.sh.in            |    4 +-
 tests/system/.gitignore                            |    2 +
 tests/system/bindctl/nsx1/.gitignore               |    3 +
 .../system/bindctl/nsx1/b10-config.db.template.in  |    3 -
 tests/system/bindctl/tests.sh                      |  110 ++-
 tests/system/glue/.gitignore                       |    1 +
 tests/system/glue/nsx1/.gitignore                  |    3 +
 tests/system/glue/nsx1/b10-config.db.in            |   11 +-
 tests/system/ixfr/.gitignore                       |    8 +
 tests/system/ixfr/b10-config.db.in                 |   10 +
 tests/system/ixfr/in-1/.gitignore                  |    1 +
 tests/system/ixfr/in-2/.gitignore                  |    1 +
 tests/system/ixfr/in-2/ns1/.gitignore              |    1 +
 tests/system/ixfr/in-2/nsx2/.gitignore             |    1 +
 tests/system/ixfr/in-2/tests.sh                    |    2 +-
 tests/system/ixfr/in-3/.gitignore                  |    1 +
 tests/system/ixfr/in-4/.gitignore                  |    1 +
 tests/system/start.pl                              |    7 +-
 tests/tools/badpacket/.gitignore                   |    1 +
 tests/tools/badpacket/scan.cc                      |    3 +-
 tests/tools/badpacket/tests/.gitignore             |    1 +
 tests/tools/perfdhcp/.gitignore                    |    1 +
 tests/tools/perfdhcp/perfdhcp.c                    |   36 +
 tools/git-obsolete-branch.py                       |  198 +++
 579 files changed, 23688 insertions(+), 8944 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 compatcheck/.gitignore
 delete mode 100755 compatcheck/sqlite3-difftbl-check.py.in
 delete mode 100755 depcomp
 create mode 100644 dns++.pc.in
 create mode 100644 doc/.gitignore
 create mode 100644 doc/devel/01-dns.dox
 create mode 100644 doc/devel/02-dhcp.dox
 create mode 100644 doc/devel/mainpage.dox
 create mode 100644 doc/images/isc-logo.png
 create mode 100644 ext/LICENSE_1_0.txt
 delete mode 100755 install-sh
 create mode 100644 m4macros/.gitignore
 delete mode 100755 missing
 create mode 100644 src/bin/auth/.gitignore
 create mode 100644 src/bin/auth/benchmarks/.gitignore
 create mode 100644 src/bin/auth/tests/.gitignore
 create mode 100644 src/bin/auth/tests/config_syntax_unittest.cc
 create mode 100644 src/bin/auth/tests/datasrc_util.cc
 create mode 100644 src/bin/auth/tests/datasrc_util.h
 create mode 100644 src/bin/bind10/.gitignore
 create mode 100644 src/bin/bind10/tests/.gitignore
 create mode 100644 src/bin/bindctl/.gitignore
 create mode 100644 src/bin/bindctl/command_sets.py
 create mode 100644 src/bin/bindctl/tests/.gitignore
 create mode 100644 src/bin/cfgmgr/.gitignore
 create mode 100644 src/bin/cfgmgr/tests/.gitignore
 create mode 100644 src/bin/cmdctl/.gitignore
 create mode 100644 src/bin/cmdctl/tests/.gitignore
 create mode 100644 src/bin/dbutil/.gitignore
 create mode 100644 src/bin/dbutil/Makefile.am
 create mode 100644 src/bin/dbutil/b10-dbutil.8
 create mode 100644 src/bin/dbutil/b10-dbutil.xml
 create mode 100755 src/bin/dbutil/dbutil.py.in
 create mode 100644 src/bin/dbutil/dbutil_messages.mes
 create mode 100755 src/bin/dbutil/run_dbutil.sh.in
 create mode 100644 src/bin/dbutil/tests/.gitignore
 create mode 100644 src/bin/dbutil/tests/Makefile.am
 create mode 100755 src/bin/dbutil/tests/dbutil_test.sh.in
 create mode 100644 src/bin/dbutil/tests/testdata/Makefile.am
 create mode 100644 src/bin/dbutil/tests/testdata/README
 create mode 100644 src/bin/dbutil/tests/testdata/corrupt.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/empty_schema.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/empty_v1.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/empty_version.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/invalid_v1.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/new_v1.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/no_schema.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/old_v1.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/too_many_version.sqlite3
 create mode 100644 src/bin/dbutil/tests/testdata/v2_0.sqlite3
 create mode 100644 src/bin/ddns/.gitignore
 create mode 100644 src/bin/dhcp4/.gitignore
 create mode 100644 src/bin/dhcp4/tests/.gitignore
 create mode 100644 src/bin/host/.gitignore
 create mode 100644 src/bin/loadzone/.gitignore
 create mode 100644 src/bin/loadzone/tests/correct/.gitignore
 create mode 100644 src/bin/loadzone/tests/error/.gitignore
 create mode 100644 src/bin/msgq/.gitignore
 create mode 100644 src/bin/msgq/tests/.gitignore
 create mode 100644 src/bin/resolver/.gitignore
 create mode 100644 src/bin/resolver/tests/.gitignore
 create mode 100644 src/bin/sockcreator/.gitignore
 create mode 100644 src/bin/sockcreator/tests/.gitignore
 create mode 100644 src/bin/stats/.gitignore
 create mode 100644 src/bin/tests/.gitignore
 create mode 100644 src/bin/usermgr/.gitignore
 create mode 100644 src/bin/xfrin/.gitignore
 create mode 100644 src/bin/xfrin/tests/.gitignore
 create mode 100644 src/bin/xfrout/.gitignore
 create mode 100644 src/bin/xfrout/tests/.gitignore
 create mode 100644 src/bin/zonemgr/.gitignore
 create mode 100644 src/bin/zonemgr/tests/.gitignore
 create mode 100644 src/lib/acl/tests/.gitignore
 create mode 100644 src/lib/asiodns/.gitignore
 create mode 100644 src/lib/asiodns/sync_udp_server.cc
 create mode 100644 src/lib/asiodns/sync_udp_server.h
 create mode 100644 src/lib/asiodns/tests/.gitignore
 create mode 100644 src/lib/asiodns/tests/dns_service_unittest.cc
 delete mode 100644 src/lib/asiodns/tests/io_service_unittest.cc
 create mode 100644 src/lib/asiolink/tests/.gitignore
 create mode 100644 src/lib/bench/example/.gitignore
 create mode 100644 src/lib/bench/tests/.gitignore
 create mode 100644 src/lib/cache/.gitignore
 create mode 100644 src/lib/cache/tests/.gitignore
 create mode 100644 src/lib/cc/.gitignore
 create mode 100644 src/lib/cc/tests/.gitignore
 create mode 100644 src/lib/config/.gitignore
 create mode 100644 src/lib/config/tests/.gitignore
 create mode 100644 src/lib/config/tests/testdata/.gitignore
 create mode 100644 src/lib/cryptolink/tests/.gitignore
 create mode 100644 src/lib/datasrc/.gitignore
 create mode 100644 src/lib/datasrc/memory_datasrc_link.cc
 create mode 100644 src/lib/datasrc/rbnode_rrset.h
 create mode 100644 src/lib/datasrc/sqlite3_accessor_link.cc
 create mode 100644 src/lib/datasrc/tests/.gitignore
 create mode 100644 src/lib/datasrc/tests/faked_nsec3.cc
 create mode 100644 src/lib/datasrc/tests/faked_nsec3.h
 create mode 100644 src/lib/datasrc/tests/rbnode_rrset_unittest.cc
 create mode 100644 src/lib/datasrc/tests/test_client.cc
 create mode 100644 src/lib/datasrc/tests/test_client.h
 create mode 100644 src/lib/datasrc/tests/testdata/.gitignore
 create mode 100644 src/lib/datasrc/tests/testdata/contexttest.zone
 create mode 100644 src/lib/datasrc/tests/testdata/new_minor_schema.sqlite3
 create mode 100644 src/lib/datasrc/tests/testdata/newschema.sqlite3
 create mode 100644 src/lib/datasrc/tests/testdata/oldschema.sqlite3
 copy src/lib/{dns => datasrc}/tests/testdata/rrset_toWire1 (100%)
 create mode 100644 src/lib/datasrc/tests/testdata/rrset_toWire2
 delete mode 100644 src/lib/datasrc/tests/testdata/rwtest.sqlite3
 delete mode 100644 src/lib/datasrc/tests/testdata/test.sqlite3.nodiffs
 create mode 100644 src/lib/datasrc/tests/zone_finder_context_unittest.cc
 create mode 100644 src/lib/datasrc/zone_finder_context.cc
 create mode 100644 src/lib/dhcp/tests/.gitignore
 create mode 100644 src/lib/dns/.gitignore
 create mode 100644 src/lib/dns/benchmarks/.gitignore
 create mode 100644 src/lib/dns/benchmarks/message_renderer_bench.cc
 create mode 100644 src/lib/dns/benchmarks/oldmessagerenderer.cc
 create mode 100644 src/lib/dns/benchmarks/oldmessagerenderer.h
 create mode 100644 src/lib/dns/labelsequence.cc
 create mode 100644 src/lib/dns/labelsequence.h
 create mode 100644 src/lib/dns/name_internal.h
 create mode 100644 src/lib/dns/rdata/generic/sshfp_44.cc
 create mode 100644 src/lib/dns/rdata/generic/sshfp_44.h
 create mode 100644 src/lib/dns/tests/.gitignore
 create mode 100644 src/lib/dns/tests/labelsequence_unittest.cc
 create mode 100644 src/lib/dns/tests/rdata_sshfp_unittest.cc
 create mode 100644 src/lib/dns/tests/testdata/.gitignore
 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire
 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec
 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2
 create mode 100644 src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec
 create mode 100644 src/lib/exceptions/tests/.gitignore
 create mode 100644 src/lib/log/compiler/.gitignore
 create mode 100644 src/lib/log/tests/.gitignore
 create mode 100644 src/lib/log/tests/message_initializer_1_unittest.cc
 create mode 100644 src/lib/log/tests/message_initializer_1a_unittest.cc
 create mode 100644 src/lib/log/tests/message_initializer_2_unittest.cc
 delete mode 100644 src/lib/log/tests/message_initializer_unittest.cc
 delete mode 100644 src/lib/log/tests/message_initializer_unittest_2.cc
 create mode 100644 src/lib/log/tests/run_initializer_unittests.cc
 create mode 100644 src/lib/nsas/.gitignore
 create mode 100644 src/lib/nsas/tests/.gitignore
 create mode 100644 src/lib/python/.gitignore
 create mode 100644 src/lib/python/isc/cc/tests/.gitignore
 create mode 100644 src/lib/python/isc/config/tests/.gitignore
 create mode 100644 src/lib/python/isc/datasrc/tests/.gitignore
 create mode 100644 src/lib/python/isc/datasrc/tests/testdata/new_minor_schema.sqlite3
 create mode 100644 src/lib/python/isc/datasrc/tests/testdata/newschema.sqlite3
 create mode 100644 src/lib/python/isc/datasrc/tests/testdata/oldschema.sqlite3
 delete mode 100644 src/lib/python/isc/datasrc/tests/testdata/test.sqlite3.nodiffs
 create mode 100644 src/lib/python/isc/log/tests/.gitignore
 create mode 100644 src/lib/python/isc/log_messages/dbutil_messages.py
 create mode 100644 src/lib/python/isc/log_messages/server_common_messages.py
 create mode 100644 src/lib/python/isc/log_messages/work/.gitignore
 create mode 100644 src/lib/python/isc/notify/tests/.gitignore
 create mode 100644 src/lib/python/isc/server_common/Makefile.am
 copy src/lib/python/isc/{bind10 => server_common}/__init__.py (100%)
 create mode 100644 src/lib/python/isc/server_common/server_common_messages.mes
 create mode 100644 src/lib/python/isc/server_common/tests/Makefile.am
 create mode 100644 src/lib/python/isc/server_common/tests/tsig_keyring_test.py
 create mode 100644 src/lib/python/isc/server_common/tsig_keyring.py
 create mode 100644 src/lib/resolve/.gitignore
 create mode 100644 src/lib/resolve/tests/.gitignore
 create mode 100644 src/lib/server_common/.gitignore
 create mode 100644 src/lib/server_common/tests/.gitignore
 create mode 100644 src/lib/statistics/tests/.gitignore
 create mode 100644 src/lib/testutils/testdata/.gitignore
 create mode 100755 src/lib/testutils/testdata/auth_test.sqlite3
 create mode 100644 src/lib/testutils/testdata/rwtest.sqlite3
 create mode 100644 src/lib/util/io/pktinfo_utilities.h
 create mode 100644 src/lib/util/python/.gitignore
 create mode 100644 src/lib/util/range_utilities.h
 create mode 100644 src/lib/util/tests/.gitignore
 create mode 100644 src/lib/util/tests/range_utilities_unittest.cc
 create mode 100644 src/lib/util/unittests/resource.cc
 create mode 100644 src/lib/util/unittests/resource.h
 create mode 100644 src/lib/xfr/tests/.gitignore
 create mode 100644 tests/lettuce/.gitignore
 create mode 100644 tests/lettuce/configurations/.gitignore
 create mode 100644 tests/lettuce/configurations/bindctl/bindctl.config.orig
 create mode 100644 tests/lettuce/configurations/bindctl_commands.config.orig
 create mode 100644 tests/lettuce/configurations/default.config
 create mode 100644 tests/lettuce/configurations/example.org.inmem.config
 create mode 100644 tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
 create mode 100644 tests/lettuce/configurations/multi_instance/.gitignore
 create mode 100644 tests/lettuce/configurations/multi_instance/multi_auth.config.orig
 create mode 100644 tests/lettuce/configurations/nsec3/nsec3_auth.config
 copy {src/lib/testutils/testdata => tests/lettuce/configurations/nsec3}/rfc5155-example.zone.signed (100%)
 create mode 100644 tests/lettuce/configurations/resolver/.gitignore
 create mode 100644 tests/lettuce/data/.gitignore
 create mode 100644 tests/lettuce/data/commands/bad_command
 create mode 100644 tests/lettuce/data/commands/directives
 copy AUTHORS => tests/lettuce/data/commands/empty (100%)
 create mode 100644 tests/lettuce/data/commands/nested
 create mode 100644 tests/lettuce/data/commands/nested1
 create mode 100644 tests/lettuce/data/example.org
 create mode 100644 tests/lettuce/data/ixfr-out/.gitignore
 delete mode 100644 tests/lettuce/data/ixfr-out/zones.slite3
 create mode 100644 tests/lettuce/data/ixfr-out/zones.sqlite3
 create mode 100644 tests/lettuce/features/default.feature
 create mode 100644 tests/lettuce/features/inmemory_over_sqlite3.feature
 create mode 100644 tests/lettuce/features/multi_instance.feature
 create mode 100644 tests/lettuce/features/nsec3_auth.feature
 create mode 100644 tests/lettuce/features/queries.feature
 create mode 100644 tests/lettuce/features/terrain/.gitignore
 create mode 100755 tests/lettuce/run_lettuce.sh
 mode change 100755 => 100644 tests/lettuce/setup_intree_bind10.sh.in
 create mode 100644 tests/system/.gitignore
 create mode 100644 tests/system/bindctl/nsx1/.gitignore
 create mode 100644 tests/system/glue/.gitignore
 create mode 100644 tests/system/glue/nsx1/.gitignore
 create mode 100644 tests/system/ixfr/.gitignore
 create mode 100644 tests/system/ixfr/in-1/.gitignore
 create mode 100644 tests/system/ixfr/in-2/.gitignore
 create mode 100644 tests/system/ixfr/in-2/ns1/.gitignore
 create mode 100644 tests/system/ixfr/in-2/nsx2/.gitignore
 create mode 100644 tests/system/ixfr/in-3/.gitignore
 create mode 100644 tests/system/ixfr/in-4/.gitignore
 create mode 100644 tests/tools/badpacket/.gitignore
 create mode 100644 tests/tools/badpacket/tests/.gitignore
 create mode 100644 tests/tools/perfdhcp/.gitignore
 create mode 100755 tools/git-obsolete-branch.py

-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..da9d3b4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+*.la
+*.lo
+*.o
+.deps/
+.libs/
+__pycache__/
+Makefile
+Makefile.in
+TAGS
+
+/aclocal.m4
+/autom4te.cache/
+/config.guess
+/config.h
+/config.h.in
+/config.log
+/config.report
+/config.status
+/config.sub
+/configure
+/cscope.files
+/cscope.out
+/depcomp
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/py-compile
+/stamp-h1
+
+/dns++.pc
diff --git a/COPYING b/COPYING
index 557bdfb..63717af 100644
--- a/COPYING
+++ b/COPYING
@@ -11,3 +11,9 @@ 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.
+
+-----------------------------------------------------------------------------
+
+The ext/asio and ext/coroutine code is externally maintained and
+distributed under the Boost Software License, Version 1.0.
+(See accompanying file ext/LICENSE_1_0.txt.)
diff --git a/ChangeLog b/ChangeLog
index 777cbdf..4233de8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,11 +1,352 @@
-3XX.	[func]	tomek
+434.	[func]	tomek
 	libdhcp++: Linux interface detection refactored. The code is
 	now cleaner. Tests better support certain versions of ifconfig.
 	(Trac #1528, git TBD)
 
-382.	[func]	jelte
+433.	[func]		tomek
+	libdhcp++: Option6 and Pkt6 now follow the same design as
+	options and packet for DHCPv4. General code refactoring after
+	end of 2011 year release.
+	(Trac #1540, git a40b6c665617125eeb8716b12d92d806f0342396)
+
+432.	[bug]*		muks
+	BIND 10 now installs its header files in a BIND 10 specific
+	sub-directory in the install prefix.
+	(Trac #1930, git fcf2f08db9ebc2198236bfa25cf73286821cba6b)
+
+431.	[func]*		muks
+	BIND 10 no longer starts b10-stats-httpd by default.
+	(Trac #1885, git 5c8bbd7ab648b6b7c48e366e7510dedca5386f6c)
+
+430.	[bug]		jelte
+	When displaying configuration data, bindctl no longer treats
+	optional list items as an error, but shows them as an empty list.
+	(Trac #1520, git 0f18039bc751a8f498c1f832196e2ecc7b997b2a)
+
+429.	[func]		jelte
+	Added an 'execute' component to bindctl, which executes either a set
+	of commands from a file or a built-in set of commands. Currently,
+	only 'init_authoritative_server' is provided as a built-in set, but
+	it is expected that more will be added later.
+	(Trac #1843, git 551657702a4197ef302c567b5c0eaf2fded3e121)
+
+428.	[bug]		marcin
+	perfdhcp: bind to local address to allow reception of replies from IPv6
+	DHCP servers.
+	(Trac #1908, git 597e059afaa4a89e767f8f10d2a4d78223af3940)
+
+427.	[bug]		jinmei
+	libdatasrc, b10-xfrin: the zone updater for database-based data
+	sources now correctly distinguishes NSEC3-related RRs (NSEC3 and
+	NSEC3-covering RRSIG) from others, and the SQLite3 implementation
+	now manipulates them in the separate table for the NSEC3 namespace.
+	As a result b10-xfrin now correctly updates NSEC3-signed zones by
+	inbound zone transfers.
+	(Trac #1891, git 672f129700dae33b701bb02069cf276238d66be3)
+
+426.	[bug]		vorner
+	The NSEC3 records are now included when transferring a signed zone out.
+	(Trac #1782, git 36efa7d10ecc4efd39d2ce4dfffa0cbdeffa74b0)
+
+425.	[func]*		muks
+	Don't autostart b10-auth, b10-xfrin, b10-xfrout and b10-zonemgr in
+	the default configuration.
+	(Trac #1818, git 31de885ba0409f54d9a1615eff5a4b03ed420393)
+
+424.	[bug]		jelte
+	Fixed a bug in bindctl where in some cases, configuration settings
+	in a named set could disappear, if a child element is modified.
+	(Trac #1491, git 00a36e752802df3cc683023d256687bf222e256a)
+
+423.	[bug]		jinmei
+	The database based zone iterator now correctly resets mixed TTLs
+	of the same RRset (when that happens) to the lowest one.  The
+	previous implementation could miss lower ones if it appears in a
+	later part of the RRset.
+	(part of Trac #1791, git f1f0bc00441057e7050241415ee0367a09c35032)
+
+422.	[bug]		jinmei
+	The database based zone iterator now separates RRSIGs of the same
+	name and type but for different covered types.
+	(part of Trac #1791, git b4466188150a50872bc3c426242bc7bba4c5f38d)
+
+421.	[build]		jinmei
+	Made sure BIND 10 can be built with clang++ 3.1.  (It failed on
+	MacOS 10.7 using Xcode 4.3, but it's more likely to be a matter of
+	clang version.)
+	(Trac #1773, git ceaa247d89ac7d97594572bc17f005144c5efb8d)
+
+420.	[bug]*		jinmei, stephen
+	Updated the DB schema used in the SQLite3 data source so it can
+	use SQL indices more effectively.  The previous schema had several
+	issues in this sense and could be very slow for some queries on a
+	very large zone (especially for negative answers).  This change
+	requires a major version up of the schema; use b10-dbutil to
+	upgrade existing database files.  Note: 'make install' will fail
+	unless old DB files installed in the standard location have been
+	upgraded.
+	(Trac #324, git 8644866497053f91ada4e99abe444d7876ed00ff)
+
+419.	[bug]		jelte
+	JSON handler has been improved; escaping now works correctly
+	(including quotes in strings), and it now rejects more types of
+	malformed input.
+	(Trac #1626, git 3b09268518e4e90032218083bcfebf7821be7bd5)
+
+418.	[bug]		vorner
+	Fixed crash in bindctl when config unset was called.
+	(Trac #1715, git 098da24dddad497810aa2787f54126488bb1095c)
+
+417.	[bug]		jelte
+	The notify-out code now looks up notify targets in their correct
+	zones (and no longer just in the zone that the notify is about).
+	(Trac #1535, git 66300a3c4769a48b765f70e2d0dbf8bbb714435b)
+
+416.	[func]*		jelte
+	The implementations of ZoneFinder::find() now throw an OutOfZone
+	exception when the name argument is not in or below the zone this
+	zonefinder contains.
+	(Trac #1535, git 66300a3c4769a48b765f70e2d0dbf8bbb714435b)
+
+bind10-devel-20120329 released on March 29, 2012
+
+415.	[doc]		jinmei, jreed
+	BIND 10 Guide updated to now describe the in-memory data source
+	configurations for b10-auth.
+	(Trac #1732, git 434d8db8dfcd23a87b8e798e5702e91f0bbbdcf6)
+
+414.	[bug]		jinmei
+	b10-auth now correctly handles delegation from an unsigned zone
+	(defined in the in-memory data source) when the query has DNSSEC
+	DO bit on.  It previously returned SERVFAIL.
+	(Trac #1836, git 78bb8f4b9676d6345f3fdd1e5cc89039806a9aba)
+
+413.	[func]		stephen, jelte
+	Created a new tool b10-dbutil, that can check and upgrade database
+	schemas, to be used when incompatible changes are introduced in the
+	backend database schema. Currently it only supports sqlite3 databases.
+	Note: there's no schema change that requires this utility as of
+	the March 29th release.  While running it shouldn't break
+	an existing database file, it should be even more advisable not to
+	run it at the moment.
+	(Trac #963, git 49ba2cf8ac63246f389ab5e8ea3b3d081dba9adf)
+
+412.	[func]		jelte
+	Added a command-line option '--clear-config' to bind10, which causes
+	the system to create a backup of the existing configuration database
+	file, and start out with a clean default configuration. This can be
+	used if the configuration file is corrupted to the point where it
+	cannot be read anymore, and BIND 10 refuses to start. The name of
+	the backup file can be found in the logs (CFGMGR_RENAMED_CONFIG_FILE).
+	(Trac #1443, git 52b36c921ee59ec69deefb6123cbdb1b91dc3bc7)
+
+411.	[func]		muks
+	Add a -i/--no-kill command-line argument to bind10, which stops
+	it from sending SIGTERM and SIGKILL to other b10 processes when
+	they're shutting down.
+	(Trac #1819, git 774554f46b20ca5ec2ef6c6d5e608114f14e2102)
+
+410.	[bug]		jinmei
+	Python CC library now ensures write operations transmit all given
+	data (unless an error happens).  Previously it didn't check the
+	size of transmitted data, which could result in partial write on
+	some systems (notably on OpenBSD) and subsequently cause system
+	hang up or other broken state.  This fix specifically solves start
+	up failure on OpenBSD.
+	(Trac #1829, git 5e5a33213b60d89e146cd5e47d65f3f9833a9297)
+
+409.	[bug]		jelte
+	Fixed a parser bug in bindctl that could make bindctl crash. Also
+	improved 'command help' output; argument order is now shown
+	correctly, and parameter descriptions are shown as well.
+	(Trac #1172, git bec26c6137c9b0a59a3a8ca0f55a17cfcb8a23de)
+
+408.	[bug]		stephen, jinmei
+	b10-auth now filters out duplicate RRsets when building a
+	response message using the new query handling logic.  It's
+	currently only used with the in-memory data source, but will
+	also be used for others soon.
+	(Trac #1688, git b77baca56ffb1b9016698c00ae0a1496d603d197)
+
+407.	[build]		haikuo
+	Remove "--enable-boost-threads" switch in configure command. This
+	thread lock mechanism is useless for bind10 and causes performance 
+	hits. 
+	(Trac #1680, git 9c4d0cadf4adc802cc41a2610dc2c30b25aad728)
+
+406.	[bug]		muks
+	On platforms such as OpenBSD where pselect() is not available,
+	make a wrapper around select() in perfdhcp.
+	(Trac #1639, git 6ea0b1d62e7b8b6596209291aa6c8b34b8e73191)
+
+405.	[bug]		jinmei
+	Make sure disabling Boost threads if the default configuration is
+	to disable it for the system.  This fixes a crash and hang up
+	problem on OpenBSD, where the use of Boost thread could be
+	different in different program files depending on the order of
+	including various header files, and could introduce inconsistent
+	states between a library and a program.  Explicitly forcing the
+	original default throughout the BIND 10 build environment will
+	prevent this from happening.
+	(Trac #1727, git 23f9c3670b544c5f8105958ff148aeba050bc1b4)
+
+404.	[bug]		naokikambe
+	The statistic counters are now properly accumulated across multiple
+	instances of b10-auth (if there are multiple instances), instead of
+	providing result for random instance.
+	(Trac #1751, git 3285353a660e881ec2b645e1bc10d94e5020f357)
+
+403.	[build]*	jelte
+	The configure option for botan (--with-botan=PATH) is replaced by
+	--with-botan-config=PATH, which takes a full path to a botan-config
+	script, instead of the botan 'install' directory. Also, if not
+	provided, configure will try out config scripts and pkg-config
+	options until it finds one that works.
+	(Trac #1640, git 582bcd66dbd8d39f48aef952902f797260280637)
+
+402.	[func]		jelte
+	b10-xfrout now has a visible command to send out notifies for
+	a given zone, callable from bindctl. Xfrout notify <zone> [class]
+	(Trac #1321, git 0bb258f8610620191d75cfd5d2308b6fc558c280)
+
+401.	[func]*		jinmei
+	libdns++: updated the internal implementation of the
+	MessageRenderer class.  This is mostly a transparent change, but
+	the new version now doesn't allow changing compression mode in the
+	middle of rendering (which shouldn't be an issue in practice).
+	On the other hand, name compression performance was significantly
+	improved: depending on the number of names, micro benchmark tests
+	showed the new version is several times faster than the previous
+	version .
+	(Trac #1603, git 9a2a86f3f47b60ff017ce1a040941d0c145cfe16)
+
+400.	[bug]		stephen
+	Fix crash on Max OS X 10.7 by altering logging so as not to allocate
+	heap storage in the static initialization of logging objects.
+	(Trac #1698, git a8e53be7039ad50d8587c0972244029ff3533b6e)
+
+399.	[func]		muks
+	Add support for the SSHFP RR type (RFC 4255).
+	(Trac #1136, git ea5ac57d508a17611cfae9d9ea1c238f59d52c51)
+
+398.	[func]		jelte
+	The b10-xfrin module now logs more information on successful
+	incoming transfers. In the case of IXFR, it logs the number of
+	changesets, and the total number of added and deleted resource
+	records. For AXFR (or AXFR-style IXFR), it logs the number of
+	resource records. In both cases, the number of overhead DNS
+	messages, runtime, amount of wire data, and transfer speed are logged.
+	(Trac #1280, git 2b01d944b6a137f95d47673ea8367315289c205d)
+
+397.	[func]		muks
+	The boss process now gives more helpful description when a
+	sub-process exits due to a signal.
+	(Trac #1673, git 1cd0d0e4fc9324bbe7f8593478e2396d06337b1e)
+
+396.	[func]*		jinmei
+	libdatasrc: change the return type of ZoneFinder::find() so it can
+	contain more context of the search, which can be used for
+	optimizing post find() processing.  A new method getAdditional()
+	is added to it for finding additional RRsets based on the result
+	of find().  External behavior shouldn't change.  The query
+	handling code of b10-auth now uses the new interface.
+	(Trac #1607, git 2e940ea65d5b9f371c26352afd9e66719c38a6b9)
+
+395.	[bug]		jelte
+	The log message compiler now errors (resulting in build failures) if
+	duplicate log message identifiers are found in a single message file.
+	Renamed one duplicate that was found (RESOLVER_SHUTDOWN, renamed to
+	RESOLVER_SHUTDOWN_RECEIVED).
+	(Trac #1093, git f537c7e12fb7b25801408f93132ed33410edae76)
+	(Trac #1741, git b8960ab85c717fe70ad282e0052ac0858c5b57f7)
+
+394.	[bug]		jelte
+	b10-auth now catches any exceptions during response building; if any
+	datasource either throws an exception or causes an exception to be
+	thrown, the message processing code will now catch it, log a debug
+	message, and return a SERVFAIL response.
+	(Trac #1612, git b5740c6b3962a55e46325b3c8b14c9d64cf0d845)
+
+393.	[func]		jelte
+	Introduced a new class LabelSequence in libdns++, which provides
+	lightweight accessor functionality to the Name class, for more
+	efficient comparison of parts of names.
+	(Trac #1602, git b33929ed5df7c8f482d095e96e667d4a03180c78)
+
+392.	[func]*		jinmei
+	libdns++: revised the (Abstract)MessageRenderer class so that it
+	has a default internal buffer and the buffer can be temporarily
+	switched.  The constructor interface was modified, and a new
+	method setBuffer() was added.
+	(Trac #1697, git 9cabc799f2bf9a3579dae7f1f5d5467c8bb1aa40)
+
+391.	[bug]*		vorner
+	The long time unused configuration options of Xfrout "log_name",
+	"log_file", "log_severity", "log_version" and "log_max_bytes" were
+	removed, as they had no effect (Xfrout uses the global logging
+	framework).  However, if you have them set, you need to remove
+	them from the configuration file or the configuration will be
+	rejected.
+	(Trac #1090, git ef1eba02e4cf550e48e7318702cff6d67c1ec82e)
+
+bind10-devel-20120301 released on March 1, 2012
+
+390.	[bug]		vorner
+	The UDP IPv6 packets are now correctly fragmented for maximum
+	guaranteed MTU, so they won't get lost because being too large
+	for some hop.
+	(Trac #1534, git ff013364643f9bfa736b2d23fec39ac35872d6ad)
+
+389.	[func]*		vorner
+	Xfrout now uses the global TSIG keyring, instead of its own. This
+	means the keys need to be set only once (in tsig_keys/keys).
+	However, the old configuration of Xfrout/tsig_keys need to be
+	removed for Xfrout to work.
+	(Trac #1643, git 5a7953933a49a0ddd4ee1feaddc908cd2285522d)
+
+388.	[func]		jreed
+	Use prefix "sockcreator-" for the private temporary directory
+	used for b10-sockcreator communication.
+	(git b98523c1260637cb33436964dc18e9763622a242)
+
+387.	[build]		muks
+	Accept a --without-werror configure switch so that some builders can
+	disable the use of -Werror in CFLAGS when building.
+	(Trac #1671, git 8684a411d7718a71ad9fb616f56b26436c4f03e5)
+
+386.	[bug]		jelte
+	Upon initial sqlite3 database creation, the 'diffs' table is now
+	always created. This already happened most of the time, but there
+	are a few cases where it was skipped, resulting in potential errors
+	in xfrout later.
+	(Trac #1717, git 30d7686cb6e2fa64866c983e0cfb7b8fabedc7a2)
+
+385.	[bug]		jinmei
+	libdns++: masterLoad() didn't accept comments placed at the end of
+	an RR.  Due to this the in-memory data source cannot load a master
+	file for a signed zone even if it's preprocessed with BIND 9's
+	named-compilezone.
+	Note: this fix is considered temporary and still only accepts some
+	limited form of such comments.  The main purpose is to allow the
+	in-memory data source to load any signed or unsigned zone files as
+	long as they are at least normalized with named-compilezone.
+	(Trac #1667, git 6f771b28eea25c693fe93a0e2379af924464a562)
+
+384.	[func]		jinmei, jelte, vorner, haikuo, kevin
+	b10-auth now supports NSEC3-signed zones in the in-memory data
+	source.
+	(Trac #1580, #1581, #1582, #1583, #1584, #1585, #1587, and
+	other related changes to the in-memory data source)
+
+383.	[build]		jinmei
+	Fixed build failure on MacOS 10.7 (Lion) due to the use of
+	IPV6_PKTINFO; the OS requires a special definition to make it
+	visible to the compiler.
+	(Trac #1633, git 19ba70c7cc3da462c70e8c4f74b321b8daad0100)
+
+382.	[func]		jelte
 	b10-auth now also experimentally supports statistics counters of
-	the rcode reponses it sends. The counters can be shown as
+	the rcode responses it sends. The counters can be shown as
 	rcode.<code name>, where code name is the lowercase textual
 	representation of the rcode (e.g. "noerror", "formerr", etc.).
 	Same note applies as for opcodes, see changelog entry 364.
@@ -55,11 +396,11 @@
 	(Trac #1570, git 2858b2098a10a8cc2d34bf87463ace0629d3670e)
 
 375.	[func]		jelte
-	Modules now inform the system when they are stopping. As a result, they
-	are removed from the 'active modules' list in bindctl, which can then
-	inform the user directly when it tries to send them a command or
-	configuration update. Previously this would result in a 'not
-	responding' error instead of 'not running'.
+	Modules now inform the system when they are stopping. As a result,
+	they are removed from the 'active modules' list in bindctl, which
+	can then inform the user directly when it tries to send them a
+	command or configuration update.  Previously this would result
+	in a 'not responding' error instead of 'not running'.
 	(Trac #640, git 17e78fa1bb1227340aa9815e91ed5c50d174425d)
 
 374.	[func]*		stephen
@@ -95,10 +436,11 @@
 	(Trac #1575, git 2c421b58e810028b303d328e4e2f5b74ea124839)
 
 369.	[func]		vorner
-	The SocketRequestor provides more information about what error happened
-	when it throws, by using subclasses of the original exception. This way
-	a user not interested in the difference can still use the original
-	exception, while it can be recognized if necessary.
+	The SocketRequestor provides more information about what error
+	happened when it throws, by using subclasses of the original
+	exception. This way a user not interested in the difference can
+	still use the original exception, while it can be recognized if
+	necessary.
 	(Trac #1542, git 2080e0316a339fa3cadea00e10b1ec4bc322ada0)
 
 368.	[func]*		jinmei
@@ -156,7 +498,8 @@ bind10-devel-20120119 released on January 19, 2012
 	configuration.  If your b10-config.db contains "setuid" for
 	Boss.components, you'll need to remove that entry by hand before
 	starting BIND 10.
-	(Trac #1508-#1510, git edc5b3c12eb45437361484c843794416ad86bb00)
+	(Trac #1508, #1509, #1510,
+	git edc5b3c12eb45437361484c843794416ad86bb00)
 
 361.	[func]		vorner,jelte,jinmei
 	The socket creator is now used to provide sockets. It means you can
diff --git a/Makefile.am b/Makefile.am
index cc91a56..54216b6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,7 @@
+ACLOCAL_AMFLAGS = -I m4macros ${ACLOCAL_FLAGS}
+# ^^^^^^^^ This has to be the first line and cannot come later in this
+# Makefile.am due to some bork in some versions of autotools.
+
 SUBDIRS = compatcheck doc src tests
 USE_LCOV=@USE_LCOV@
 LCOV=@LCOV@
@@ -79,7 +83,7 @@ report-coverage: report-cpp-coverage report-python-coverage
 
 # for static C++ check using cppcheck (when available)
 cppcheck:
-	cppcheck --enable=all --suppressions src/cppcheck-suppress.lst \
+	cppcheck --enable=all --suppressions src/cppcheck-suppress.lst --inline-suppr \
 		--quiet --error-exitcode=1 \
 		--template '{file}:{line}: check_fail: {message} ({severity},{id})' \
 		src
@@ -398,3 +402,6 @@ EXTRA_DIST += ext/asio/asio/system_error.hpp
 EXTRA_DIST += ext/asio/asio/deadline_timer.hpp
 EXTRA_DIST += ext/asio/asio/stream_socket_service.hpp
 EXTRA_DIST += ext/coroutine/coroutine.h
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = dns++.pc
diff --git a/compatcheck/.gitignore b/compatcheck/.gitignore
new file mode 100644
index 0000000..180a3ec
--- /dev/null
+++ b/compatcheck/.gitignore
@@ -0,0 +1 @@
+/sqlite3-difftbl-check.py
diff --git a/compatcheck/Makefile.am b/compatcheck/Makefile.am
index 029578d..15ef017 100644
--- a/compatcheck/Makefile.am
+++ b/compatcheck/Makefile.am
@@ -1,8 +1,12 @@
-noinst_SCRIPTS = sqlite3-difftbl-check.py
-
 # We're going to abuse install-data-local for a pre-install check.
 # This is to be considered a short term hack and is expected to be removed
 # in a near future version.
 install-data-local:
-	$(PYTHON) sqlite3-difftbl-check.py \
-	$(localstatedir)/$(PACKAGE)/zone.sqlite3
+	if test -e $(localstatedir)/$(PACKAGE)/zone.sqlite3; then \
+		$(SHELL) $(top_builddir)/src/bin/dbutil/run_dbutil.sh --check \
+		$(localstatedir)/$(PACKAGE)/zone.sqlite3 || \
+		(echo "\nSQLite3 DB file schema version is old.  " \
+		"Please run: " \
+		"$(abs_top_builddir)/src/bin/dbutil/run_dbutil.sh --upgrade " \
+		"$(localstatedir)/$(PACKAGE)/zone.sqlite3";  exit 1) \
+	fi
diff --git a/compatcheck/sqlite3-difftbl-check.py.in b/compatcheck/sqlite3-difftbl-check.py.in
deleted file mode 100755
index e3b7b91..0000000
--- a/compatcheck/sqlite3-difftbl-check.py.in
+++ /dev/null
@@ -1,60 +0,0 @@
-#!@PYTHON@
-
-# Copyright (C) 2011  Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-import os, sqlite3, sys
-from optparse import OptionParser
-
-usage = 'usage: %prog [options] db_file'
-parser = OptionParser(usage=usage)
-parser.add_option("-u", "--upgrade", action="store_true",
-                  dest="upgrade", default=False,
-                  help="Upgrade the database file [default: %default]")
-(options, args) = parser.parse_args()
-if len(args) == 0:
-    parser.error('missing argument')
-
-db_file = args[0]
-
-# If the file doesn't exist, there's nothing to do
-if not os.path.exists(db_file):
-    sys.exit(0)
-
-conn = sqlite3.connect(db_file)
-cur = conn.cursor()
-try:
-    # This can be anything that works iff the "diffs" table exists
-    cur.execute('SELECT name FROM diffs DESC LIMIT 1')
-except sqlite3.OperationalError as ex:
-    # If it fails with 'no such table', create a new one or fail with
-    # warning depending on the --upgrade command line option.
-    if str(ex) == 'no such table: diffs':
-        if options.upgrade:
-            cur.execute('CREATE TABLE diffs (id INTEGER PRIMARY KEY, ' +
-                        'zone_id INTEGER NOT NULL, ' +
-                        'version INTEGER NOT NULL, ' +
-                        'operation INTEGER NOT NULL, ' +
-                        'name STRING NOT NULL COLLATE NOCASE, ' +
-                        'rrtype STRING NOT NULL COLLATE NOCASE, ' +
-                        'ttl INTEGER NOT NULL, rdata STRING NOT NULL)')
-        else:
-            sys.stdout.write('Found an older version of SQLite3 DB file: ' +
-                             db_file + '\n' + "Perform '" + os.getcwd() +
-                             "/sqlite3-difftbl-check.py --upgrade " +
-                             db_file + "'\n" +
-                             'before continuing install.\n')
-            sys.exit(1)
-conn.close()
diff --git a/configure.ac b/configure.ac
index de79551..af9125f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,10 +2,12 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20120127, bind10-dev at isc.org)
+AC_INIT(bind10-devel, 20120405, bind10-dev at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible
 AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4macros])
 
 # Checks for programs.
 AC_PROG_CXX
@@ -84,11 +86,6 @@ if test $enable_shared = no; then
 	AC_MSG_ERROR([BIND 10 requires shared libraries to be built])
 fi
 
-AC_ARG_ENABLE(boost-threads,
-AC_HELP_STRING([--enable-boost-threads],
-  [use boost threads. Currently this only means using its locks instead of dummy locks, in the cache and NSAS]),
-  use_boost_threads=$enableval, use_boost_threads=no)
-
 # allow configuring without setproctitle.
 AC_ARG_ENABLE(setproctitle-check,
 AC_HELP_STRING([--disable-setproctitle-check],
@@ -108,6 +105,10 @@ case "$host" in
 	LDFLAGS="$LDFLAGS -z now"
 	;;
 *-apple-darwin*)
+	# Starting with OSX 10.7 (Lion) we must choose which IPv6 API to use
+	# (RFC2292 or RFC3542).
+	CPPFLAGS="$CPPFLAGS -D__APPLE_USE_RFC_3542"
+
 	# libtool doesn't work perfectly with Darwin: libtool embeds the
 	# final install path in dynamic libraries and our loadable python
 	# modules always refer to that path even if it's loaded within the
@@ -123,6 +124,9 @@ case "$host" in
 *-netbsd*)
 	SET_ENV_LIBRARY_PATH=yes
 	;;
+*-openbsd*)
+	SET_ENV_LIBRARY_PATH=yes
+	;;
 esac
 AM_CONDITIONAL(SET_ENV_LIBRARY_PATH, test $SET_ENV_LIBRARY_PATH = yes)
 AC_SUBST(SET_ENV_LIBRARY_PATH)
@@ -270,7 +274,7 @@ AC_DEFUN([BIND10_CXX_TRY_FLAG], [
   bind10_save_CXXFLAGS="$CXXFLAGS"
   CXXFLAGS="$CXXFLAGS $1"
 
-  AC_LINK_IFELSE([int main(void){ return 0;} ],
+  AC_LINK_IFELSE([AC_LANG_SOURCE([int main(void){ return 0;}])],
                  [bind10_cxx_flag=yes], [bind10_cxx_flag=no])
   CXXFLAGS="$bind10_save_CXXFLAGS"
 
@@ -283,8 +287,6 @@ AC_DEFUN([BIND10_CXX_TRY_FLAG], [
   AC_MSG_RESULT([$bind10_cxx_flag])
 ])
 
-werror_ok=0
-
 # SunStudio compiler requires special compiler options for boost
 # (http://blogs.sun.com/sga/entry/boost_mini_howto)
 if test "$SUNCXX" = "yes"; then
@@ -292,7 +294,7 @@ CXXFLAGS="$CXXFLAGS -library=stlport4 -features=tmplife -features=tmplrefstatic"
 MULTITHREADING_FLAG="-mt"
 fi
 
-BIND10_CXX_TRY_FLAG(-Wno-missing-field-initializers,
+BIND10_CXX_TRY_FLAG([-Wno-missing-field-initializers],
 	[WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG="-Wno-missing-field-initializers"])
 AC_SUBST(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
@@ -310,19 +312,34 @@ case "$host" in
 	;;
 esac
 
+# Don't use -Werror if configured not to
+AC_ARG_WITH(werror,
+    AC_HELP_STRING([--with-werror], [Compile using -Werror (default=yes)]),
+    [
+     case "${withval}" in
+         yes) with_werror=1 ;;
+         no)  with_werror=0 ;;
+         *)   AC_MSG_ERROR(bad value ${withval} for --with-werror) ;;
+     esac],
+     [with_werror=1])
+
+werror_ok=0
+
 # Certain versions of gcc (g++) have a bug that incorrectly warns about
 # the use of anonymous name spaces even if they're closed in a single
 # translation unit.  For these versions we have to disable -Werror.
-CXXFLAGS_SAVED="$CXXFLAGS"
-CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
-AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
-AC_TRY_COMPILE([namespace { class Foo {}; }
-namespace isc {class Bar {Foo foo_;};} ],,
+if test $with_werror = 1; then
+   CXXFLAGS_SAVED="$CXXFLAGS"
+   CXXFLAGS="$CXXFLAGS $B10_CXXFLAGS -Werror"
+   AC_MSG_CHECKING(for in-TU anonymous namespace breakage)
+   AC_TRY_COMPILE([namespace { class Foo {}; }
+   namespace isc {class Bar {Foo foo_;};} ],,
 	[AC_MSG_RESULT(no)
 	 werror_ok=1
 	 B10_CXXFLAGS="$B10_CXXFLAGS -Werror"],
 	[AC_MSG_RESULT(yes)])
-CXXFLAGS="$CXXFLAGS_SAVED"
+   CXXFLAGS="$CXXFLAGS_SAVED"
+fi
 
 # Python 3.2 has an unused parameter in one of its headers. This
 # has been reported, but not fixed as of yet, so we check if we need
@@ -477,61 +494,163 @@ if test "$lcov" != "no"; then
 fi
 AC_SUBST(USE_LCOV)
 
+# Simplified, non-caching AC_CHECK_PROG
+# Searches $PATH for the existence of argument 2,
+# and sets the full path to the variable in argument 1.
+# if not found, and a third argument is given, the value
+# is set to that. If not, the value is untouched.
+# Does not take absolute paths into account at this point,
+# and also works for single files only (arguments are not
+# stripped like in AC_CHECK_PROG)
+AC_DEFUN([ACX_CHECK_PROG_NONCACHE], [
+    RESULT=""
+    IFS_SAVED="$IFS"
+    IFS=${PATH_SEPARATOR}
+    for cur_path in ${PATH} ; do
+      if test -e "${cur_path}/$2" ; then
+          RESULT="${cur_path}/$2"
+      fi
+    done
+    if test "$RESULT" = "" ; then
+        :
+        m4_ifvaln([$3], [$1=$3])
+    else
+        $1=$RESULT
+    fi
+    IFS="$IFS_SAVED"
+])
+
+# Botan helper test function
+# Tries to compile a botan program, given the output of the given
+# config tool
+# Arguments:
+# - name of tool (checked for path), must support --libs and --cflags
+# - fixed argument(s) for tool
+# - action if successful
+AC_DEFUN([ACX_TRY_BOTAN_TOOL], [
+    TOOL=$1
+    TOOL_ARG=$2
+    BOTAN_TOOL=""
+    ACX_CHECK_PROG_NONCACHE([BOTAN_TOOL], [${TOOL}])
+    AC_MSG_CHECKING([usability of ${TOOL} ${TOOL_ARG}])
+    if test "$BOTAN_TOOL" != "" ; then
+        if test -x ${BOTAN_TOOL}; then
+            BOTAN_LIBS=`$BOTAN_TOOL $TOOL_ARG --libs`
+            LIBS_SAVED=${LIBS}
+            LIBS="$LIBS $BOTAN_LIBS"
+            BOTAN_INCLUDES=`$BOTAN_TOOL $TOOL_ARG --cflags`
+            CPPFLAGS_SAVED=${CPPFLAGS}
+            CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
+            #AC_MSG_RESULT([found])
+            AC_LINK_IFELSE(
+                [AC_LANG_PROGRAM([#include <botan/botan.h>
+                                  #include <botan/hash.h>
+                                 ],
+                                 [using namespace Botan;
+                                  LibraryInitializer::initialize();
+                                  HashFunction *h = get_hash("MD5");
+                                 ])],
+                [ AC_MSG_RESULT([ok])
+                  $3
+                ],
+                [ AC_MSG_RESULT([not usable]) ]
+            )
+            LIBS=${LIBS_SAVED}
+            CPPFLAGS=${CPPFLAGS_SAVED}
+        else
+            AC_MSG_RESULT([not executable])
+        fi
+    else
+        AC_MSG_RESULT([not found])
+    fi
+    BOTAN_TOOL=""
+    AC_SUBST(BOTAN_TOOL)
+    ]
+)
+
 # Check for Botan
-botan_path="yes"
-AC_ARG_WITH([botan],
-  AC_HELP_STRING([--with-botan=PATH],
-    [specify the path to botan-config (PATH/bin/botan-config will be used)]),
-    [botan_path="$withval"])
-if test "${botan_path}" = "no" ; then
+#
+# Unless --with-botan-config is given, we first try to find these config
+# scripts ourselves. Unfortunately, on some systems, these scripts do not
+# provide the correct implementation, so for each script found, we try
+# a compilation test (ACX_TRY_BOTAN_TOOL). If none are found, or none of
+# them work, we see if pkg-config is available. If so, we try the several
+# potential pkg-config .pc files. Again, on some systems, these can return
+# incorrect information as well, so the try-compile test is repeated for
+# each.
+#
+# If a working config script or pkgconfig file is found, we then munge its
+# output for use in our Makefiles, and to make sure it works, another header
+# and compilation test is done (this should also check whether we can compile
+# against botan should neither -config scripts nor pkgconfig data exist).
+#
+botan_config="yes"
+AC_ARG_WITH([botan-config],
+  AC_HELP_STRING([--with-botan-config=PATH],
+    [specify the path to the botan-config script]),
+    [botan_config="$withval"])
+if test "${botan_config}" = "no" ; then
     AC_MSG_ERROR([Need botan for libcryptolink])
 fi
-if test "${botan_path}" != "yes" ; then
-    if test -x "${botan_path}/bin/botan-config" ; then
-        BOTAN_CONFIG="${botan_path}/bin/botan-config"
+if test "${botan_config}" != "yes" ; then
+    if test -x "${botan_config}" ; then
+        if test -d "${botan_config}" ; then
+            AC_MSG_ERROR([${botan_config} is a directory])
+        else
+            BOTAN_CONFIG="${botan_config}"
+        fi
     else
-        AC_MSG_ERROR([${botan_path}/bin/botan-config not found])
+        AC_MSG_ERROR([--with-botan-config should point to a botan-config program and not a directory (${botan_config})])
     fi
 else
-    # First see if pkg-config knows of it.
-    # Unfortunately, the botan.pc files have their minor version in them
-    # too, so we need to try them one by one
     BOTAN_CONFIG=""
-    AC_PATH_PROG([PKG_CONFIG], [pkg-config])
-    if test "$PKG_CONFIG" != "" ; then
-        BOTAN_VERSIONS="botan-1.10 botan-1.9 botan-1.8"
-        for version in $BOTAN_VERSIONS; do
-            AC_MSG_CHECKING([Checking botan version with pkg-config $version])
-            
-            if [ $PKG_CONFIG --exists ${version} ]; then
-                AC_MSG_RESULT([found])
-                BOTAN_CONFIG="$PKG_CONFIG ${version}"
+    # first try several possible names of the config script
+    # (botan-config-1.8 is there just in case, the official name change
+    # came later)
+    BOTAN_CONFIG_VERSIONS="botan-config-1.10 botan-config-1.9 botan-config-1.8 botan-config"
+    for botan_config in $BOTAN_CONFIG_VERSIONS; do
+        ACX_TRY_BOTAN_TOOL([$botan_config],,
+                           [ BOTAN_CONFIG="$botan_config"  ]
+                          )
+        if test "$BOTAN_CONFIG" != "" ; then
+            break
+        fi
+    done
+    if test "$BOTAN_CONFIG" = "" ; then
+        AC_PATH_PROG([PKG_CONFIG], [pkg-config])
+        if test "$PKG_CONFIG" != "" ; then
+            # Ok so no script found, see if pkg-config knows of it.
+            # Unfortunately, the botan.pc files also have their minor version
+            # in their name, so we need to try them one by one
+            BOTAN_VERSIONS="botan-1.10 botan-1.9 botan-1.8"
+            for version in $BOTAN_VERSIONS; do
+                ACX_TRY_BOTAN_TOOL([pkg-config], ["$version --silence-errors"],
+                                   [ BOTAN_CONFIG="$PKG_CONFIG $version" ]
+                                  )
+            if test "$BOTAN_CONFIG" != "" ; then
                 break
-            else
-                AC_MSG_RESULT([not found])
             fi
-        done
-    fi
-    # If we had no pkg-config, or it didn't know about botan, use botan-config
-    if test "$BOTAN_CONFIG" = "" ; then
-        AC_PATH_PROG([BOTAN_CONFIG], [botan-config])
+            done
+        fi
     fi
 fi
 
-BOTAN_LIBS=`${BOTAN_CONFIG} --libs`
-BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
-
-# We expect botan-config --libs to contain -L<path_to_libbotan>, but
-# this is not always the case.  As a heuristics workaround we add
-# -L`botan-config --prefix/lib` in this case (if not present already).
-# Same for BOTAN_INCLUDES (but using include instead of lib) below.
-if [ $BOTAN_CONFIG --prefix >/dev/null 2>&1 ] ; then
-    echo ${BOTAN_LIBS} | grep -- -L > /dev/null || \
-        BOTAN_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LIBS}"
-    echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
-        BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
+if test "x${BOTAN_CONFIG}" != "x"
+then
+    BOTAN_LIBS=`${BOTAN_CONFIG} --libs`
+    BOTAN_INCLUDES=`${BOTAN_CONFIG} --cflags`
+
+    # We expect botan-config --libs to contain -L<path_to_libbotan>, but
+    # this is not always the case.  As a heuristics workaround we add
+    # -L`botan-config --prefix/lib` in this case (if not present already).
+    # Same for BOTAN_INCLUDES (but using include instead of lib) below.
+    if [ ${BOTAN_CONFIG} --prefix >/dev/null 2>&1 ] ; then
+        echo ${BOTAN_LIBS} | grep -- -L > /dev/null || \
+            BOTAN_LIBS="-L`${BOTAN_CONFIG} --prefix`/lib ${BOTAN_LIBS}"
+        echo ${BOTAN_INCLUDES} | grep -- -I > /dev/null || \
+            BOTAN_INCLUDES="-I`${BOTAN_CONFIG} --prefix`/include ${BOTAN_INCLUDES}"
+    fi
 fi
-
 # botan-config script (and the way we call pkg-config) returns -L and -l
 # as one string, but we need them in separate values
 BOTAN_LDFLAGS=
@@ -563,6 +682,9 @@ AC_SUBST(BOTAN_LDFLAGS)
 AC_SUBST(BOTAN_LIBS)
 AC_SUBST(BOTAN_INCLUDES)
 
+# Even though chances are high we already performed a real compilation check
+# in the search for the right (pkg)config data, we try again here, to
+# be sure.
 CPPFLAGS_SAVED=$CPPFLAGS
 CPPFLAGS="$BOTAN_INCLUDES $CPPFLAGS"
 LIBS_SAVED="$LIBS"
@@ -578,7 +700,11 @@ AC_LINK_IFELSE(
                          ])],
         [AC_MSG_RESULT([checking for Botan library... yes])],
         [AC_MSG_RESULT([checking for Botan library... no])
-         AC_MSG_ERROR([Needs Botan library 1.8 or higher])]
+         AC_MSG_ERROR([Needs Botan library 1.8 or higher. On some systems,
+         the botan package has a few missing dependencies (libbz2 and
+         libgmp), if libbotan has been installed and you see this error,
+         try upgrading to a higher version of botan or installing libbz2
+         and libgmp.])]
 )
 CPPFLAGS=$CPPFLAGS_SAVED
 LIBS=$LIBS_SAVED
@@ -658,70 +784,23 @@ if test "${boost_include_path}" ; then
 fi
 AC_CHECK_HEADERS([boost/shared_ptr.hpp boost/foreach.hpp boost/interprocess/sync/interprocess_upgradable_mutex.hpp boost/date_time/posix_time/posix_time_types.hpp boost/bind.hpp boost/function.hpp],,
   AC_MSG_ERROR([Missing required header files.]))
-CPPFLAGS="$CPPFLAGS_SAVES"
-AC_SUBST(BOOST_INCLUDES)
-
 
-if test "${use_boost_threads}" = "yes" ; then
-    AC_DEFINE([USE_BOOST_THREADS], [], [Use boost threads])
-
-    # Using boost::mutex can result in requiring libboost_thread with older
-    # versions of Boost.  We'd like to avoid relying on a compiled Boost library
-    # whenever possible, so we need to check for it step by step.
-    #
-    # NOTE: another fix of this problem is to simply require newer versions of
-    # boost.  If we choose that solution we should simplify the following tricky
-    # checks accordingly and all Makefile.am's that refer to NEED_LIBBOOST_THREAD.
-    AC_MSG_CHECKING(for boost::mutex)
-    CPPFLAGS_SAVES="$CPPFLAGS"
-    LIBS_SAVES="$LIBS"
-    CPPFLAGS="$BOOST_INCLUDES $CPPFLAGS $MULTITHREADING_FLAG"
-    need_libboost_thread=0
-    need_sunpro_workaround=0
-    AC_TRY_LINK([
-    #include <boost/thread.hpp>
-    ],[
-    boost::mutex m;
-    ],
-        [ AC_MSG_RESULT(yes (without libboost_thread)) ],
-        # there is one specific problem with SunStudio 5.10
-        # where including boost/thread causes a compilation failure
-        # There is a workaround in boost but it checks the version not being 5.10
-        # This will probably be fixed in the future, in which case this
-        # is only a temporary workaround
-        [ AC_TRY_LINK([
-    #if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-    #undef __SUNPRO_CC
-    #define __SUNPRO_CC 0x5090
-    #endif
-    #include <boost/thread.hpp>
-    ],[
-    boost::mutex m;
-    ],
-        [ AC_MSG_RESULT(yes (with SUNOS workaround))
-          need_sunpro_workaround=1 ],
-            [ LIBS=" $LIBS -lboost_thread"
-          AC_TRY_LINK([
-    #include <boost/thread.hpp>
-    ],[
-    boost::mutex m;
-    ],
-              [ AC_MSG_RESULT(yes (with libboost_thread))
-                need_libboost_thread=1 ],
-              [ AC_MSG_RESULT(no)
-                AC_MSG_ERROR([boost::mutex cannot be linked in this build environment.
-    Perhaps you are using an older version of Boost that requires libboost_thread for the mutex support, which does not appear to be available.
-    You may want to check the availability of the library or to upgrade Boost.])
-              ])])])
-    CPPFLAGS="$CPPFLAGS_SAVES"
-    LIBS="$LIBS_SAVES"
-    AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test $need_libboost_thread = 1)
-    if test $need_sunpro_workaround = 1; then
-        AC_DEFINE([NEED_SUNPRO_WORKAROUND], [], [Need boost sunstudio workaround])
-    fi
-else
-    AM_CONDITIONAL(NEED_LIBBOOST_THREAD, test "${use_boost_threads}" = "yes")
-fi
+# Detect whether Boost tries to use threads by default, and, if not,
+# make it sure explicitly.  In some systems the automatic detection
+# may depend on preceding header files, and if inconsistency happens
+# it could lead to a critical disruption.
+AC_MSG_CHECKING([whether Boost tries to use threads])
+AC_TRY_COMPILE([
+#include <boost/config.hpp>
+#ifdef BOOST_HAS_THREADS
+#error "boost will use threads"
+#endif],,
+[AC_MSG_RESULT(no)
+ CPPFLAGS_BOOST_THREADCONF="-DBOOST_DISABLE_THREADS=1"],
+[AC_MSG_RESULT(yes)])
+
+CPPFLAGS="$CPPFLAGS_SAVES $CPPFLAGS_BOOST_THREADCONF"
+AC_SUBST(BOOST_INCLUDES)
 
 # I can't get some of the #include <asio.hpp> right without this
 # TODO: find the real cause of asio/boost wanting pthreads
@@ -846,28 +925,9 @@ CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/ext/coroutine"
 #
 # Disable threads: Currently we don't use them.
 CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
-#
-# kqueue portability: ASIO uses kqueue by default if it's available (it's
-# generally available in BSD variants).  Unfortunately, some public
-# implementation of kqueue forces a conversion from a pointer to an integer,
-# which is prohibited in C++ unless reinterpret_cast, C++'s most evil beast
-# (and ASIO doesn't use it anyway) is used.  This will cause build error for
-# some of our C++ files including ASIO header files.  The following check
-# detects such cases and tells ASIO not to use kqueue if so.
-AC_CHECK_FUNC(kqueue, ac_cv_have_kqueue=yes, ac_cv_have_kqueue=no)
-if test "X$ac_cv_have_kqueue" = "Xyes"; then
-	AC_MSG_CHECKING([whether kqueue EV_SET compiles in C++])
-	AC_TRY_COMPILE([
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/event.h>],
-[char* udata;
-EV_SET(NULL, 0, 0, 0, 0, 0, udata);],
-	[AC_MSG_RESULT(yes)],
-	[AC_MSG_RESULT([no, disable kqueue for ASIO])
-	 CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_KQUEUE=1"
-	])
-fi
+
+# 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().
@@ -917,6 +977,11 @@ AC_ARG_ENABLE(install-configurations,
 
 AM_CONDITIONAL(INSTALL_CONFIGURATIONS, test x$install_configurations = xyes || test x$install_configurations = xtrue)
 
+AC_ARG_ENABLE(logger-checks, [AC_HELP_STRING([--enable-logger-checks],
+  [check logger messages [default=no]])], enable_logger_checks=$enableval, enable_logger_checks=no)
+AM_CONDITIONAL(ENABLE_LOGGER_CHECKS, test x$enable_logger_checks != xno)
+AM_COND_IF([ENABLE_LOGGER_CHECKS], [AC_DEFINE([ENABLE_LOGGER_CHECKS], [1], [Check logger messages?])])
+
 AC_CONFIG_FILES([Makefile
                  doc/Makefile
                  doc/guide/Makefile
@@ -933,6 +998,9 @@ AC_CONFIG_FILES([Makefile
                  src/bin/cfgmgr/plugins/Makefile
                  src/bin/cfgmgr/plugins/tests/Makefile
                  src/bin/cfgmgr/tests/Makefile
+                 src/bin/dbutil/Makefile
+                 src/bin/dbutil/tests/Makefile
+                 src/bin/dbutil/tests/testdata/Makefile
                  src/bin/host/Makefile
                  src/bin/loadzone/Makefile
                  src/bin/loadzone/tests/correct/Makefile
@@ -946,8 +1014,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/ddns/tests/Makefile
                  src/bin/dhcp6/Makefile
                  src/bin/dhcp6/tests/Makefile
-		 src/bin/dhcp4/Makefile
-		 src/bin/dhcp4/tests/Makefile
+                 src/bin/dhcp4/Makefile
+                 src/bin/dhcp4/tests/Makefile
                  src/bin/resolver/Makefile
                  src/bin/resolver/tests/Makefile
                  src/bin/sockcreator/Makefile
@@ -1001,6 +1069,8 @@ AC_CONFIG_FILES([Makefile
                  src/lib/python/isc/bind10/tests/Makefile
                  src/lib/python/isc/xfrin/Makefile
                  src/lib/python/isc/xfrin/tests/Makefile
+                 src/lib/python/isc/server_common/Makefile
+                 src/lib/python/isc/server_common/tests/Makefile
                  src/lib/config/Makefile
                  src/lib/config/tests/Makefile
                  src/lib/config/tests/testdata/Makefile
@@ -1050,15 +1120,18 @@ AC_CONFIG_FILES([Makefile
                  tests/tools/badpacket/Makefile
                  tests/tools/badpacket/tests/Makefile
                  tests/tools/perfdhcp/Makefile
+                 dns++.pc
                ])
 AC_OUTPUT([doc/version.ent
-           compatcheck/sqlite3-difftbl-check.py
            src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/tests/b10-cfgmgr_test.py
            src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
            src/bin/cmdctl/tests/cmdctl_test
            src/bin/cmdctl/cmdctl.spec.pre
+           src/bin/dbutil/dbutil.py
+           src/bin/dbutil/run_dbutil.sh
+           src/bin/dbutil/tests/dbutil_test.sh
            src/bin/ddns/ddns.py
            src/bin/xfrin/tests/xfrin_test
            src/bin/xfrin/xfrin.py
@@ -1135,13 +1208,14 @@ AC_OUTPUT([doc/version.ent
            tests/system/ixfr/in-3/setup.sh
            tests/system/ixfr/in-4/setup.sh
           ], [
-           chmod +x compatcheck/sqlite3-difftbl-check.py
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/xfrin/run_b10-xfrin.sh
            chmod +x src/bin/xfrout/run_b10-xfrout.sh
            chmod +x src/bin/zonemgr/run_b10-zonemgr.sh
            chmod +x src/bin/bind10/run_bind10.sh
            chmod +x src/bin/cmdctl/tests/cmdctl_test
+           chmod +x src/bin/dbutil/run_dbutil.sh
+           chmod +x src/bin/dbutil/tests/dbutil_test.sh
            chmod +x src/bin/xfrin/tests/xfrin_test
            chmod +x src/bin/xfrout/tests/xfrout_test
            chmod +x src/bin/zonemgr/tests/zonemgr_test
diff --git a/depcomp b/depcomp
deleted file mode 100755
index df8eea7..0000000
--- a/depcomp
+++ /dev/null
@@ -1,630 +0,0 @@
-#! /bin/sh
-# depcomp - compile a program generating dependencies as side-effects
-
-scriptversion=2009-04-28.21; # UTC
-
-# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
-# Software Foundation, Inc.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-# Originally written by Alexandre Oliva <oliva at dcc.unicamp.br>.
-
-case $1 in
-  '')
-     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
-     exit 1;
-     ;;
-  -h | --h*)
-    cat <<\EOF
-Usage: depcomp [--help] [--version] PROGRAM [ARGS]
-
-Run PROGRAMS ARGS to compile a file, generating dependencies
-as side-effects.
-
-Environment variables:
-  depmode     Dependency tracking mode.
-  source      Source file read by `PROGRAMS ARGS'.
-  object      Object file output by `PROGRAMS ARGS'.
-  DEPDIR      directory where to store dependencies.
-  depfile     Dependency file to output.
-  tmpdepfile  Temporary file to use when outputing dependencies.
-  libtool     Whether libtool is used (yes/no).
-
-Report bugs to <bug-automake at gnu.org>.
-EOF
-    exit $?
-    ;;
-  -v | --v*)
-    echo "depcomp $scriptversion"
-    exit $?
-    ;;
-esac
-
-if test -z "$depmode" || test -z "$source" || test -z "$object"; then
-  echo "depcomp: Variables source, object and depmode must be set" 1>&2
-  exit 1
-fi
-
-# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
-depfile=${depfile-`echo "$object" |
-  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
-tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
-
-rm -f "$tmpdepfile"
-
-# Some modes work just like other modes, but use different flags.  We
-# parameterize here, but still list the modes in the big case below,
-# to make depend.m4 easier to write.  Note that we *cannot* use a case
-# here, because this file can only contain one case statement.
-if test "$depmode" = hp; then
-  # HP compiler uses -M and no extra arg.
-  gccflag=-M
-  depmode=gcc
-fi
-
-if test "$depmode" = dashXmstdout; then
-   # This is just like dashmstdout with a different argument.
-   dashmflag=-xM
-   depmode=dashmstdout
-fi
-
-cygpath_u="cygpath -u -f -"
-if test "$depmode" = msvcmsys; then
-   # This is just like msvisualcpp but w/o cygpath translation.
-   # Just convert the backslash-escaped backslashes to single forward
-   # slashes to satisfy depend.m4
-   cygpath_u="sed s,\\\\\\\\,/,g"
-   depmode=msvisualcpp
-fi
-
-case "$depmode" in
-gcc3)
-## gcc 3 implements dependency tracking that does exactly what
-## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
-## it if -MD -MP comes after the -MF stuff.  Hmm.
-## Unfortunately, FreeBSD c89 acceptance of flags depends upon
-## the command line argument order; so add the flags where they
-## appear in depend2.am.  Note that the slowdown incurred here
-## affects only configure: in makefiles, %FASTDEP% shortcuts this.
-  for arg
-  do
-    case $arg in
-    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
-    *)  set fnord "$@" "$arg" ;;
-    esac
-    shift # fnord
-    shift # $arg
-  done
-  "$@"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  mv "$tmpdepfile" "$depfile"
-  ;;
-
-gcc)
-## There are various ways to get dependency output from gcc.  Here's
-## why we pick this rather obscure method:
-## - Don't want to use -MD because we'd like the dependencies to end
-##   up in a subdir.  Having to rename by hand is ugly.
-##   (We might end up doing this anyway to support other compilers.)
-## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
-##   -MM, not -M (despite what the docs say).
-## - Using -M directly means running the compiler twice (even worse
-##   than renaming).
-  if test -z "$gccflag"; then
-    gccflag=-MD,
-  fi
-  "$@" -Wp,"$gccflag$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  rm -f "$depfile"
-  echo "$object : \\" > "$depfile"
-  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
-## The second -e expression handles DOS-style file names with drive letters.
-  sed -e 's/^[^:]*: / /' \
-      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
-## This next piece of magic avoids the `deleted header file' problem.
-## The problem is that when a header file which appears in a .P file
-## is deleted, the dependency causes make to die (because there is
-## typically no way to rebuild the header).  We avoid this by adding
-## dummy dependencies for each header file.  Too bad gcc doesn't do
-## this for us directly.
-  tr ' ' '
-' < "$tmpdepfile" |
-## Some versions of gcc put a space before the `:'.  On the theory
-## that the space means something, we add a space to the output as
-## well.
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-hp)
-  # This case exists only to let depend.m4 do its work.  It works by
-  # looking at the text of this script.  This case will never be run,
-  # since it is checked for above.
-  exit 1
-  ;;
-
-sgi)
-  if test "$libtool" = yes; then
-    "$@" "-Wp,-MDupdate,$tmpdepfile"
-  else
-    "$@" -MDupdate "$tmpdepfile"
-  fi
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  rm -f "$depfile"
-
-  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
-    echo "$object : \\" > "$depfile"
-
-    # Clip off the initial element (the dependent).  Don't try to be
-    # clever and replace this with sed code, as IRIX sed won't handle
-    # lines with more than a fixed number of characters (4096 in
-    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
-    # the IRIX cc adds comments like `#:fec' to the end of the
-    # dependency line.
-    tr ' ' '
-' < "$tmpdepfile" \
-    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
-    tr '
-' ' ' >> "$depfile"
-    echo >> "$depfile"
-
-    # The second pass generates a dummy entry for each header file.
-    tr ' ' '
-' < "$tmpdepfile" \
-   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
-   >> "$depfile"
-  else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
-  fi
-  rm -f "$tmpdepfile"
-  ;;
-
-aix)
-  # The C for AIX Compiler uses -M and outputs the dependencies
-  # in a .u file.  In older versions, this file always lives in the
-  # current directory.  Also, the AIX compiler puts `$object:' at the
-  # start of each line; $object doesn't have directory information.
-  # Version 6 uses the directory in both cases.
-  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-  test "x$dir" = "x$object" && dir=
-  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-  if test "$libtool" = yes; then
-    tmpdepfile1=$dir$base.u
-    tmpdepfile2=$base.u
-    tmpdepfile3=$dir.libs/$base.u
-    "$@" -Wc,-M
-  else
-    tmpdepfile1=$dir$base.u
-    tmpdepfile2=$dir$base.u
-    tmpdepfile3=$dir$base.u
-    "$@" -M
-  fi
-  stat=$?
-
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
-    exit $stat
-  fi
-
-  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
-  do
-    test -f "$tmpdepfile" && break
-  done
-  if test -f "$tmpdepfile"; then
-    # Each line is of the form `foo.o: dependent.h'.
-    # Do two passes, one to just change these to
-    # `$object: dependent.h' and one to simply `dependent.h:'.
-    sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-    # That's a tab and a space in the [].
-    sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-  else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
-  fi
-  rm -f "$tmpdepfile"
-  ;;
-
-icc)
-  # Intel's C compiler understands `-MD -MF file'.  However on
-  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
-  # ICC 7.0 will fill foo.d with something like
-  #    foo.o: sub/foo.c
-  #    foo.o: sub/foo.h
-  # which is wrong.  We want:
-  #    sub/foo.o: sub/foo.c
-  #    sub/foo.o: sub/foo.h
-  #    sub/foo.c:
-  #    sub/foo.h:
-  # ICC 7.1 will output
-  #    foo.o: sub/foo.c sub/foo.h
-  # and will wrap long lines using \ :
-  #    foo.o: sub/foo.c ... \
-  #     sub/foo.h ... \
-  #     ...
-
-  "$@" -MD -MF "$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  rm -f "$depfile"
-  # Each line is of the form `foo.o: dependent.h',
-  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
-  # Do two passes, one to just change these to
-  # `$object: dependent.h' and one to simply `dependent.h:'.
-  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
-  # Some versions of the HPUX 10.20 sed can't process this invocation
-  # correctly.  Breaking it into two sed invocations is a workaround.
-  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
-    sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-hp2)
-  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
-  # compilers, which have integrated preprocessors.  The correct option
-  # to use with these is +Maked; it writes dependencies to a file named
-  # 'foo.d', which lands next to the object file, wherever that
-  # happens to be.
-  # Much of this is similar to the tru64 case; see comments there.
-  dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-  test "x$dir" = "x$object" && dir=
-  base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-  if test "$libtool" = yes; then
-    tmpdepfile1=$dir$base.d
-    tmpdepfile2=$dir.libs/$base.d
-    "$@" -Wc,+Maked
-  else
-    tmpdepfile1=$dir$base.d
-    tmpdepfile2=$dir$base.d
-    "$@" +Maked
-  fi
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-     rm -f "$tmpdepfile1" "$tmpdepfile2"
-     exit $stat
-  fi
-
-  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
-  do
-    test -f "$tmpdepfile" && break
-  done
-  if test -f "$tmpdepfile"; then
-    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
-    # Add `dependent.h:' lines.
-    sed -ne '2,${
-	       s/^ *//
-	       s/ \\*$//
-	       s/$/:/
-	       p
-	     }' "$tmpdepfile" >> "$depfile"
-  else
-    echo "#dummy" > "$depfile"
-  fi
-  rm -f "$tmpdepfile" "$tmpdepfile2"
-  ;;
-
-tru64)
-   # The Tru64 compiler uses -MD to generate dependencies as a side
-   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
-   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
-   # dependencies in `foo.d' instead, so we check for that too.
-   # Subdirectories are respected.
-   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-   test "x$dir" = "x$object" && dir=
-   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-
-   if test "$libtool" = yes; then
-      # With Tru64 cc, shared objects can also be used to make a
-      # static library.  This mechanism is used in libtool 1.4 series to
-      # handle both shared and static libraries in a single compilation.
-      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
-      #
-      # With libtool 1.5 this exception was removed, and libtool now
-      # generates 2 separate objects for the 2 libraries.  These two
-      # compilations output dependencies in $dir.libs/$base.o.d and
-      # in $dir$base.o.d.  We have to check for both files, because
-      # one of the two compilations can be disabled.  We should prefer
-      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
-      # automatically cleaned when .libs/ is deleted, while ignoring
-      # the former would cause a distcleancheck panic.
-      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
-      tmpdepfile2=$dir$base.o.d          # libtool 1.5
-      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
-      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
-      "$@" -Wc,-MD
-   else
-      tmpdepfile1=$dir$base.o.d
-      tmpdepfile2=$dir$base.d
-      tmpdepfile3=$dir$base.d
-      tmpdepfile4=$dir$base.d
-      "$@" -MD
-   fi
-
-   stat=$?
-   if test $stat -eq 0; then :
-   else
-      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-      exit $stat
-   fi
-
-   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-   do
-     test -f "$tmpdepfile" && break
-   done
-   if test -f "$tmpdepfile"; then
-      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-      # That's a tab and a space in the [].
-      sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-   else
-      echo "#dummy" > "$depfile"
-   fi
-   rm -f "$tmpdepfile"
-   ;;
-
-#nosideeffect)
-  # This comment above is used by automake to tell side-effect
-  # dependency tracking mechanisms from slower ones.
-
-dashmstdout)
-  # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout, regardless of -o.
-  "$@" || exit $?
-
-  # Remove the call to Libtool.
-  if test "$libtool" = yes; then
-    while test "X$1" != 'X--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-
-  # Remove `-o $object'.
-  IFS=" "
-  for arg
-  do
-    case $arg in
-    -o)
-      shift
-      ;;
-    $object)
-      shift
-      ;;
-    *)
-      set fnord "$@" "$arg"
-      shift # fnord
-      shift # $arg
-      ;;
-    esac
-  done
-
-  test -z "$dashmflag" && dashmflag=-M
-  # Require at least two characters before searching for `:'
-  # in the target name.  This is to cope with DOS-style filenames:
-  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
-  "$@" $dashmflag |
-    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
-  rm -f "$depfile"
-  cat < "$tmpdepfile" > "$depfile"
-  tr ' ' '
-' < "$tmpdepfile" | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-dashXmstdout)
-  # This case only exists to satisfy depend.m4.  It is never actually
-  # run, as this mode is specially recognized in the preamble.
-  exit 1
-  ;;
-
-makedepend)
-  "$@" || exit $?
-  # Remove any Libtool call
-  if test "$libtool" = yes; then
-    while test "X$1" != 'X--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-  # X makedepend
-  shift
-  cleared=no eat=no
-  for arg
-  do
-    case $cleared in
-    no)
-      set ""; shift
-      cleared=yes ;;
-    esac
-    if test $eat = yes; then
-      eat=no
-      continue
-    fi
-    case "$arg" in
-    -D*|-I*)
-      set fnord "$@" "$arg"; shift ;;
-    # Strip any option that makedepend may not understand.  Remove
-    # the object too, otherwise makedepend will parse it as a source file.
-    -arch)
-      eat=yes ;;
-    -*|$object)
-      ;;
-    *)
-      set fnord "$@" "$arg"; shift ;;
-    esac
-  done
-  obj_suffix=`echo "$object" | sed 's/^.*\././'`
-  touch "$tmpdepfile"
-  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
-  rm -f "$depfile"
-  cat < "$tmpdepfile" > "$depfile"
-  sed '1,2d' "$tmpdepfile" | tr ' ' '
-' | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile" "$tmpdepfile".bak
-  ;;
-
-cpp)
-  # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout.
-  "$@" || exit $?
-
-  # Remove the call to Libtool.
-  if test "$libtool" = yes; then
-    while test "X$1" != 'X--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-
-  # Remove `-o $object'.
-  IFS=" "
-  for arg
-  do
-    case $arg in
-    -o)
-      shift
-      ;;
-    $object)
-      shift
-      ;;
-    *)
-      set fnord "$@" "$arg"
-      shift # fnord
-      shift # $arg
-      ;;
-    esac
-  done
-
-  "$@" -E |
-    sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-       -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
-    sed '$ s: \\$::' > "$tmpdepfile"
-  rm -f "$depfile"
-  echo "$object : \\" > "$depfile"
-  cat < "$tmpdepfile" >> "$depfile"
-  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-msvisualcpp)
-  # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout.
-  "$@" || exit $?
-
-  # Remove the call to Libtool.
-  if test "$libtool" = yes; then
-    while test "X$1" != 'X--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-
-  IFS=" "
-  for arg
-  do
-    case "$arg" in
-    -o)
-      shift
-      ;;
-    $object)
-      shift
-      ;;
-    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
-	set fnord "$@"
-	shift
-	shift
-	;;
-    *)
-	set fnord "$@" "$arg"
-	shift
-	shift
-	;;
-    esac
-  done
-  "$@" -E 2>/dev/null |
-  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
-  rm -f "$depfile"
-  echo "$object : \\" > "$depfile"
-  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::	\1 \\:p' >> "$depfile"
-  echo "	" >> "$depfile"
-  sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-msvcmsys)
-  # This case exists only to let depend.m4 do its work.  It works by
-  # looking at the text of this script.  This case will never be run,
-  # since it is checked for above.
-  exit 1
-  ;;
-
-none)
-  exec "$@"
-  ;;
-
-*)
-  echo "Unknown depmode $depmode" 1>&2
-  exit 1
-  ;;
-esac
-
-exit 0
-
-# Local Variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/dns++.pc.in b/dns++.pc.in
new file mode 100644
index 0000000..d2a7e06
--- /dev/null
+++ b/dns++.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: dns++
+Description: BIND 10 DNS library
+Version: @PACKAGE_VERSION@
+Requires: botan-1.8
+Cflags: -I${includedir}/@PACKAGE_NAME@
+Libs: -L${libdir} -ldns++ -lcryptolink -lutil -lexceptions -lm
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..fd8742e
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+/version.ent
+html
diff --git a/doc/Doxyfile b/doc/Doxyfile
index c9c5c5a..8730ae4 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -25,13 +25,17 @@ DOXYFILE_ENCODING      = UTF-8
 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded
 # by quotes) that should identify the project.
 
-PROJECT_NAME           = BIND
+PROJECT_NAME           = BIND10
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number.
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 10.0.0
+# Currently this variable is overwritten (see devel target in Makefile.am)
+# If the number of parameters to overwrite increases, we should generate
+# Doxyfile (rename it to Doxyfile.in and generate during configure phase)
+
+PROJECT_NUMBER         =
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
@@ -47,7 +51,7 @@ OUTPUT_DIRECTORY       = html
 # source files, where putting all generated files in the same directory would
 # otherwise cause performance problems for the file system.
 
-CREATE_SUBDIRS         = NO
+CREATE_SUBDIRS         = YES
 
 # The OUTPUT_LANGUAGE tag is used to specify the language in which all
 # documentation generated by doxygen is written. Doxygen will use this
@@ -281,7 +285,7 @@ TYPEDEF_HIDES_STRUCT   = NO
 # causing a significant performance penality.
 # If the system has enough physical memory increasing the cache will improve the
 # performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will rougly double the
+# a logarithmic scale so increasing the size by one will roughly double the
 # memory usage. The cache size is given by this formula:
 # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
 # corresponding to a cache size of 2^16 = 65536 symbols
@@ -574,7 +578,8 @@ 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/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp \
+    ../src/bin/dhcp4 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
@@ -591,7 +596,7 @@ INPUT_ENCODING         = UTF-8
 # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
 # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
 
-FILE_PATTERNS          =
+FILE_PATTERNS          = *.c *.cc *.h *.hpp *.dox
 
 # The RECURSIVE tag can be used to turn specify whether or not subdirectories
 # should be searched for input files as well. Possible values are YES and NO.
@@ -651,7 +656,7 @@ EXAMPLE_RECURSIVE      = NO
 # directories that contain image that are included in the documentation (see
 # the \image command).
 
-IMAGE_PATH             =
+IMAGE_PATH             = ../doc/images
 
 # The INPUT_FILTER tag can be used to specify a program that doxygen should
 # invoke to filter for each input file. Doxygen will invoke the filter program
@@ -773,7 +778,7 @@ GENERATE_HTML          = YES
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
 # put in front of it. If left blank `html' will be used as the default path.
 
-HTML_OUTPUT            = cpp
+HTML_OUTPUT            = ../html
 
 # The HTML_FILE_EXTENSION tag can be used to specify the file extension for
 # each generated HTML page (for example: .htm,.php,.asp). If it is left blank
@@ -954,7 +959,7 @@ ENUM_VALUES_PER_LINE   = 4
 # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
 # Windows users are probably better off using the HTML help feature.
 
-GENERATE_TREEVIEW      = NO
+GENERATE_TREEVIEW      = YES
 
 # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
 # and Class Hierarchy pages using a tree view instead of an ordered list.
@@ -965,7 +970,7 @@ USE_INLINE_TREES       = NO
 # used to set the initial width (in pixels) of the frame in which the tree
 # is shown.
 
-TREEVIEW_WIDTH         = 250
+TREEVIEW_WIDTH         = 180
 
 # Use this tag to change the font size of Latex formulas included
 # as images in the HTML documentation. The default is 10. Note that
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 31d0cfa..7642220 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,3 +1,16 @@
 SUBDIRS = guide
 
 EXTRA_DIST = version.ent.in
+
+devel:
+	mkdir -p html
+	(cat Doxyfile; echo PROJECT_NUMBER=$(PACKAGE_VERSION)) | doxygen - > html/doxygen.log 2> html/doxygen-error.log
+	echo `grep -i ": warning:" html/doxygen-error.log | wc -l` warnings/errors detected.
+
+clean:
+	rm -rf html
+
+# That's a bit of a hack, but we are making sure that devel target
+# is always valid. The alternative is to make devel depend on all
+# *.cc *.h files in the whole tree.
+.PHONY: devel
diff --git a/doc/devel/01-dns.dox b/doc/devel/01-dns.dox
new file mode 100644
index 0000000..e72dbbd
--- /dev/null
+++ b/doc/devel/01-dns.dox
@@ -0,0 +1,14 @@
+/**
+ *
+ * @page dns BIND10 DNS
+ *
+ * @section dns-auth b10-auth
+ *
+ * @todo: Describe b10-auth here.
+ *
+ * @section b10-cfgmgr b10-cfgmgr Overview
+ *
+ * @todo: Descibe b10-cfgmgr here.
+ *
+ *
+ */
diff --git a/doc/devel/02-dhcp.dox b/doc/devel/02-dhcp.dox
new file mode 100644
index 0000000..7ebd9b2
--- /dev/null
+++ b/doc/devel/02-dhcp.dox
@@ -0,0 +1,117 @@
+/**
+ * @page dhcpv4 DHCPv4 Server Component
+ *
+ * BIND10 offers DHCPv4 server implementation. It is implemented as
+ * b10-dhcp4 component.  Its primary code is located in
+ * isc::dhcp::Dhcpv4Srv class. It uses \ref libdhcp extensively,
+ * especially isc::dhcp::Pkt4, isc::dhcp::Option and
+ * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+ * functionality, i.e. it is able to receive and process incoming
+ * requests and trasmit responses. However, it does not have database
+ * management, so it returns only one, hardcoded lease to whoever asks
+ * for it.
+ *
+ * DHCPv4 server component does not support direct traffic (relayed
+ * only), as support for transmission to hosts without IPv4 address
+ * assigned is not implemented in IfaceMgr yet.
+ *
+ * DHCPv4 server component does not listen to BIND10 message queue.
+ *
+ * DHCPv4 server component does not use BIND10 logging yet.
+ *
+ * DHCPv4 server component is not integrated with boss yet.
+ *
+ * @page dhcpv6 DHCPv6 Server Component
+ *
+ * BIND10 offers DHCPv6 server implementation. It is implemented as
+ * b10-dhcp6 component. Its primary code is located in
+ * isc::dhcp::Dhcpv6Srv class. It uses \ref libdhcp extensively,
+ * especially lib::dhcp::Pkt6, isc::dhcp::Option and
+ * isc::dhcp::IfaceMgr classes. Currently this code offers skeleton
+ * functionality, i.e. it is able to receive and process incoming
+ * requests and trasmit responses. However, it does not have database
+ * management, so it returns only one, hardcoded lease to whoever asks
+ * for it.
+ *
+ * DHCPv6 server component does not support relayed traffic yet, as
+ * support for relay decapsulation is not implemented yet.
+ *
+ * DHCPv6 server component does not listen to BIND10 message queue.
+ *
+ * DHCPv6 server component does not use BIND10 logging yet.
+ *
+ * DHCPv6 server component is not integrated with boss yet.
+ *
+ * @page libdhcp libdhcp++ library
+ *
+ * @section libdhcpIntro Libdhcp++ Introduction
+ *
+ * libdhcp++ is an all-purpose DHCP-manipulation library, written in
+ * C++. It offers packet parsing and assembly, DHCPv4 and DHCPv6
+ * options parsing and ssembly, interface detection (currently on
+ * Linux systems only) and socket operations. Following classes are
+ * implemented:
+ *
+ * - isc::dhcp::Pkt4 - represents DHCPv4 packet.
+ * - isc::dhcp::Pkt6 - represents DHCPv6 packet.
+ *
+ * There are two pointer types defined: Pkt4Ptr and Pkt6Ptr. They are
+ * smart pointer and are using boost::shared_ptr. There are not const
+ * versions defined, as we assume that hooks can modify any aspect of
+ * the packet at almost any stage of processing.
+ *
+ * Both packets use collection of Option objects to represent DHCPv4
+ * and DHCPv6 options. The base class -- Option -- can be used to
+ * represent generic option that contains collection of
+ * bytes. Depending on if the option is instantiated as v4 or v6
+ * option, it will adjust its header (DHCPv4 options use 1 octet for
+ * type and 1 octet for length, while DHCPv6 options use 2 bytes for
+ * each).
+ *
+ * There are many specialized classes that are intended to handle options with
+ * specific content:
+ * - isc::dhcp::Option4AddrLst -- DHCPv4 option, contains one or more IPv4 addresses;
+ * - isc::dhcp::Option6AddrLst -- DHCPv6 option, contains one or more IPv6 addresses;
+ * - isc::dhcp::Option6IAAddr -- DHCPv6 option, represents IAADDR_OPTION (an option that
+ *                     contains IPv6 address with extra parameters);
+ * - isc::dhcp::Option6IA -- DHCPv6 option used to store IA_NA and its suboptions.
+ *
+ * All options can store sub-options (i.e. options that are stored within option
+ * rather than in a message directly). This functionality is commonly used in
+ * DHCPv6, but is rarely used in DHCPv4. isc::dhcp::Option::addOption(),
+ * isc::dhcp::Option::delOption(), isc::dhcp::Option::getOption() can be used
+ * for that purpose.
+ *
+ * @section lidhcpIfaceMgr Interface Manager
+ *
+ * Interface Manager (or IfaceMgr) is an abstraction layer about low-level
+ * network operations. In particlar, it provides information about existing
+ * network interfaces See isc::dhcp::IfaceMgr::Iface class and
+ * isc::dhcp::IfaceMgr::detectIfaces() and isc::dhcp::IfaceMgr::getIface().
+ *
+ * Currently there is interface detection is implemented in Linux only. There
+ * are plans to implement such support for other OSes, but they remain low
+ * priority for now.
+ *
+ * Generic parts of the code are isc::dhcp::IfaceMgr class in
+ * src/lib/dhcp/iface_mgr.cc file. OS-specific code is located in separate
+ * files, e.g. iface_mgr_linux.cc. Such separation should be maintained when
+ * additional code will be developed.
+ *
+ * For systems that interface detection is not supported on, there is a stub
+ * mechanism implemented. It assumes that interface name is read from a text
+ * file. This is a temporary solution and will be removed as soon as proper
+ * interface detection is implemented. It is not going to be developed further.
+ * To use this feature, store interfaces.txt file. It uses a simple syntax.
+ * Each line represents an interface name, followed by IPv4 or IPv6 address
+ * that follows it. This is usually link-local IPv6 address that the server
+ * should bind to. In theory this mechanism also supports IPv4, but it was
+ * never tested. The code currently supports only a single interface defined
+ * that way.
+ *
+ * Another useful methods are dedicated to transmission
+ * (isc::dhcp::IfaceMgr::send(), 2 overloads) and reception
+ * (isc::dhcp::IfaceMgr::receive4() and isc::dhcp::IfaceMgr::receive6()).
+ * Note that receive4() and receive6() methods may return NULL, e.g.
+ * when timeout is reached or if dhcp daemon receives a signal.
+ */
\ No newline at end of file
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
new file mode 100644
index 0000000..a472add
--- /dev/null
+++ b/doc/devel/mainpage.dox
@@ -0,0 +1,36 @@
+/**
+ *
+ * @mainpage BIND10 Developer's Guide
+ *
+ * Welcome to BIND10 Developer's Guide. This documentation is addressed
+ * at existing and prospecting developers and programmers, who would like
+ * to gain insight into internal workings of BIND 10. It could also be useful
+ * for existing and prospective contributors.
+ *
+ * If you are a user or system administrator, rather than software engineer,
+ * you should read <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
+ * Guide (Administrator Reference for BIND10)</a> instead.
+ *
+ * Regardless of your field of expertise, you are encouraged to visit
+ * <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
+ *
+ * @section DNS
+ * - @subpage DataScrubbing
+ *
+ * @section DHCP
+ * - @subpage dhcpv4
+ * - @subpage dhcpv6
+ * - @subpage libdhcp
+ *
+ * @section misc Miscellaneous topics
+ * - @subpage LoggingApi
+ *   - @subpage LoggingApiOverview
+ *   - @subpage LoggingApiLoggerNames
+ *   - @subpage LoggingApiLoggingMessages
+ * - @subpage SocketSessionUtility
+ * - <a href="./doxygen-error.log">Documentation warnings and errors</a>
+ *
+ * @todo: Move this logo to the right (and possibly up). Not sure what
+ * is the best way to do it in Doxygen, without using CSS hacks.
+ * @image html isc-logo.png
+ */
\ No newline at end of file
diff --git a/doc/guide/bind10-guide.html b/doc/guide/bind10-guide.html
index 4ae31f5..b879af5 100644
--- a/doc/guide/bind10-guide.html
+++ b/doc/guide/bind10-guide.html
@@ -1,18 +1,18 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" type="text/css" href="./bind10-guide.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1"><meta name="description" content="BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers managed by Internet Systems Consortium (ISC). It includes DNS libraries, modular components for controlling authoritative and recursive DNS servers, and experimental DHCPv4 and DHCPv6 servers. This is the reference guide for BIND 10 version 20120127. The most up-to-date version of this document (in PDF, HTML, and plain text formats), along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="idm148
 92896"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the reference guide for BIND 10 version
-        20120127.</p></div><div><p class="copyright">Copyright © 2010-2012 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a framework that features Domain Name System
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Guide</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a framework that features Domain Name System (DNS) suite and Dynamic Host Configuration Protocol (DHCP) servers managed by Internet Systems Consortium (ISC). It includes DNS libraries, modular components for controlling authoritative and recursive DNS servers, and experimental DHCPv4 and DHCPv6 servers. This is the reference guide for BIND 10 version 20120405. The most up-to-date version of this document (in PDF, HTML, and plain text formats), along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Guide"><div class="titlepage"><div><div><h1 class="title"><a name="idm146
 04752"></a>BIND 10 Guide</h1></div><div><h2 class="subtitle">Administrator Reference for BIND 10</h2></div><div><p class="releaseinfo">This is the reference guide for BIND 10 version
+        20120405.</p></div><div><p class="copyright">Copyright © 2010-2012 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a framework that features Domain Name System
       (DNS) suite and Dynamic Host Configuration Protocol (DHCP)
       servers managed by Internet Systems Consortium (ISC). It
       includes DNS libraries, modular components for controlling
       authoritative and recursive DNS servers, and experimental DHCPv4
       and DHCPv6 servers.
       </p><p>
-        This is the reference guide for BIND 10 version 20120127.
+        This is the reference guide for BIND 10 version 20120405.
         The most up-to-date version of this document (in PDF, HTML,
         and plain text formats), along with other documents for
         BIND 10, can be found at <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
-        </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="preface"><a href="#idp61424">Preface</a></span></dt><dd><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></dd><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#idp64344">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick 
 start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#idp113000">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#idp114472">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#idp119504">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#idp126792">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#idp127848">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#idp129504">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">3.1. Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">3.2. Configuration of started processes</a></span></dt></dl></dd><
 dt><span class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">6.1. Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#idp200360">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#idp205096">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#idp207592">8.3. Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dd><dl><dt><span class="section"><a href="#idp217864">
 9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#idp220904">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#idp11976">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#resolverserver">11. Recursive Name Server</a></span></dt><dd><dl><dt><span class="section"><a href="#idp259896">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#idp269088">11.2. Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp4">12. DHCPv4 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp4-usage">12.1. DHCPv4 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp4-config">12.2. DHCPv4 Server Configuration</a></span></dt><dt><span c
 lass="section"><a href="#dhcp4-std">12.3. Supported standards</a></span></dt><dt><span class="section"><a href="#dhcp4-limit">12.4. DHCPv4 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp6">13. DHCPv6 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp6-usage">13.1. DHCPv6 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp6-config">13.2. DHCPv6 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp6-std">13.3. Supported DHCPv6 Standards</a></span></dt><dt><span class="section"><a href="#dhcp6-limit">13.4. DHCPv6 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#libdhcp">14. libdhcp++ library</a></span></dt><dd><dl><dt><span class="section"><a href="#iface-detect">14.1. Interface detection</a></span></dt><dt><span class="section"><a href="#packet-handling">14.2. DHCPv4/DHCPv6 packet handling</a></span></dt></dl></dd><dt><span class="chapter"><a href="#s
 tatistics">15. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">16. Logging</a></span></dt><dd><dl><dt><span class="section"><a href="#idp327280">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#idp328272">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#idp349480">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#idp362088">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#idp379592">16.2. Logging Message Format</a></span></dt></dl></dd></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>3.1. <a href="#idp150584"></a></dt></dl></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="idp61424"></a>Preface</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></
 div><div class="section" title="1. Acknowledgements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="acknowledgements"></a>1. Acknowledgements</h2></div></div></div><p>ISC would like to acknowledge generous support for
+        </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="preface"><a href="#idp17128">Preface</a></span></dt><dd><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></dd><dt><span class="chapter"><a href="#intro">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#idp20200">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></dd><dt><span class="chapter"><a href="#installation">2. Installation</a></span></dt><dd><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick 
 start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#idp70704">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#idp72200">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#idp77232">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#idp84464">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#idp85536">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#idp87192">2.3.6. Install Hierarchy</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#bind10">3. Starting BIND10 with <span class="command"><strong>bind10</strong></span></a></span></dt><dd><dl><dt><span class="section"><a href="#start">3.1. Starting BIND 10</a></span></dt><dt><span class="section"><a href="#bind10.config">3.2. Configuration of started processes</a></span></dt></dl></dd><dt><sp
 an class="chapter"><a href="#msgq">4. Command channel</a></span></dt><dt><span class="chapter"><a href="#cfgmgr">5. Configuration manager</a></span></dt><dt><span class="chapter"><a href="#cmdctl">6. Remote control daemon</a></span></dt><dd><dl><dt><span class="section"><a href="#cmdctl.spec">6.1. Configuration specification for b10-cmdctl</a></span></dt></dl></dd><dt><span class="chapter"><a href="#bindctl">7. Control and configure user interface</a></span></dt><dt><span class="chapter"><a href="#authserver">8. Authoritative Server</a></span></dt><dd><dl><dt><span class="section"><a href="#idp156456">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#idp174000">8.2. Data Source Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#in-memory-datasource">8.2.1. In-memory Data Source</a></span></dt></dl></dd><dt><span class="section"><a href="#idp185968">8.3. Loading Master Zones Files</a></span></dt></dl></dd><dt><span class="chapter">
 <a href="#xfrin">9. Incoming Zone Transfers</a></span></dt><dd><dl><dt><span class="section"><a href="#idp196456">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#idp199496">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#idp209248">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#xfrout">10. Outbound Zone Transfers</a></span></dt><dt><span class="chapter"><a href="#resolverserver">11. Recursive Name Server</a></span></dt><dd><dl><dt><span class="section"><a href="#idp228136">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#idp237328">11.2. Forwarding</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp4">12. DHCPv4 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp4-usage">12.1. DHCPv4 Server Usage</a></span>
 </dt><dt><span class="section"><a href="#dhcp4-config">12.2. DHCPv4 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp4-std">12.3. Supported standards</a></span></dt><dt><span class="section"><a href="#dhcp4-limit">12.4. DHCPv4 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#dhcp6">13. DHCPv6 Server</a></span></dt><dd><dl><dt><span class="section"><a href="#dhcp6-usage">13.1. DHCPv6 Server Usage</a></span></dt><dt><span class="section"><a href="#dhcp6-config">13.2. DHCPv6 Server Configuration</a></span></dt><dt><span class="section"><a href="#dhcp6-std">13.3. Supported DHCPv6 Standards</a></span></dt><dt><span class="section"><a href="#dhcp6-limit">13.4. DHCPv6 Server Limitations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#libdhcp">14. libdhcp++ library</a></span></dt><dd><dl><dt><span class="section"><a href="#iface-detect">14.1. Interface detection</a></span></dt><dt><span class="section"><a href="#
 packet-handling">14.2. DHCPv4/DHCPv6 packet handling</a></span></dt></dl></dd><dt><span class="chapter"><a href="#statistics">15. Statistics</a></span></dt><dt><span class="chapter"><a href="#logging">16. Logging</a></span></dt><dd><dl><dt><span class="section"><a href="#idp295312">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#idp296304">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#idp317512">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#idp331704">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#idp349208">16.2. Logging Message Format</a></span></dt></dl></dd></dl></div><div class="list-of-tables"><p><b>List of Tables</b></p><dl><dt>3.1. <a href="#idp106648"></a></dt></dl></div><div class="preface" title="Preface"><div class="titlepage"><div><div><h2 class="title"><a name="idp17128"></a>Preface</h2></div></div></div><div class="toc"><p><b>Table of C
 ontents</b></p><dl><dt><span class="section"><a href="#acknowledgements">1. Acknowledgements</a></span></dt></dl></div><div class="section" title="1. Acknowledgements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="acknowledgements"></a>1. Acknowledgements</h2></div></div></div><p>ISC would like to acknowledge generous support for
       BIND 10 development of DHCPv4 and DHCPv6 components provided
-      by <a class="ulink" href="http://www.comcast.com/" target="_top">Comcast</a>.</p></div></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp64344">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></div><p>
+      by <a class="ulink" href="http://www.comcast.com/" target="_top">Comcast</a>.</p></div></div><div class="chapter" title="Chapter 1. Introduction"><div class="titlepage"><div><div><h2 class="title"><a name="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp20200">1.1. Supported Platforms</a></span></dt><dt><span class="section"><a href="#required-software">1.2. Required Software</a></span></dt><dt><span class="section"><a href="#starting_stopping">1.3. Starting and Stopping the Server</a></span></dt><dt><span class="section"><a href="#managing_once_running">1.4. Managing BIND 10</a></span></dt></dl></div><p>
       BIND is the popular implementation of a DNS server, developer
       interfaces, and DNS tools.
       BIND 10 is a rewrite of BIND 9.  BIND 10 is written in C++ and Python
@@ -22,8 +22,8 @@
       provides forwarding.
     </p><p>
       This guide covers the experimental prototype of
-      BIND 10 version 20120127.
-    </p><div class="section" title="1.1. Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp64344"></a>1.1. Supported Platforms</h2></div></div></div><p>
+      BIND 10 version 20120405.
+    </p><div class="section" title="1.1. Supported Platforms"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp20200"></a>1.1. Supported Platforms</h2></div></div></div><p>
   BIND 10 builds have been tested on Debian GNU/Linux 5 and unstable,
   Ubuntu 9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, CentOS
   Linux 5.3, and MacOS 10.6.
@@ -78,11 +78,6 @@
       </p><p>
 
         </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
-              <span class="command"><strong>b10-msgq</strong></span> —
-              Message bus daemon.
-              This process coordinates communication between all of the other
-              BIND 10 processes.
-            </li><li class="listitem">
               <span class="command"><strong>b10-auth</strong></span> —
               Authoritative DNS server.
               This process serves DNS requests.
@@ -95,15 +90,29 @@
               Command and control service.
               This process allows external control of the BIND 10 system.
             </li><li class="listitem">
+              <span class="command"><strong>b10-msgq</strong></span> —
+              Message bus daemon.
+              This process coordinates communication between all of the other
+              BIND 10 processes.
+            </li><li class="listitem">
               <span class="command"><strong>b10-resolver</strong></span> —
               Recursive name server.
               This process handles incoming queries.
 
             </li><li class="listitem">
+              <span class="command"><strong>b10-sockcreator</strong></span> —
+              Socket creator daemon.
+              This process creates sockets used by
+              network-listening BIND 10 processes.
+            </li><li class="listitem">
               <span class="command"><strong>b10-stats</strong></span> —
               Statistics collection daemon.
               This process collects and reports statistics data.
             </li><li class="listitem">
+              <span class="command"><strong>b10-stats-httpd</strong></span> —
+              HTTP server for statistics reporting.
+              This process reports statistics data in XML format over HTTP.
+            </li><li class="listitem">
               <span class="command"><strong>b10-xfrin</strong></span> —
               Incoming zone transfer service.
               This process is used to transfer a new copy
@@ -129,8 +138,9 @@
         </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
               <span class="command"><strong>bindctl</strong></span> —
               interactive administration interface.
-              This is a command-line tool which allows an administrator
-              to control BIND 10.
+              This is a low-level command-line tool which allows
+              a developer or an experienced administrator to control
+              BIND 10.
             </li><li class="listitem">
               <span class="command"><strong>b10-loadzone</strong></span> —
               zone file loader.
@@ -152,7 +162,7 @@
       and, of course, DNS. These include detailed developer
       documentation and code examples.
 
-    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#idp113000">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#idp114472">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#idp119504">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#idp126792">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#idp127848">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#idp129504">2
 .3.6. Install Hierarchy</a></span></dt></dl></dd></dl></div><div class="section" title="2.1. Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="build-requirements"></a>2.1. Building Requirements</h2></div></div></div><p>
+    </p></div><div class="chapter" title="Chapter 2. Installation"><div class="titlepage"><div><div><h2 class="title"><a name="installation"></a>Chapter 2. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#build-requirements">2.1. Building Requirements</a></span></dt><dt><span class="section"><a href="#quickstart">2.2. Quick start</a></span></dt><dt><span class="section"><a href="#install">2.3. Installation from source</a></span></dt><dd><dl><dt><span class="section"><a href="#idp70704">2.3.1. Download Tar File</a></span></dt><dt><span class="section"><a href="#idp72200">2.3.2. Retrieve from Git</a></span></dt><dt><span class="section"><a href="#idp77232">2.3.3. Configure before the build</a></span></dt><dt><span class="section"><a href="#idp84464">2.3.4. Build</a></span></dt><dt><span class="section"><a href="#idp85536">2.3.5. Install</a></span></dt><dt><span class="section"><a href="#idp87192">2.3.6. 
 Install Hierarchy</a></span></dt></dl></dd></dl></div><div class="section" title="2.1. Building Requirements"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="build-requirements"></a>2.1. Building Requirements</h2></div></div></div><p>
           In addition to the run-time requirements, building BIND 10
           from source code requires various development include headers.
         </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
@@ -214,14 +224,14 @@
         the Git code revision control system or as a downloadable
         tar file. It may also be available in pre-compiled ready-to-use
         packages from operating system vendors.
-      </p><div class="section" title="2.3.1. Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="idp113000"></a>2.3.1. Download Tar File</h3></div></div></div><p>
+      </p><div class="section" title="2.3.1. Download Tar File"><div class="titlepage"><div><div><h3 class="title"><a name="idp70704"></a>2.3.1. Download Tar File</h3></div></div></div><p>
           Downloading a release tar file is the recommended method to
           obtain the source code.
         </p><p>
           The BIND 10 releases are available as tar file downloads from
           <a class="ulink" href="ftp://ftp.isc.org/isc/bind10/" target="_top">ftp://ftp.isc.org/isc/bind10/</a>.
           Periodic development snapshots may also be available.
-        </p></div><div class="section" title="2.3.2. Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="idp114472"></a>2.3.2. Retrieve from Git</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.2. Retrieve from Git"><div class="titlepage"><div><div><h3 class="title"><a name="idp72200"></a>2.3.2. Retrieve from Git</h3></div></div></div><p>
           Downloading this "bleeding edge" code is recommended only for
           developers or advanced users.  Using development code in a production
           environment is not recommended.
@@ -255,7 +265,7 @@
           <span class="command"><strong>autoheader</strong></span>,
           <span class="command"><strong>automake</strong></span>,
           and related commands.
-        </p></div><div class="section" title="2.3.3. Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="idp119504"></a>2.3.3. Configure before the build</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.3. Configure before the build"><div class="titlepage"><div><div><h3 class="title"><a name="idp77232"></a>2.3.3. Configure before the build</h3></div></div></div><p>
           BIND 10 uses the GNU Build System to discover build environment
           details.
           To generate the makefiles using the defaults, simply run:
@@ -286,16 +296,16 @@
         </p><p>
           If the configure fails, it may be due to missing or old
           dependencies.
-        </p></div><div class="section" title="2.3.4. Build"><div class="titlepage"><div><div><h3 class="title"><a name="idp126792"></a>2.3.4. Build</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.4. Build"><div class="titlepage"><div><div><h3 class="title"><a name="idp84464"></a>2.3.4. Build</h3></div></div></div><p>
     After the configure step is complete, to build the executables
     from the C++ code and prepare the Python scripts, run:
 
           </p><pre class="screen">$ <strong class="userinput"><code>make</code></strong></pre><p>
-        </p></div><div class="section" title="2.3.5. Install"><div class="titlepage"><div><div><h3 class="title"><a name="idp127848"></a>2.3.5. Install</h3></div></div></div><p>
+        </p></div><div class="section" title="2.3.5. Install"><div class="titlepage"><div><div><h3 class="title"><a name="idp85536"></a>2.3.5. Install</h3></div></div></div><p>
           To install the BIND 10 executables, support files,
           and documentation, run:
           </p><pre class="screen">$ <strong class="userinput"><code>make install</code></strong></pre><p>
-        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="2.3.6. Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="idp129504"></a>2.3.6. Install Hierarchy</h3></div></div></div><p>
+        </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>The install step may require superuser privileges.</p></div></div><div class="section" title="2.3.6. Install Hierarchy"><div class="titlepage"><div><div><h3 class="title"><a name="idp87192"></a>2.3.6. Install Hierarchy</h3></div></div></div><p>
           The following is the layout of the complete BIND 10 installation:
           </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem">
                 <code class="filename">bin/</code> —
@@ -349,12 +359,10 @@
     </p><p>
       In its default configuration, the <span class="command"><strong>bind10</strong></span>
       master process will also start up
-      <span class="command"><strong>b10-cmdctl</strong></span> for admins to communicate with the
-      system, <span class="command"><strong>b10-auth</strong></span> for authoritative DNS service,
-      <span class="command"><strong>b10-stats</strong></span> for statistics collection,
-      <span class="command"><strong>b10-xfrin</strong></span> for inbound DNS zone transfers,
-      <span class="command"><strong>b10-xfrout</strong></span> for outbound DNS zone transfers,
-      and <span class="command"><strong>b10-zonemgr</strong></span> for secondary service.
+      <span class="command"><strong>b10-cmdctl</strong></span> for administration tools to
+      communicate with the system,
+      <span class="command"><strong>b10-stats</strong></span> for statistics collection, and
+      <span class="command"><strong>b10-stats-httpd</strong></span> for statistics reporting.
     </p><div class="section" title="3.1. Starting BIND 10"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="start"></a>3.1. Starting BIND 10</h2></div></div></div><p>
         To start the BIND 10 service, simply run <span class="command"><strong>bind10</strong></span>.
         Run it with the <code class="option">--verbose</code> switch to
@@ -372,12 +380,7 @@
         The configuration is in the Boss/components section. Each element
         represents one component, which is an abstraction of a process
         (currently there's also one component which doesn't represent
-        a process). If you didn't want to transfer out at all (your server
-        is a slave only), you would just remove the corresponding component
-        from the set, like this and the process would be stopped immediately
-        (and not started on the next startup):
-      </p><pre class="screen">> <strong class="userinput"><code>config remove Boss/components b10-xfrout</code></strong>
-> <strong class="userinput"><code>config commit</code></strong></pre><p>
+        a process).
       </p><p>
         To add a process to the set, let's say the resolver (which not started
         by default), you would do this:
@@ -395,7 +398,7 @@
         during startup or shutdown. Unless specified, the component is started
         in usual way. This is the list of components that need to be started
         in a special way, with the value of special used for them:
-        </p><div class="table"><a name="idp150584"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left" class="component"><col align="left" class="special"><col align="left" class="description"></colgroup><thead><tr><th align="left">Component</th><th align="left">Special</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">b10-auth</td><td align="left">auth</td><td align="left">Authoritative server</td></tr><tr><td align="left">b10-resolver</td><td align="left">resolver</td><td align="left">The resolver</td></tr><tr><td align="left">b10-cmdctl</td><td align="left">cmdctl</td><td align="left">The command control (remote control interface)</td></tr></tbody></table></div></div><p><br class="table-break">
+        </p><div class="table"><a name="idp106648"></a><p class="title"><b>Table 3.1. </b></p><div class="table-contents"><table border="1"><colgroup><col align="left"><col align="left"><col align="left"></colgroup><thead><tr><th align="left">Component</th><th align="left">Special</th><th align="left">Description</th></tr></thead><tbody><tr><td align="left">b10-auth</td><td align="left">auth</td><td align="left">Authoritative server</td></tr><tr><td align="left">b10-resolver</td><td align="left">resolver</td><td align="left">The resolver</td></tr><tr><td align="left">b10-cmdctl</td><td align="left">cmdctl</td><td align="left">The command control (remote control interface)</td></tr></tbody></table></div></div><p><br class="table-break">
       </p><p>
         The kind specifies how a failure of the component should
         be handled.  If it is set to <span class="quote">“<span class="quote">dispensable</span>”</span>
@@ -434,7 +437,7 @@
           This system allows you to start the same component multiple times
           (by including it in the configuration with different names, but the
           same process setting). However, the rest of the system doesn't expect
-          such situation, so it would probably not do what you want. Such
+          such a situation, so it would probably not do what you want. Such
           support is yet to be implemented.
         </p></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
           The configuration is quite powerful, but that includes
@@ -442,10 +445,10 @@
           <span class="command"><strong>b10-cmdctl</strong></span>, but then you couldn't
           change it back the usual way, as it would require it to
           be running (you would have to find and edit the configuration
-          directly).  Also, some modules might have dependencies
-          -- <span class="command"><strong>b10-stats-httpd</strong></span> need
+          directly).  Also, some modules might have dependencies:
+          <span class="command"><strong>b10-stats-httpd</strong></span> needs
           <span class="command"><strong>b10-stats</strong></span>, <span class="command"><strong>b10-xfrout</strong></span>
-          needs the <span class="command"><strong>b10-auth</strong></span> to be running, etc.
+          needs <span class="command"><strong>b10-auth</strong></span> to be running, etc.
 
 
 
@@ -499,7 +502,7 @@
         manager via <span class="command"><strong>b10-cmdctl</strong></span>'s REST-ful interface.
         <span class="command"><strong>b10-cmdctl</strong></span> is covered in <a class="xref" href="#cmdctl" title="Chapter 6. Remote control daemon">Chapter 6, <i>Remote control daemon</i></a>.
       </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-          The development prototype release only provides the
+          The development prototype release only provides
           <span class="command"><strong>bindctl</strong></span> as a user interface to
           <span class="command"><strong>b10-cmdctl</strong></span>.
           Upcoming releases will provide another interactive command-line
@@ -586,7 +589,7 @@
       The port can be set by using the <code class="option">--port</code> command line option.
       The address to listen on can be set using the <code class="option">--address</code> command
       line argument.
-      Each HTTPS connection is stateless and timesout in 1200 seconds
+      Each HTTPS connection is stateless and times out in 1200 seconds
       by default.  This can be
       redefined by using the <code class="option">--idle-timeout</code> command line argument.
     </p><div class="section" title="6.1. Configuration specification for b10-cmdctl"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmdctl.spec"></a>6.1. Configuration specification for b10-cmdctl</h2></div></div></div><p>
@@ -623,32 +626,92 @@ shutdown
       the details and relays (over a <span class="command"><strong>b10-msgq</strong></span> command
       channel) the configuration on to the specified module.
     </p><p>
-    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp200360">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#idp205096">8.2. Data Source Backends</a></span></dt><dt><span class="section"><a href="#idp207592">8.3. Loading Master Zones Files</a></span></dt></dl></div><p>
+    </p></div><div class="chapter" title="Chapter 8. Authoritative Server"><div class="titlepage"><div><div><h2 class="title"><a name="authserver"></a>Chapter 8. Authoritative Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp156456">8.1. Server Configurations</a></span></dt><dt><span class="section"><a href="#idp174000">8.2. Data Source Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#in-memory-datasource">8.2.1. In-memory Data Source</a></span></dt></dl></dd><dt><span class="section"><a href="#idp185968">8.3. Loading Master Zones Files</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-auth</strong></span> is the authoritative DNS server.
       It supports EDNS0 and DNSSEC. It supports IPv6.
       Normally it is started by the <span class="command"><strong>bind10</strong></span> master
       process.
-    </p><div class="section" title="8.1. Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp200360"></a>8.1. Server Configurations</h2></div></div></div><p>
+    </p><div class="section" title="8.1. Server Configurations"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp156456"></a>8.1. Server Configurations</h2></div></div></div><p>
         <span class="command"><strong>b10-auth</strong></span> is configured via the
         <span class="command"><strong>b10-cfgmgr</strong></span> configuration manager.
         The module name is <span class="quote">“<span class="quote">Auth</span>”</span>.
-        The configuration data item is:
+        The configuration data items are:
 
         </p><div class="variablelist"><dl><dt><span class="term">database_file</span></dt><dd>This is an optional string to define the path to find
                  the SQLite3 database file.
 
 Note: Later the DNS server will use various data source backends.
 This may be a temporary setting until then.
+              </dd><dt><span class="term">datasources</span></dt><dd>
+      <code class="varname">datasources</code> configures data sources.
+      The list items include:
+      <code class="varname">type</code> to define the required data source type
+      (such as <span class="quote">“<span class="quote">memory</span>”</span>);
+      <code class="varname">class</code> to optionally select the class
+      (it defaults to <span class="quote">“<span class="quote">IN</span>”</span>);
+      and
+      <code class="varname">zones</code> to define the
+      <code class="varname">file</code> path name and the
+      <code class="varname">origin</code> (default domain).
+
+      By default, this is empty.
+
+      <div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+        In this development version, currently this is only used for the
+        memory data source.
+        Only the IN class is supported at this time.
+        By default, the memory data source is disabled.
+        Also, currently the zone file must be canonical such as
+        generated by <span class="command"><strong>named-compilezone -D</strong></span>.
+      </p></div>
+
+              </dd><dt><span class="term">listen_on</span></dt><dd>
+      <code class="varname">listen_on</code> is a list of addresses and ports for
+      <span class="command"><strong>b10-auth</strong></span> to listen on.
+      The list items are the <code class="varname">address</code> string
+      and <code class="varname">port</code> number.
+      By default, <span class="command"><strong>b10-auth</strong></span> listens on port 53
+      on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses.
+              </dd><dt><span class="term">statistics-interval</span></dt><dd>
+      <code class="varname">statistics-interval</code> is the timer interval
+      in seconds for <span class="command"><strong>b10-auth</strong></span> to share its
+      statistics information to
+      <span class="citerefentry"><span class="refentrytitle">b10-stats</span>(8)</span>.
+      Statistics updates can be disabled by setting this to 0.
+      The default is 60.
               </dd></dl></div><p>
 
       </p><p>
 
-        The configuration command is:
-
-        </p><div class="variablelist"><dl><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
+        The configuration commands are:
+
+        </p><div class="variablelist"><dl><dt><span class="term">loadzone</span></dt><dd>
+      <span class="command"><strong>loadzone</strong></span> tells <span class="command"><strong>b10-auth</strong></span>
+      to load or reload a zone file. The arguments include:
+      <code class="varname">class</code> which optionally defines the class
+      (it defaults to <span class="quote">“<span class="quote">IN</span>”</span>);
+      <code class="varname">origin</code> is the domain name of the zone;
+      and
+      <code class="varname">datasrc</code> optionally defines the type of datasource
+      (it defaults to <span class="quote">“<span class="quote">memory</span>”</span>).
+
+      <div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+        In this development version, currently this only supports the
+        IN class and the memory data source.
+      </p></div>
+              </dd><dt><span class="term">sendstats</span></dt><dd>
+      <span class="command"><strong>sendstats</strong></span> tells <span class="command"><strong>b10-auth</strong></span>
+      to send its statistics data to
+      <span class="citerefentry"><span class="refentrytitle">b10-stats</span>(8)</span>
+      immediately.
+              </dd><dt><span class="term">shutdown</span></dt><dd>Stop the authoritative DNS server.
+      This has an optional <code class="varname">pid</code> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
               </dd></dl></div><p>
 
-      </p></div><div class="section" title="8.2. Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp205096"></a>8.2. Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+      </p></div><div class="section" title="8.2. Data Source Backends"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp174000"></a>8.2. Data Source Backends</h2></div></div></div><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         For the development prototype release, <span class="command"><strong>b10-auth</strong></span>
         supports a SQLite3 data source backend and in-memory data source
         backend.
@@ -660,11 +723,57 @@ This may be a temporary setting until then.
         (The full path is what was defined at build configure time for
         <code class="option">--localstatedir</code>.
         The default is <code class="filename">/usr/local/var/</code>.)
-  This data file location may be changed by defining the
-  <span class="quote">“<span class="quote">database_file</span>”</span> configuration.
-      </p></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp207592"></a>8.3. Loading Master Zones Files</h2></div></div></div><p>
+	This data file location may be changed by defining the
+	<span class="quote">“<span class="quote">database_file</span>”</span> configuration.
+      </p><div class="section" title="8.2.1. In-memory Data Source"><div class="titlepage"><div><div><h3 class="title"><a name="in-memory-datasource"></a>8.2.1. In-memory Data Source</h3></div></div></div><p>
+
+	  The following commands to <span class="command"><strong>bindctl</strong></span>
+	  provide an example of configuring an in-memory data
+	  source containing the <span class="quote">“<span class="quote">example.com</span>”</span> zone
+	  with the zone file named <span class="quote">“<span class="quote">example.com.zone</span>”</span>:
+
+
+
+          </p><pre class="screen">> <strong class="userinput"><code>config add Auth/datasources</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[0]/type "<code class="option">memory</code>"</code></strong>
+> <strong class="userinput"><code>config add Auth/datasources[0]/zones</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[0]/zones[0]/origin "<code class="option">example.com</code>"</code></strong>
+> <strong class="userinput"><code>config set Auth/datasources[0]/zones[0]/file "<code class="option">example.com.zone</code>"</code></strong>
+> <strong class="userinput"><code>config commit</code></strong></pre><p>
+
+	  The authoritative server will begin serving it immediately
+	  after it is loaded.
+	</p><p>
+	  Use the <span class="command"><strong>Auth loadzone</strong></span> command in
+	  <span class="command"><strong>bindctl</strong></span> to reload a changed master
+	  file into memory; for example:
+
+	  </p><pre class="screen">> <strong class="userinput"><code>Auth loadzone origin="example.com"</code></strong>
+</pre><p>
+
+	</p><p>
+	By default, the memory data source is disabled; it must be
+	configured explicitly.  To disable all the in-memory zones,
+	specify a null list for <code class="varname">Auth/datasources</code>:
+
+
+
+	  </p><pre class="screen">> <strong class="userinput"><code>config set Auth/datasources/ []</code></strong>
+> <strong class="userinput"><code>config commit</code></strong></pre><p>
+	</p><p>
+          The following example stops serving a specific zone:
+
+	  </p><pre class="screen">> <strong class="userinput"><code>config remove Auth/datasources[<code class="option">0</code>]/zones[<code class="option">0</code>]</code></strong>
+> <strong class="userinput"><code>config commit</code></strong></pre><p>
+
+	  (Replace the list number(s) in
+	  <code class="varname">datasources[<em class="replaceable"><code>0</code></em>]</code>
+	  and/or <code class="varname">zones[<em class="replaceable"><code>0</code></em>]</code>
+	  for the relevant zone as needed.)
+
+	</p></div></div><div class="section" title="8.3. Loading Master Zones Files"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp185968"></a>8.3. Loading Master Zones Files</h2></div></div></div><p>
         RFC 1035 style DNS master zone files may imported
-        into a BIND 10 data source by using the
+        into a BIND 10 SQLite3 data source by using the
         <span class="command"><strong>b10-loadzone</strong></span> utility.
       </p><p>
         <span class="command"><strong>b10-loadzone</strong></span> supports the following
@@ -681,7 +790,7 @@ This may be a temporary setting until then.
         default origin for loaded zone file records.
       </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         In the development prototype release, only the SQLite3 back
-        end is used.
+        end is used by <span class="command"><strong>b10-loadzone</strong></span>.
         By default, it stores the zone data in
         <code class="filename">/usr/local/var/bind10-devel/zone.sqlite3</code>
         unless the <code class="option">-d</code> switch is used to set the
@@ -691,7 +800,7 @@ This may be a temporary setting until then.
         If you reload a zone already existing in the database,
         all records from that prior zone disappear and a whole new set
         appears.
-      </p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp217864">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#idp220904">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#idp11976">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></div><p>
+      </p></div></div><div class="chapter" title="Chapter 9. Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title"><a name="xfrin"></a>Chapter 9. Incoming Zone Transfers</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp196456">9.1. Configuration for Incoming Zone Transfers</a></span></dt><dt><span class="section"><a href="#idp199496">9.2. Enabling IXFR</a></span></dt><dt><span class="section"><a href="#zonemgr">9.3. Secondary Manager</a></span></dt><dt><span class="section"><a href="#idp209248">9.4. Trigger an Incoming Zone Transfer Manually</a></span></dt></dl></div><p>
       Incoming zones are transferred using the <span class="command"><strong>b10-xfrin</strong></span>
       process which is started by <span class="command"><strong>bind10</strong></span>.
       When received, the zone is stored in the corresponding BIND 10
@@ -709,7 +818,7 @@ This may be a temporary setting until then.
      In the current development release of BIND 10, incoming zone
      transfers are only available for SQLite3-based data sources,
      that is, they don't work for an in-memory data source.
-    </p></div><div class="section" title="9.1. Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp217864"></a>9.1. Configuration for Incoming Zone Transfers</h2></div></div></div><p>
+    </p></div><div class="section" title="9.1. Configuration for Incoming Zone Transfers"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp196456"></a>9.1. Configuration for Incoming Zone Transfers</h2></div></div></div><p>
         In practice, you need to specify a list of secondary zones to
         enable incoming zone transfers for these zones (you can still
         trigger a zone transfer manually, without a prior configuration
@@ -725,7 +834,7 @@ This may be a temporary setting until then.
 > <strong class="userinput"><code>config commit</code></strong></pre><p>
 
       (We assume there has been no zone configuration before).
-      </p></div><div class="section" title="9.2. Enabling IXFR"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp220904"></a>9.2. Enabling IXFR</h2></div></div></div><p>
+      </p></div><div class="section" title="9.2. Enabling IXFR"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp199496"></a>9.2. Enabling IXFR</h2></div></div></div><p>
         As noted above, <span class="command"><strong>b10-xfrin</strong></span> uses AXFR for
         zone transfers by default.  To enable IXFR for zone transfers
         for a particular zone, set the <strong class="userinput"><code>use_ixfr</code></strong>
@@ -777,7 +886,7 @@ This may be a temporary setting until then.
         (i.e. no SOA record for it), <span class="command"><strong>b10-zonemgr</strong></span>
         will automatically tell <span class="command"><strong>b10-xfrin</strong></span>
         to transfer the zone in.
-      </p></div><div class="section" title="9.4. Trigger an Incoming Zone Transfer Manually"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp11976"></a>9.4. Trigger an Incoming Zone Transfer Manually</h2></div></div></div><p>
+      </p></div><div class="section" title="9.4. Trigger an Incoming Zone Transfer Manually"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp209248"></a>9.4. Trigger an Incoming Zone Transfer Manually</h2></div></div></div><p>
         To manually trigger a zone transfer to retrieve a remote zone,
         you may use the <span class="command"><strong>bindctl</strong></span> utility.
         For example, at the <span class="command"><strong>bindctl</strong></span> prompt run:
@@ -815,41 +924,28 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
         for <code class="option">transfer_acl</code> were divided for
         readability.  In the actual input it must be in a single line.
     </p></div><p>
-      If you want to require TSIG in access control, a separate TSIG
-      "key ring" must be configured specifically
-      for <span class="command"><strong>b10-xfrout</strong></span> as well as a system wide
-      key ring, both containing a consistent set of keys.
+      If you want to require TSIG in access control, a system wide TSIG
+      "key ring" must be configured.
       For example, to change the previous example to allowing requests
       from 192.0.2.1 signed by a TSIG with a key name of
       "key.example", you'll need to do this:
     </p><pre class="screen">> <strong class="userinput"><code>config set tsig_keys/keys ["key.example:<base64-key>"]</code></strong>
-> <strong class="userinput"><code>config set Xfrout/tsig_keys/keys ["key.example:<base64-key>"]</code></strong>
 > <strong class="userinput"><code>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]</code></strong>
-> <strong class="userinput"><code>config commit</code></strong></pre><p>
-      The first line of configuration defines a system wide key ring.
-      This is necessary because the <span class="command"><strong>b10-auth</strong></span> server
-      also checks TSIGs and it uses the system wide configuration.
-    </p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
-        In a future version, <span class="command"><strong>b10-xfrout</strong></span> will also
-        use the system wide TSIG configuration.
+> <strong class="userinput"><code>config commit</code></strong></pre><p>Both Xfrout and Auth will use the system wide keyring to check
+    TSIGs in the incoming messages and to sign responses.</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
         The way to specify zone specific configuration (ACLs, etc) is
-        likely to be changed, too.
-    </p></div></div><div class="chapter" title="Chapter 11. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 11. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp259896">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#idp269088">11.2. Forwarding</a></span></dt></dl></div><p>
+        likely to be changed.
+    </p></div></div><div class="chapter" title="Chapter 11. Recursive Name Server"><div class="titlepage"><div><div><h2 class="title"><a name="resolverserver"></a>Chapter 11. Recursive Name Server</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp228136">11.1. Access Control</a></span></dt><dt><span class="section"><a href="#idp237328">11.2. Forwarding</a></span></dt></dl></div><p>
       The <span class="command"><strong>b10-resolver</strong></span> process is started by
       <span class="command"><strong>bind10</strong></span>.
 
     </p><p>
       The main <span class="command"><strong>bind10</strong></span> process can be configured
       to select to run either the authoritative or resolver or both.
-      By default, it starts the authoritative service.
-
-
-      You may change this using <span class="command"><strong>bindctl</strong></span>, for example:
+      By default, it doesn't start either one. You may change this using
+      <span class="command"><strong>bindctl</strong></span>, for example:
 
       </p><pre class="screen">
-> <strong class="userinput"><code>config remove Boss/components b10-xfrout</code></strong>
-> <strong class="userinput"><code>config remove Boss/components b10-xfrin</code></strong>
-> <strong class="userinput"><code>config remove Boss/components b10-auth</code></strong>
 > <strong class="userinput"><code>config add Boss/components b10-resolver</code></strong>
 > <strong class="userinput"><code>config set Boss/components/b10-resolver/special resolver</code></strong>
 > <strong class="userinput"><code>config set Boss/components/b10-resolver/kind needed</code></strong>
@@ -873,7 +969,7 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 </pre><p>
     </p><p>(Replace the <span class="quote">“<span class="quote"><em class="replaceable"><code>2</code></em></span>”</span>
        as needed; run <span class="quote">“<span class="quote"><strong class="userinput"><code>config show
-       Resolver/listen_on</code></strong></span>”</span> if needed.)</p><div class="section" title="11.1. Access Control"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp259896"></a>11.1. Access Control</h2></div></div></div><p>
+       Resolver/listen_on</code></strong></span>”</span> if needed.)</p><div class="section" title="11.1. Access Control"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp228136"></a>11.1. Access Control</h2></div></div></div><p>
         By default, the <span class="command"><strong>b10-resolver</strong></span> daemon only accepts
         DNS queries from the localhost (127.0.0.1 and ::1).
         The <code class="option">Resolver/query_acl</code> configuration may
@@ -906,7 +1002,7 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</pre><p>
 </pre><p>(Replace the <span class="quote">“<span class="quote"><em class="replaceable"><code>2</code></em></span>”</span>
        as needed; run <span class="quote">“<span class="quote"><strong class="userinput"><code>config show
        Resolver/query_acl</code></strong></span>”</span> if needed.)</p><div class="note" title="Note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>This prototype access control configuration
-      syntax may be changed.</p></div></div><div class="section" title="11.2. Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp269088"></a>11.2. Forwarding</h2></div></div></div><p>
+      syntax may be changed.</p></div></div><div class="section" title="11.2. Forwarding"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp237328"></a>11.2. Forwarding</h2></div></div></div><p>
 
         To enable forwarding, the upstream address and port must be
         configured to forward queries to, such as:
@@ -1218,7 +1314,7 @@ eth0 fe80::21e:8cff:fe9b:7349
     }
 }
        </pre><p>
-    </p></div><div class="chapter" title="Chapter 16. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 16. Logging</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp327280">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#idp328272">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#idp349480">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#idp362088">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#idp379592">16.2. Logging Message Format</a></span></dt></dl></div><div class="section" title="16.1. Logging configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp327280"></a>16.1. Logging configuration</h2></div></div></div><p>
+    </p></div><div class="chapter" title="Chapter 16. Logging"><div class="titlepage"><div><div><h2 class="title"><a name="logging"></a>Chapter 16. Logging</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="#idp295312">16.1. Logging configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#idp296304">16.1.1. Loggers</a></span></dt><dt><span class="section"><a href="#idp317512">16.1.2. Output Options</a></span></dt><dt><span class="section"><a href="#idp331704">16.1.3. Example session</a></span></dt></dl></dd><dt><span class="section"><a href="#idp349208">16.2. Logging Message Format</a></span></dt></dl></div><div class="section" title="16.1. Logging configuration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp295312"></a>16.1. Logging configuration</h2></div></div></div><p>
 
         The logging system in BIND 10 is configured through the
         Logging module. All BIND 10 modules will look at the
@@ -1227,7 +1323,7 @@ eth0 fe80::21e:8cff:fe9b:7349
 
 
 
-      </p><div class="section" title="16.1.1. Loggers"><div class="titlepage"><div><div><h3 class="title"><a name="idp328272"></a>16.1.1. Loggers</h3></div></div></div><p>
+      </p><div class="section" title="16.1.1. Loggers"><div class="titlepage"><div><div><h3 class="title"><a name="idp296304"></a>16.1.1. Loggers</h3></div></div></div><p>
 
           Within BIND 10, a message is logged through a component
           called a "logger". Different parts of BIND 10 log messages
@@ -1238,7 +1334,7 @@ eth0 fe80::21e:8cff:fe9b:7349
 
           In the Logging module, you can specify the configuration
           for zero or more loggers; any that are not specified will
-          take appropriate default values..
+          take appropriate default values.
 
         </p><p>
 
@@ -1248,7 +1344,7 @@ eth0 fe80::21e:8cff:fe9b:7349
           (what to log), and the <code class="option">output_options</code>
           (where to log).
 
-        </p><div class="section" title="16.1.1.1. name (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp330520"></a>16.1.1.1. name (string)</h4></div></div></div><p>
+        </p><div class="section" title="16.1.1.1. name (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp298552"></a>16.1.1.1. name (string)</h4></div></div></div><p>
           Each logger in the system has a name, the name being that
           of the component using it to log messages. For instance,
           if you want to configure logging for the resolver module,
@@ -1321,7 +1417,7 @@ eth0 fe80::21e:8cff:fe9b:7349
           <span class="quote">“<span class="quote">Auth.cache</span>”</span> logger will appear in the output
           with a logger name of <span class="quote">“<span class="quote">b10-auth.cache</span>”</span>).
 
-        </p></div><div class="section" title="16.1.1.2. severity (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp340304"></a>16.1.1.2. severity (string)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.2. severity (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp308336"></a>16.1.1.2. severity (string)</h4></div></div></div><p>
 
           This specifies the category of messages logged.
           Each message is logged with an associated severity which
@@ -1337,7 +1433,7 @@ eth0 fe80::21e:8cff:fe9b:7349
 
 
 
-        </p></div><div class="section" title="16.1.1.3. output_options (list)"><div class="titlepage"><div><div><h4 class="title"><a name="idp344096"></a>16.1.1.3. output_options (list)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.3. output_options (list)"><div class="titlepage"><div><div><h4 class="title"><a name="idp312128"></a>16.1.1.3. output_options (list)</h4></div></div></div><p>
 
           Each logger can have zero or more
           <code class="option">output_options</code>. These specify where log
@@ -1347,7 +1443,7 @@ eth0 fe80::21e:8cff:fe9b:7349
 
           The other options for a logger are:
 
-        </p></div><div class="section" title="16.1.1.4. debuglevel (integer)"><div class="titlepage"><div><div><h4 class="title"><a name="idp345320"></a>16.1.1.4. debuglevel (integer)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.4. debuglevel (integer)"><div class="titlepage"><div><div><h4 class="title"><a name="idp313352"></a>16.1.1.4. debuglevel (integer)</h4></div></div></div><p>
 
           When a logger's severity is set to DEBUG, this value
           specifies what debug messages should be printed. It ranges
@@ -1356,7 +1452,7 @@ eth0 fe80::21e:8cff:fe9b:7349
 
           If severity for the logger is not DEBUG, this value is ignored.
 
-        </p></div><div class="section" title="16.1.1.5. additive (true or false)"><div class="titlepage"><div><div><h4 class="title"><a name="idp346760"></a>16.1.1.5. additive (true or false)</h4></div></div></div><p>
+        </p></div><div class="section" title="16.1.1.5. additive (true or false)"><div class="titlepage"><div><div><h4 class="title"><a name="idp314792"></a>16.1.1.5. additive (true or false)</h4></div></div></div><p>
 
           If this is true, the <code class="option">output_options</code> from
           the parent will be used. For example, if there are two
@@ -1370,45 +1466,53 @@ eth0 fe80::21e:8cff:fe9b:7349
 
 
 
-      </p></div></div><div class="section" title="16.1.2. Output Options"><div class="titlepage"><div><div><h3 class="title"><a name="idp349480"></a>16.1.2. Output Options</h3></div></div></div><p>
+      </p></div></div><div class="section" title="16.1.2. Output Options"><div class="titlepage"><div><div><h3 class="title"><a name="idp317512"></a>16.1.2. Output Options</h3></div></div></div><p>
 
           The main settings for an output option are the
           <code class="option">destination</code> and a value called
           <code class="option">output</code>, the meaning of which depends on
           the destination that is set.
 
-        </p><div class="section" title="16.1.2.1. destination (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp350616"></a>16.1.2.1. destination (string)</h4></div></div></div><p>
+        </p><div class="section" title="16.1.2.1. destination (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp318648"></a>16.1.2.1. destination (string)</h4></div></div></div><p>
 
             The destination is the type of output. It can be one of:
 
-          </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> console </li><li class="listitem"> file </li><li class="listitem"> syslog </li></ul></div></div><div class="section" title="16.1.2.2. output (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp352704"></a>16.1.2.2. output (string)</h4></div></div></div><p>
+          </p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"> console </li><li class="listitem"> file </li><li class="listitem"> syslog </li></ul></div></div><div class="section" title="16.1.2.2. output (string)"><div class="titlepage"><div><div><h4 class="title"><a name="idp320736"></a>16.1.2.2. output (string)</h4></div></div></div><p>
 
           Depending on what is set as the output destination, this
           value is interpreted as follows:
 
-        </p><div class="variablelist"><dl><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">console</span>”</span></span></dt><dd>
+        </p><div class="variablelist"><dl><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">console</span>”</span></span></dt><dd><p>
                  The value of output must be one of <span class="quote">“<span class="quote">stdout</span>”</span>
                  (messages printed to standard output) or
                  <span class="quote">“<span class="quote">stderr</span>”</span> (messages printed to standard
                  error).
-              </dd><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span></span></dt><dd>
+              </p><p>
+                Note: if output is set to <span class="quote">“<span class="quote">stderr</span>”</span> and a lot of
+                messages are produced in a short time (e.g. if the logging
+                level is set to DEBUG), you may occasionally see some messages
+                jumbled up together.  This is due to a combination of the way
+                that messages are written to the screen and the unbuffered
+                nature of the standard error stream.  If this occurs, it is
+                recommended that output be set to <span class="quote">“<span class="quote">stdout</span>”</span>.
+              </p></dd><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span></span></dt><dd><p>
                 The value of output is interpreted as a file name;
                 log messages will be appended to this file.
-              </dd><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">syslog</span>”</span></span></dt><dd>
+              </p></dd><dt><span class="term"><code class="option">destination</code> is <span class="quote">“<span class="quote">syslog</span>”</span></span></dt><dd><p>
                 The value of output is interpreted as the
                 <span class="command"><strong>syslog</strong></span> facility (e.g.
                 <span class="emphasis"><em>local0</em></span>) that should be used
                 for log messages.
-              </dd></dl></div><p>
+              </p></dd></dl></div><p>
 
           The other options for <code class="option">output_options</code> are:
 
-        </p><div class="section" title="16.1.2.2.1. flush (true of false)"><div class="titlepage"><div><div><h5 class="title"><a name="idp358664"></a>16.1.2.2.1. flush (true of false)</h5></div></div></div><p>
+        </p><div class="section" title="16.1.2.2.1. flush (true of false)"><div class="titlepage"><div><div><h5 class="title"><a name="idp328280"></a>16.1.2.2.1. flush (true of false)</h5></div></div></div><p>
             Flush buffers after each log message. Doing this will
             reduce performance but will ensure that if the program
             terminates abnormally, all messages up to the point of
             termination are output.
-          </p></div><div class="section" title="16.1.2.2.2. maxsize (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="idp359528"></a>16.1.2.2.2. maxsize (integer)</h5></div></div></div><p>
+          </p></div><div class="section" title="16.1.2.2.2. maxsize (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="idp329144"></a>16.1.2.2.2. maxsize (integer)</h5></div></div></div><p>
             Only relevant when destination is file, this is maximum
             file size of output files in bytes. When the maximum
             size is reached, the file is renamed and a new file opened.
@@ -1417,11 +1521,11 @@ eth0 fe80::21e:8cff:fe9b:7349
             etc.)
           </p><p>
             If this is 0, no maximum file size is used.
-          </p></div><div class="section" title="16.1.2.2.3. maxver (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="idp360776"></a>16.1.2.2.3. maxver (integer)</h5></div></div></div><p>
+          </p></div><div class="section" title="16.1.2.2.3. maxver (integer)"><div class="titlepage"><div><div><h5 class="title"><a name="idp330392"></a>16.1.2.2.3. maxver (integer)</h5></div></div></div><p>
             Maximum number of old log files to keep around when
             rolling the output file. Only relevant when
             <code class="option">destination</code> is <span class="quote">“<span class="quote">file</span>”</span>.
-          </p></div></div></div><div class="section" title="16.1.3. Example session"><div class="titlepage"><div><div><h3 class="title"><a name="idp362088"></a>16.1.3. Example session</h3></div></div></div><p>
+          </p></div></div></div><div class="section" title="16.1.3. Example session"><div class="titlepage"><div><div><h3 class="title"><a name="idp331704"></a>16.1.3. Example session</h3></div></div></div><p>
 
           In this example we want to set the global logging to
           write to the file <code class="filename">/var/log/my_bind10.log</code>,
@@ -1515,7 +1619,7 @@ Logging/loggers[0]/output_options[0]/maxver	0	integer	(default)
 
           </p><pre class="screen">> <strong class="userinput"><code> config set Logging/loggers[0]/output_options[0]/destination file</code></strong>
 > <strong class="userinput"><code> config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log</code></strong>
-> <strong class="userinput"><code> config set Logging/loggers[0]/output_options[0]/maxsize 30000</code></strong>
+> <strong class="userinput"><code> config set Logging/loggers[0]/output_options[0]/maxsize 204800</code></strong>
 > <strong class="userinput"><code> config set Logging/loggers[0]/output_options[0]/maxver 8</code></strong>
 </pre><p>
 
@@ -1534,7 +1638,7 @@ Logging/loggers[0]/additive	false	boolean	(default)
 Logging/loggers[0]/output_options[0]/destination	"file"	string	(modified)
 Logging/loggers[0]/output_options[0]/output	"/var/log/bind10.log"	string	(modified)
 Logging/loggers[0]/output_options[0]/flush	false	boolean	(default)
-Logging/loggers[0]/output_options[0]/maxsize	30000	integer	(modified)
+Logging/loggers[0]/output_options[0]/maxsize	204800	integer	(modified)
 Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 </pre><p>
 
@@ -1582,7 +1686,7 @@ Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
           And every module will now be using the values from the
           logger named <span class="quote">“<span class="quote">*</span>”</span>.
 
-        </p></div></div><div class="section" title="16.2. Logging Message Format"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp379592"></a>16.2. Logging Message Format</h2></div></div></div><p>
+        </p></div></div><div class="section" title="16.2. Logging Message Format"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="idp349208"></a>16.2. Logging Message Format</h2></div></div></div><p>
           Each message written by BIND 10 to the configured logging
           destinations comprises a number of components that identify
           the origin of the message and, if the message indicates
diff --git a/doc/guide/bind10-guide.txt b/doc/guide/bind10-guide.txt
index 7ec35e6..cf81af6 100644
--- a/doc/guide/bind10-guide.txt
+++ b/doc/guide/bind10-guide.txt
@@ -2,9 +2,9 @@
 
 Administrator Reference for BIND 10
 
-   This is the reference guide for BIND 10 version 20120127.
+   This is the reference guide for BIND 10 version 20120405.
 
-   Copyright (c) 2010-2012 Internet Systems Consortium, Inc.
+   Copyright © 2010-2012 Internet Systems Consortium, Inc.
 
    Abstract
 
@@ -14,7 +14,7 @@ Administrator Reference for BIND 10
    for controlling authoritative and recursive DNS servers, and experimental
    DHCPv4 and DHCPv6 servers.
 
-   This is the reference guide for BIND 10 version 20120127. The most
+   This is the reference guide for BIND 10 version 20120405. The most
    up-to-date version of this document (in PDF, HTML, and plain text
    formats), along with other documents for BIND 10, can be found at
    http://bind10.isc.org/docs.
@@ -79,6 +79,8 @@ Administrator Reference for BIND 10
 
                 8.2. Data Source Backends
 
+                             8.2.1. In-memory Data Source
+
                 8.3. Loading Master Zones Files
 
    9. Incoming Zone Transfers
@@ -149,12 +151,12 @@ Preface
 
    1. Acknowledgements
 
-1. Acknowledgements
+1. Acknowledgements
 
    ISC would like to acknowledge generous support for BIND 10 development of
    DHCPv4 and DHCPv6 components provided by Comcast.
 
-Chapter 1. Introduction
+Chapter 1. Introduction
 
    Table of Contents
 
@@ -172,9 +174,9 @@ Chapter 1. Introduction
    DNS. BIND 10 provides a EDNS0- and DNSSEC-capable authoritative DNS server
    and a caching recursive name server which also provides forwarding.
 
-   This guide covers the experimental prototype of BIND 10 version 20120127.
+   This guide covers the experimental prototype of BIND 10 version 20120405.
 
-1.1. Supported Platforms
+1.1. Supported Platforms
 
    BIND 10 builds have been tested on Debian GNU/Linux 5 and unstable, Ubuntu
    9.10, NetBSD 5, Solaris 10, FreeBSD 7 and 8, CentOS Linux 5.3, and MacOS
@@ -182,7 +184,7 @@ Chapter 1. Introduction
    is planned for BIND 10 to build, install and run on Windows and standard
    Unix-type platforms.
 
-1.2. Required Software
+1.2. Required Software
 
    BIND 10 requires at least Python 3.1 (http://www.python.org/). It has also
    been tested with Python 3.2.
@@ -208,7 +210,7 @@ Chapter 1. Introduction
    installation nor standard packages collections. You may need to install
    them separately.
 
-1.3. Starting and Stopping the Server
+1.3. Starting and Stopping the Server
 
    BIND 10 is modular. Part of this modularity is accomplished using multiple
    cooperating processes which, together, provide the server functionality.
@@ -221,39 +223,43 @@ Chapter 1. Introduction
    processes as needed. The processes started by the bind10 command have
    names starting with "b10-", including:
 
-     o b10-msgq -- Message bus daemon. This process coordinates communication
-       between all of the other BIND 10 processes.
-     o b10-auth -- Authoritative DNS server. This process serves DNS
-       requests.
-     o b10-cfgmgr -- Configuration manager. This process maintains all of the
+     o b10-auth — Authoritative DNS server. This process serves DNS requests.
+     o b10-cfgmgr — Configuration manager. This process maintains all of the
        configuration for BIND 10.
-     o b10-cmdctl -- Command and control service. This process allows
-       external control of the BIND 10 system.
-     o b10-resolver -- Recursive name server. This process handles incoming
+     o b10-cmdctl — Command and control service. This process allows external
+       control of the BIND 10 system.
+     o b10-msgq — Message bus daemon. This process coordinates communication
+       between all of the other BIND 10 processes.
+     o b10-resolver — Recursive name server. This process handles incoming
        queries.
-     o b10-stats -- Statistics collection daemon. This process collects and
+     o b10-sockcreator — Socket creator daemon. This process creates sockets
+       used by network-listening BIND 10 processes.
+     o b10-stats — Statistics collection daemon. This process collects and
        reports statistics data.
-     o b10-xfrin -- Incoming zone transfer service. This process is used to
+     o b10-stats-httpd — HTTP server for statistics reporting. This process
+       reports statistics data in XML format over HTTP.
+     o b10-xfrin — Incoming zone transfer service. This process is used to
        transfer a new copy of a zone into BIND 10, when acting as a secondary
        server.
-     o b10-xfrout -- Outgoing zone transfer service. This process is used to
+     o b10-xfrout — Outgoing zone transfer service. This process is used to
        handle transfer requests to send a local zone to a remote secondary
        server, when acting as a master server.
-     o b10-zonemgr -- Secondary manager. This process keeps track of timers
+     o b10-zonemgr — Secondary manager. This process keeps track of timers
        and other necessary information for BIND 10 to act as a slave server.
 
    These are ran automatically by bind10 and do not need to be run manually.
 
-1.4. Managing BIND 10
+1.4. Managing BIND 10
 
    Once BIND 10 is running, a few commands are used to interact directly with
    the system:
 
-     o bindctl -- interactive administration interface. This is a
-       command-line tool which allows an administrator to control BIND 10.
-     o b10-loadzone -- zone file loader. This tool will load standard
+     o bindctl — interactive administration interface. This is a low-level
+       command-line tool which allows a developer or an experienced
+       administrator to control BIND 10.
+     o b10-loadzone — zone file loader. This tool will load standard
        masterfile-format zone files into BIND 10.
-     o b10-cmdctl-usermgr -- user access control. This tool allows an
+     o b10-cmdctl-usermgr — user access control. This tool allows an
        administrator to authorize additional users to manage BIND 10.
 
    The tools and modules are covered in full detail in this guide. In
@@ -263,7 +269,7 @@ Chapter 1. Introduction
    Python for the message bus, configuration backend, and, of course, DNS.
    These include detailed developer documentation and code examples.
 
-Chapter 2. Installation
+Chapter 2. Installation
 
    Table of Contents
 
@@ -285,7 +291,7 @@ Chapter 2. Installation
 
                 2.3.6. Install Hierarchy
 
-2.1. Building Requirements
+2.1. Building Requirements
 
    In addition to the run-time requirements, building BIND 10 from source
    code requires various development include headers.
@@ -311,7 +317,7 @@ Chapter 2. Installation
    Visit the wiki at http://bind10.isc.org/wiki/SystemSpecificNotes for
    system-specific installation tips.
 
-2.2. Quick start
+2.2. Quick start
 
   Note
 
@@ -322,48 +328,48 @@ Chapter 2. Installation
 
    To quickly get started with BIND 10, follow these steps.
 
-    1. Install required run-time and build dependencies.
-    2. Download the BIND 10 source tar file from
+    1. Install required run-time and build dependencies.
+    2. Download the BIND 10 source tar file from
        ftp://ftp.isc.org/isc/bind10/.
-    3. Extract the tar file:
+    3. Extract the tar file:
 
  $ gzcat bind10-VERSION.tar.gz | tar -xvf -
 
-    4. Go into the source and run configure:
+    4. Go into the source and run configure:
 
  $ cd bind10-VERSION
    $ ./configure
 
-    5. Build it:
+    5. Build it:
 
  $ make
 
-    6. Install it (to default /usr/local):
+    6. Install it (to default /usr/local):
 
  $ make install
 
-    7. Start the server:
+    7. Start the server:
 
  $ /usr/local/sbin/bind10
 
-    8. Test it; for example:
+    8. Test it; for example:
 
  $ dig @127.0.0.1 -c CH -t TXT authors.bind
 
-    9. Load desired zone file(s), for example:
+    9. Load desired zone file(s), for example:
 
  $ b10-loadzone your.zone.example.org
 
-   10. Test the new zone.
+   10. Test the new zone.
 
-2.3. Installation from source
+2.3. Installation from source
 
    BIND 10 is open source software written in C++ and Python. It is freely
    available in source code form from ISC via the Git code revision control
    system or as a downloadable tar file. It may also be available in
    pre-compiled ready-to-use packages from operating system vendors.
 
-  2.3.1. Download Tar File
+  2.3.1. Download Tar File
 
    Downloading a release tar file is the recommended method to obtain the
    source code.
@@ -372,7 +378,7 @@ Chapter 2. Installation
    ftp://ftp.isc.org/isc/bind10/. Periodic development snapshots may also be
    available.
 
-  2.3.2. Retrieve from Git
+  2.3.2. Retrieve from Git
 
    Downloading this "bleeding edge" code is recommended only for developers
    or advanced users. Using development code in a production environment is
@@ -387,7 +393,7 @@ Chapter 2. Installation
    The latest development code, including temporary experiments and
    un-reviewed code, is available via the BIND 10 code revision control
    system. This is powered by Git and all the BIND 10 development is public.
-   The leading development is done in the "master".
+   The leading development is done in the “master”.
 
    The code can be checked out from git://git.bind10.isc.org/bind10; for
    example:
@@ -400,7 +406,7 @@ Chapter 2. Installation
    the --install switch. This will run autoconf, aclocal, libtoolize,
    autoheader, automake, and related commands.
 
-  2.3.3. Configure before the build
+  2.3.3. Configure before the build
 
    BIND 10 uses the GNU Build System to discover build environment details.
    To generate the makefiles using the defaults, simply run:
@@ -435,14 +441,14 @@ Chapter 2. Installation
 
    If the configure fails, it may be due to missing or old dependencies.
 
-  2.3.4. Build
+  2.3.4. Build
 
    After the configure step is complete, to build the executables from the
    C++ code and prepare the Python scripts, run:
 
  $ make
 
-  2.3.5. Install
+  2.3.5. Install
 
    To install the BIND 10 executables, support files, and documentation, run:
 
@@ -452,22 +458,22 @@ Chapter 2. Installation
 
    The install step may require superuser privileges.
 
-  2.3.6. Install Hierarchy
+  2.3.6. Install Hierarchy
 
    The following is the layout of the complete BIND 10 installation:
 
-     o bin/ -- general tools and diagnostic clients.
-     o etc/bind10-devel/ -- configuration files.
-     o lib/ -- libraries and python modules.
-     o libexec/bind10-devel/ -- executables that a user wouldn't normally run
+     o bin/ — general tools and diagnostic clients.
+     o etc/bind10-devel/ — configuration files.
+     o lib/ — libraries and python modules.
+     o libexec/bind10-devel/ — executables that a user wouldn't normally run
        directly and are not run independently. These are the BIND 10 modules
        which are daemons started by the bind10 tool.
-     o sbin/ -- commands used by the system administrator.
-     o share/bind10-devel/ -- configuration specifications.
-     o share/man/ -- manual pages (online documentation).
-     o var/bind10-devel/ -- data source and configuration databases.
+     o sbin/ — commands used by the system administrator.
+     o share/bind10-devel/ — configuration specifications.
+     o share/man/ — manual pages (online documentation).
+     o var/bind10-devel/ — data source and configuration databases.
 
-Chapter 3. Starting BIND10 with bind10
+Chapter 3. Starting BIND10 with bind10
 
    Table of Contents
 
@@ -491,12 +497,11 @@ Chapter 3. Starting BIND10 with bind10
    b10-sockcreator will allocate sockets for the rest of the system.
 
    In its default configuration, the bind10 master process will also start up
-   b10-cmdctl for admins to communicate with the system, b10-auth for
-   authoritative DNS service, b10-stats for statistics collection, b10-xfrin
-   for inbound DNS zone transfers, b10-xfrout for outbound DNS zone
-   transfers, and b10-zonemgr for secondary service.
+   b10-cmdctl for administration tools to communicate with the system,
+   b10-stats for statistics collection, and b10-stats-httpd for statistics
+   reporting.
 
-3.1. Starting BIND 10
+3.1. Starting BIND 10
 
    To start the BIND 10 service, simply run bind10. Run it with the --verbose
    switch to get additional debugging or diagnostic output.
@@ -505,23 +510,16 @@ Chapter 3. Starting BIND10 with bind10
 
    If the setproctitle Python module is detected at start up, the process
    names for the Python-based daemons will be renamed to better identify them
-   instead of just "python". This is not needed on some operating systems.
+   instead of just “python”. This is not needed on some operating systems.
 
-3.2. Configuration of started processes
+3.2. Configuration of started processes
 
    The processes to be started can be configured, with the exception of the
    b10-sockcreator, b10-msgq and b10-cfgmgr.
 
    The configuration is in the Boss/components section. Each element
    represents one component, which is an abstraction of a process (currently
-   there's also one component which doesn't represent a process). If you
-   didn't want to transfer out at all (your server is a slave only), you
-   would just remove the corresponding component from the set, like this and
-   the process would be stopped immediately (and not started on the next
-   startup):
-
- > config remove Boss/components b10-xfrout
- > config commit
+   there's also one component which doesn't represent a process).
 
    To add a process to the set, let's say the resolver (which not started by
    default), you would do this:
@@ -541,7 +539,7 @@ Chapter 3. Starting BIND10 with bind10
    usual way. This is the list of components that need to be started in a
    special way, with the value of special used for them:
 
-   Table 3.1.
+   Table 3.1. 
 
    +------------------------------------------------------------------------+
    | Component    | Special  | Description                                  |
@@ -555,11 +553,11 @@ Chapter 3. Starting BIND10 with bind10
    +------------------------------------------------------------------------+
 
    The kind specifies how a failure of the component should be handled. If it
-   is set to "dispensable" (the default unless you set something else), it
-   will get started again if it fails. If it is set to "needed" and it fails
+   is set to “dispensable” (the default unless you set something else), it
+   will get started again if it fails. If it is set to “needed” and it fails
    at startup, the whole bind10 shuts down and exits with error exit code.
    But if it fails some time later, it is just started again. If you set it
-   to "core", you indicate that the system is not usable without the
+   to “core”, you indicate that the system is not usable without the
    component and if such component fails, the system shuts down no matter
    when the failure happened. This is the behaviour of the core components
    (the ones you can't turn off), but you can declare any other components as
@@ -572,10 +570,10 @@ Chapter 3. Starting BIND10 with bind10
    the default is enough.
 
    There are other parameters we didn't use in our example. One of them is
-   "address". It is the address used by the component on the b10-msgq message
+   “address”. It is the address used by the component on the b10-msgq message
    bus. The special components already know their address, but the usual ones
    don't. The address is by convention the thing after b10-, with the first
-   letter capital (eg. b10-stats would have "Stats" as its address).
+   letter capital (eg. b10-stats would have “Stats” as its address).
 
    The last one is process. It is the name of the process to be started. It
    defaults to the name of the component if not set, but you can use this to
@@ -585,7 +583,7 @@ Chapter 3. Starting BIND10 with bind10
 
    This system allows you to start the same component multiple times (by
    including it in the configuration with different names, but the same
-   process setting). However, the rest of the system doesn't expect such
+   process setting). However, the rest of the system doesn't expect such a
    situation, so it would probably not do what you want. Such support is yet
    to be implemented.
 
@@ -595,16 +593,32 @@ Chapter 3. Starting BIND10 with bind10
    mistakes. You could turn off the b10-cmdctl, but then you couldn't change
    it back the usual way, as it would require it to be running (you would
    have to find and edit the configuration directly). Also, some modules
-   might have dependencies -- b10-stats-httpd need b10-stats, b10-xfrout
-   needs the b10-auth to be running, etc.
+   might have dependencies: b10-stats-httpd needs b10-stats, b10-xfrout needs
+   b10-auth to be running, etc.
 
    In short, you should think twice before disabling something here.
 
-Chapter 4. Command channel
+   It is possible to start some components multiple times (currently b10-auth
+   and b10-resolzer). You might want to do that to gain more performance
+   (each one uses only single core). Just put multiple entries under
+   different names, like this, with the same config:
+
+ > config add Boss/components b10-resolver-2
+ > config set Boss/components/b10-resolver-2/special resolver
+ > config set Boss/components/b10-resolver-2/kind needed
+ > config commit
+
+   However, this is work in progress and the support is not yet complete. For
+   example, each resolver will have its own cache, each authoritative server
+   will keep its own copy of in-memory data and there could be problems with
+   locking the sqlite database, if used. The configuration might be changed
+   to something more convenient in future.
+
+Chapter 4. Command channel
 
    The BIND 10 components use the b10-msgq message routing daemon to
    communicate with other BIND 10 components. The b10-msgq implements what is
-   called the "Command Channel". Processes intercommunicate by sending
+   called the “Command Channel”. Processes intercommunicate by sending
    messages on the command channel. Example messages include shutdown, get
    configurations, and set configurations. This Command Channel is not used
    for DNS message passing. It is used only to control and monitor the BIND
@@ -614,7 +628,7 @@ Chapter 4. Command channel
    default, BIND 10 uses port 9912 for the b10-msgq service. It listens on
    127.0.0.1.
 
-Chapter 5. Configuration manager
+Chapter 5. Configuration manager
 
    The configuration manager, b10-cfgmgr, handles all BIND 10 system
    configuration. It provides persistent storage for configuration, and
@@ -626,12 +640,12 @@ Chapter 5. Configuration manager
 
    The administrator doesn't connect to it directly, but uses a user
    interface to communicate with the configuration manager via b10-cmdctl's
-   REST-ful interface. b10-cmdctl is covered in Chapter 6, Remote control
+   REST-ful interface. b10-cmdctl is covered in Chapter 6, Remote control
    daemon.
 
   Note
 
-   The development prototype release only provides the bindctl as a user
+   The development prototype release only provides bindctl as a user
    interface to b10-cmdctl. Upcoming releases will provide another
    interactive command-line interface and a web-based interface.
 
@@ -650,10 +664,10 @@ Chapter 5. Configuration manager
 
    The configuration manager does not have any command line arguments.
    Normally it is not started manually, but is automatically started using
-   the bind10 master process (as covered in Chapter 3, Starting BIND10 with
+   the bind10 master process (as covered in Chapter 3, Starting BIND10 with
    bind10).
 
-Chapter 6. Remote control daemon
+Chapter 6. Remote control daemon
 
    Table of Contents
 
@@ -666,7 +680,7 @@ Chapter 6. Remote control daemon
 
    When b10-cmdctl starts, it firsts asks b10-cfgmgr about what modules are
    running and what their configuration is (over the b10-msgq channel). Then
-   it will start listening on HTTPS for clients -- the user interface -- such
+   it will start listening on HTTPS for clients — the user interface — such
    as bindctl.
 
    b10-cmdctl directly sends commands (received from the user interface) to
@@ -693,7 +707,7 @@ Chapter 6. Remote control daemon
    /usr/local/etc/bind10-devel/cmdctl-accounts.csv. This comma-delimited file
    lists the accounts with a user name, hashed password, and salt. (A sample
    file is at /usr/local/share/bind10-devel/cmdctl-accounts.csv. It contains
-   the user named "root" with the password "bind10".)
+   the user named “root” with the password “bind10”.)
 
    The administrator may create a user account with the b10-cmdctl-usermgr
    tool.
@@ -701,17 +715,17 @@ Chapter 6. Remote control daemon
    By default the HTTPS server listens on the localhost port 8080. The port
    can be set by using the --port command line option. The address to listen
    on can be set using the --address command line argument. Each HTTPS
-   connection is stateless and timesout in 1200 seconds by default. This can
+   connection is stateless and times out in 1200 seconds by default. This can
    be redefined by using the --idle-timeout command line argument.
 
-6.1. Configuration specification for b10-cmdctl
+6.1. Configuration specification for b10-cmdctl
 
    The configuration items for b10-cmdctl are: key_file cert_file
    accounts_file
 
    The control commands are: print_settings shutdown
 
-Chapter 7. Control and configure user interface
+Chapter 7. Control and configure user interface
 
   Note
 
@@ -731,7 +745,7 @@ Chapter 7. Control and configure user interface
    b10-cfgmgr which then stores the details and relays (over a b10-msgq
    command channel) the configuration on to the specified module.
 
-Chapter 8. Authoritative Server
+Chapter 8. Authoritative Server
 
    Table of Contents
 
@@ -739,28 +753,74 @@ Chapter 8. Authoritative Server
 
    8.2. Data Source Backends
 
+                8.2.1. In-memory Data Source
+
    8.3. Loading Master Zones Files
 
    The b10-auth is the authoritative DNS server. It supports EDNS0 and
    DNSSEC. It supports IPv6. Normally it is started by the bind10 master
    process.
 
-8.1. Server Configurations
+8.1. Server Configurations
 
    b10-auth is configured via the b10-cfgmgr configuration manager. The
-   module name is "Auth". The configuration data item is:
+   module name is “Auth”. The configuration data items are:
 
    database_file
            This is an optional string to define the path to find the SQLite3
            database file. Note: Later the DNS server will use various data
            source backends. This may be a temporary setting until then.
 
-   The configuration command is:
+   datasources
+           datasources configures data sources. The list items include: type
+           to define the required data source type (such as “memory”); class
+           to optionally select the class (it defaults to “IN”); and zones to
+           define the file path name and the origin (default domain). By
+           default, this is empty.
+
+  Note
+
+           In this development version, currently this is only used for the
+           memory data source. Only the IN class is supported at this time.
+           By default, the memory data source is disabled. Also, currently
+           the zone file must be canonical such as generated by
+           named-compilezone -D.
+
+   listen_on
+           listen_on is a list of addresses and ports for b10-auth to listen
+           on. The list items are the address string and port number. By
+           default, b10-auth listens on port 53 on the IPv6 (::) and IPv4
+           (0.0.0.0) wildcard addresses.
+
+   statistics-interval
+           statistics-interval is the timer interval in seconds for b10-auth
+           to share its statistics information to b10-stats(8). Statistics
+           updates can be disabled by setting this to 0. The default is 60.
+
+   The configuration commands are:
+
+   loadzone
+           loadzone tells b10-auth to load or reload a zone file. The
+           arguments include: class which optionally defines the class (it
+           defaults to “IN”); origin is the domain name of the zone; and
+           datasrc optionally defines the type of datasource (it defaults to
+           “memory”).
+
+  Note
+
+           In this development version, currently this only supports the IN
+           class and the memory data source.
+
+   sendstats
+           sendstats tells b10-auth to send its statistics data to
+           b10-stats(8) immediately.
 
    shutdown
-           Stop the authoritative DNS server.
+           Stop the authoritative DNS server. This has an optional pid
+           argument to select the process ID to stop. (Note that the BIND 10
+           boss process may restart this service if configured.)
 
-8.2. Data Source Backends
+8.2. Data Source Backends
 
   Note
 
@@ -773,12 +833,48 @@ Chapter 8. Authoritative Server
    /usr/local/var/bind10-devel/zone.sqlite3. (The full path is what was
    defined at build configure time for --localstatedir. The default is
    /usr/local/var/.) This data file location may be changed by defining the
-   "database_file" configuration.
+   “database_file” configuration.
+
+  8.2.1. In-memory Data Source
+
+   The following commands to bindctl provide an example of configuring an
+   in-memory data source containing the “example.com” zone with the zone file
+   named “example.com.zone”:
+
+ > config add Auth/datasources
+ > config set Auth/datasources[0]/type "memory"
+ > config add Auth/datasources[0]/zones
+ > config set Auth/datasources[0]/zones[0]/origin "example.com"
+ > config set Auth/datasources[0]/zones[0]/file "example.com.zone"
+ > config commit
 
-8.3. Loading Master Zones Files
+   The authoritative server will begin serving it immediately after it is
+   loaded.
+
+   Use the Auth loadzone command in bindctl to reload a changed master file
+   into memory; for example:
+
+ > Auth loadzone origin="example.com"
+
+   By default, the memory data source is disabled; it must be configured
+   explicitly. To disable all the in-memory zones, specify a null list for
+   Auth/datasources:
+
+ > config set Auth/datasources/ []
+ > config commit
 
-   RFC 1035 style DNS master zone files may imported into a BIND 10 data
-   source by using the b10-loadzone utility.
+   The following example stops serving a specific zone:
+
+ > config remove Auth/datasources[0]/zones[0]
+ > config commit
+
+   (Replace the list number(s) in datasources[0] and/or zones[0] for the
+   relevant zone as needed.)
+
+8.3. Loading Master Zones Files
+
+   RFC 1035 style DNS master zone files may imported into a BIND 10 SQLite3
+   data source by using the b10-loadzone utility.
 
    b10-loadzone supports the following special directives (control entries):
 
@@ -797,8 +893,8 @@ Chapter 8. Authoritative Server
 
   Note
 
-   In the development prototype release, only the SQLite3 back end is used.
-   By default, it stores the zone data in
+   In the development prototype release, only the SQLite3 back end is used by
+   b10-loadzone. By default, it stores the zone data in
    /usr/local/var/bind10-devel/zone.sqlite3 unless the -d switch is used to
    set the database filename. Multiple zones are stored in a single SQLite3
    zone database.
@@ -806,7 +902,7 @@ Chapter 8. Authoritative Server
    If you reload a zone already existing in the database, all records from
    that prior zone disappear and a whole new set appears.
 
-Chapter 9. Incoming Zone Transfers
+Chapter 9. Incoming Zone Transfers
 
    Table of Contents
 
@@ -822,7 +918,7 @@ Chapter 9. Incoming Zone Transfers
    started by bind10. When received, the zone is stored in the corresponding
    BIND 10 data source, and its records can be served by b10-auth. In
    combination with b10-zonemgr (for automated SOA checks), this allows the
-   BIND 10 server to provide "secondary" service.
+   BIND 10 server to provide “secondary” service.
 
    The b10-xfrin process supports both AXFR and IXFR. Due to some
    implementation limitations of the current development release, however, it
@@ -834,7 +930,7 @@ Chapter 9. Incoming Zone Transfers
    only available for SQLite3-based data sources, that is, they don't work
    for an in-memory data source.
 
-9.1. Configuration for Incoming Zone Transfers
+9.1. Configuration for Incoming Zone Transfers
 
    In practice, you need to specify a list of secondary zones to enable
    incoming zone transfers for these zones (you can still trigger a zone
@@ -851,7 +947,7 @@ Chapter 9. Incoming Zone Transfers
 
    (We assume there has been no zone configuration before).
 
-9.2. Enabling IXFR
+9.2. Enabling IXFR
 
    As noted above, b10-xfrin uses AXFR for zone transfers by default. To
    enable IXFR for zone transfers for a particular zone, set the use_ixfr
@@ -874,7 +970,7 @@ Chapter 9. Incoming Zone Transfers
    be implemented in a near future version, at which point we will enable
    IXFR by default.
 
-9.3. Secondary Manager
+9.3. Secondary Manager
 
    The b10-zonemgr process is started by bind10. It keeps track of SOA
    refresh, retry, and expire timers and other details for BIND 10 to perform
@@ -900,14 +996,14 @@ Chapter 9. Incoming Zone Transfers
    for it), b10-zonemgr will automatically tell b10-xfrin to transfer the
    zone in.
 
-9.4. Trigger an Incoming Zone Transfer Manually
+9.4. Trigger an Incoming Zone Transfer Manually
 
    To manually trigger a zone transfer to retrieve a remote zone, you may use
    the bindctl utility. For example, at the bindctl prompt run:
 
  > Xfrin retransfer zone_name="foo.example.org" master=192.0.2.99
 
-Chapter 10. Outbound Zone Transfers
+Chapter 10. Outbound Zone Transfers
 
    The b10-xfrout process is started by bind10. When the b10-auth
    authoritative DNS server receives an AXFR or IXFR request, b10-auth
@@ -939,28 +1035,24 @@ Chapter 10. Outbound Zone Transfers
    In the above example the lines for transfer_acl were divided for
    readability. In the actual input it must be in a single line.
 
-   If you want to require TSIG in access control, a separate TSIG "key ring"
-   must be configured specifically for b10-xfrout as well as a system wide
-   key ring, both containing a consistent set of keys. For example, to change
-   the previous example to allowing requests from 192.0.2.1 signed by a TSIG
-   with a key name of "key.example", you'll need to do this:
+   If you want to require TSIG in access control, a system wide TSIG "key
+   ring" must be configured. For example, to change the previous example to
+   allowing requests from 192.0.2.1 signed by a TSIG with a key name of
+   "key.example", you'll need to do this:
 
  > config set tsig_keys/keys ["key.example:<base64-key>"]
- > config set Xfrout/tsig_keys/keys ["key.example:<base64-key>"]
  > config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]
  > config commit
 
-   The first line of configuration defines a system wide key ring. This is
-   necessary because the b10-auth server also checks TSIGs and it uses the
-   system wide configuration.
+   Both Xfrout and Auth will use the system wide keyring to check TSIGs in
+   the incoming messages and to sign responses.
 
   Note
 
-   In a future version, b10-xfrout will also use the system wide TSIG
-   configuration. The way to specify zone specific configuration (ACLs, etc)
-   is likely to be changed, too.
+   The way to specify zone specific configuration (ACLs, etc) is likely to be
+   changed.
 
-Chapter 11. Recursive Name Server
+Chapter 11. Recursive Name Server
 
    Table of Contents
 
@@ -971,12 +1063,9 @@ Chapter 11. Recursive Name Server
    The b10-resolver process is started by bind10.
 
    The main bind10 process can be configured to select to run either the
-   authoritative or resolver or both. By default, it starts the authoritative
-   service. You may change this using bindctl, for example:
+   authoritative or resolver or both. By default, it doesn't start either
+   one. You may change this using bindctl, for example:
 
- > config remove Boss/components b10-xfrout
- > config remove Boss/components b10-xfrin
- > config remove Boss/components b10-auth
  > config add Boss/components b10-resolver
  > config set Boss/components/b10-resolver/special resolver
  > config set Boss/components/b10-resolver/kind needed
@@ -994,26 +1083,26 @@ Chapter 11. Recursive Name Server
  > config set Resolver/listen_on[2]/port 53
  > config commit
 
-   (Replace the "2" as needed; run "config show Resolver/listen_on" if
+   (Replace the “2” as needed; run “config show Resolver/listen_on” if
    needed.)
 
-11.1. Access Control
+11.1. Access Control
 
    By default, the b10-resolver daemon only accepts DNS queries from the
    localhost (127.0.0.1 and ::1). The Resolver/query_acl configuration may be
    used to reject, drop, or allow specific IPs or networks. This
    configuration list is first match.
 
-   The configuration's action item may be set to "ACCEPT" to allow the
-   incoming query, "REJECT" to respond with a DNS REFUSED return code, or
-   "DROP" to ignore the query without any response (such as a blackhole). For
+   The configuration's action item may be set to “ACCEPT” to allow the
+   incoming query, “REJECT” to respond with a DNS REFUSED return code, or
+   “DROP” to ignore the query without any response (such as a blackhole). For
    more information, see the respective debugging messages:
    RESOLVER_QUERY_ACCEPTED, RESOLVER_QUERY_REJECTED, and
    RESOLVER_QUERY_DROPPED.
 
    The required configuration's from item is set to an IPv4 or IPv6 address,
    addresses with an network mask, or to the special lowercase keywords
-   "any6" (for any IPv6 address) or "any4" (for any IPv4 address).
+   “any6” (for any IPv6 address) or “any4” (for any IPv4 address).
 
    For example to allow the 192.168.1.0/24 network to use your recursive name
    server, at the bindctl prompt run:
@@ -1023,14 +1112,14 @@ Chapter 11. Recursive Name Server
  > config set Resolver/query_acl[2]/from "192.168.1.0/24"
  > config commit
 
-   (Replace the "2" as needed; run "config show Resolver/query_acl" if
+   (Replace the “2” as needed; run “config show Resolver/query_acl” if
    needed.)
 
   Note
 
    This prototype access control configuration syntax may be changed.
 
-11.2. Forwarding
+11.2. Forwarding
 
    To enable forwarding, the upstream address and port must be configured to
    forward queries to, such as:
@@ -1046,7 +1135,7 @@ Chapter 11. Recursive Name Server
  > config set Resolver/forward_addresses []
  > config commit
 
-Chapter 12. DHCPv4 Server
+Chapter 12. DHCPv4 Server
 
    Table of Contents
 
@@ -1066,7 +1155,7 @@ Chapter 12. DHCPv4 Server
    clients. Even though principles of both DHCPv4 and DHCPv6 are somewhat
    similar, these are two radically different protocols. BIND10 offers server
    implementations for both DHCPv4 and DHCPv6. This chapter is about DHCP for
-   IPv4. For a description of the DHCPv6 server, see Chapter 13, DHCPv6
+   IPv4. For a description of the DHCPv6 server, see Chapter 13, DHCPv6
    Server.
 
    The DHCPv4 server component is currently under intense development. You
@@ -1074,7 +1163,7 @@ Chapter 12. DHCPv4 Server
    developers mailing list.
 
    The DHCPv4 and DHCPv6 components in BIND10 architecture are internally
-   code named "Kea".
+   code named “Kea”.
 
   Note
 
@@ -1082,17 +1171,17 @@ Chapter 12. DHCPv4 Server
    servers. That means that while they are capable of performing DHCP
    configuration, they are not fully functional yet. In particular, neither
    has functional lease databases. This means that they will assign the same,
-   fixed, hardcoded addresses to any client that will ask. See Section 12.4,
-   "DHCPv4 Server Limitations" and Section 13.4, "DHCPv6 Server Limitations"
+   fixed, hardcoded addresses to any client that will ask. See Section 12.4,
+   “DHCPv4 Server Limitations” and Section 13.4, “DHCPv6 Server Limitations”
    for detailed description.
 
-12.1. DHCPv4 Server Usage
+12.1. DHCPv4 Server Usage
 
    BIND10 provides the DHCPv4 server component since December 2011. It is a
    skeleton server and can be described as an early prototype that is not
    fully functional yet. It is mature enough to conduct first tests in lab
-   environment, but it has significant limitations. See Section 12.4, "DHCPv4
-   Server Limitations" for details.
+   environment, but it has significant limitations. See Section 12.4, “DHCPv4
+   Server Limitations” for details.
 
    The DHCPv4 server is implemented as b10-dhcp4 daemon. As it is not
    configurable yet, it is fully autonomous, that is it does not interact
@@ -1118,7 +1207,7 @@ Chapter 12. DHCPv4 Server
    started directly, but rather via bind10. Please be aware of this planned
    change.
 
-12.2. DHCPv4 Server Configuration
+12.2. DHCPv4 Server Configuration
 
    The DHCPv4 server does not have a lease database implemented yet nor any
    support for configuration, so every time the same set of configuration
@@ -1139,60 +1228,60 @@ Chapter 12. DHCPv4 Server
 
    Lease database and configuration support is planned for 2012.
 
-12.3. Supported standards
+12.3. Supported standards
 
    The following standards and draft standards are currently supported:
 
-     o RFC2131: Supported messages are DISCOVER, OFFER, REQUEST, and ACK.
-     o RFC2132: Supported options are: PAD (0), END(255), Message Type(53),
+     o RFC2131: Supported messages are DISCOVER, OFFER, REQUEST, and ACK.
+     o RFC2132: Supported options are: PAD (0), END(255), Message Type(53),
        DHCP Server Identifier (54), Domain Name (15), DNS Servers (6), IP
        Address Lease Time (51), Subnet mask (1), and Routers (3).
 
-12.4. DHCPv4 Server Limitations
+12.4. DHCPv4 Server Limitations
 
    These are the current limitations of the DHCPv4 server software. Most of
    them are reflections of the early stage of development and should be
-   treated as "not implemented yet", rather than actual limitations.
+   treated as “not implemented yet”, rather than actual limitations.
 
-     o During initial IPv4 node configuration, the server is expected to send
+     o During initial IPv4 node configuration, the server is expected to send
        packets to a node that does not have IPv4 address assigned yet. The
        server requires certain tricks (or hacks) to transmit such packets.
        This is not implemented yet, therefore DHCPv4 server supports relayed
        traffic only (that is, normal point to point communication).
-     o b10-dhcp4 provides a single, fixed, hardcoded lease to any client that
+     o b10-dhcp4 provides a single, fixed, hardcoded lease to any client that
        asks. There is no lease manager implemented. If two clients request
        addresses, they will both get the same fixed address.
-     o b10-dhcp4 does not support any configuration mechanisms yet. The whole
+     o b10-dhcp4 does not support any configuration mechanisms yet. The whole
        configuration is currently hardcoded. The only way to tweak
-       configuration is to directly modify source code. See see Section 12.2,
-       "DHCPv4 Server Configuration" for details.
-     o Upon start, the server will open sockets on all interfaces that are
+       configuration is to directly modify source code. See see Section 12.2,
+       “DHCPv4 Server Configuration” for details.
+     o Upon start, the server will open sockets on all interfaces that are
        not loopback, are up and running and have IPv4 address. Support for
        multiple interfaces is not coded in reception routines yet, so if you
        are running this code on a machine that has many interfaces and
        b10-dhcp4 happens to listen on wrong interface, the easiest way to
        work around this problem is to turn down other interfaces. This
        limitation will be fixed shortly.
-     o PRL (Parameter Request List, a list of options requested by a client)
+     o PRL (Parameter Request List, a list of options requested by a client)
        is currently ignored and server assigns DNS SERVER and DOMAIN NAME
        options.
-     o b10-dhcp4 does not support BOOTP. That is a design choice. This
+     o b10-dhcp4 does not support BOOTP. That is a design choice. This
        limitation is permanent. If you have legacy nodes that can't use DHCP
        and require BOOTP support, please use latest version of ISC DHCP
        http://www.isc.org/software/dhcp.
-     o Interface detection is currently working on Linux only. See
-       Section 14.1, "Interface detection" for details.
-     o b10-dhcp4 does not verify that assigned address is unused. According
+     o Interface detection is currently working on Linux only. See
+       Section 14.1, “Interface detection” for details.
+     o b10-dhcp4 does not verify that assigned address is unused. According
        to RFC2131, the allocating server should verify that address is no
        used by sending ICMP echo request.
-     o Address renewal (RENEW), rebinding (REBIND), confirmation (CONFIRM),
+     o Address renewal (RENEW), rebinding (REBIND), confirmation (CONFIRM),
        duplication report (DECLINE) and release (RELEASE) are not supported
        yet.
-     o DNS Update is not supported yet.
-     o -v (verbose) command line option is currently the default, and cannot
+     o DNS Update is not supported yet.
+     o -v (verbose) command line option is currently the default, and cannot
        be disabled.
 
-Chapter 13. DHCPv6 Server
+Chapter 13. DHCPv6 Server
 
    Table of Contents
 
@@ -1207,14 +1296,14 @@ Chapter 13. DHCPv6 Server
    Dynamic Host Configuration Protocol for IPv6 (DHCPv6) is specified in
    RFC3315. BIND10 provides DHCPv6 server implementation that is described in
    this chapter. For a description of the DHCPv4 server implementation, see
-   Chapter 12, DHCPv4 Server.
+   Chapter 12, DHCPv4 Server.
 
    The DHCPv6 server component is currently under intense development. You
    may want to check out BIND10 DHCP (Kea) wiki and recent posts on BIND10
    developers mailing list.
 
    The DHCPv4 and DHCPv6 components in BIND10 architecture are internally
-   code named "Kea".
+   code named “Kea”.
 
   Note
 
@@ -1222,17 +1311,17 @@ Chapter 13. DHCPv6 Server
    servers. That means that while they are capable of performing DHCP
    configuration, they are not fully functional yet. In particular, neither
    has functional lease databases. This means that they will assign the same,
-   fixed, hardcoded addresses to any client that will ask. See Section 12.4,
-   "DHCPv4 Server Limitations" and Section 13.4, "DHCPv6 Server Limitations"
+   fixed, hardcoded addresses to any client that will ask. See Section 12.4,
+   “DHCPv4 Server Limitations” and Section 13.4, “DHCPv6 Server Limitations”
    for detailed description.
 
-13.1. DHCPv6 Server Usage
+13.1. DHCPv6 Server Usage
 
    BIND10 provides the DHCPv6 server component since September 2011. It is a
    skeleton server and can be described as an early prototype that is not
    fully functional yet. It is mature enough to conduct first tests in lab
-   environment, but it has significant limitations. See Section 13.4, "DHCPv6
-   Server Limitations" for details.
+   environment, but it has significant limitations. See Section 13.4, “DHCPv6
+   Server Limitations” for details.
 
    The DHCPv6 server is implemented as b10-dhcp6 daemon. As it is not
    configurable yet, it is fully autonomous, that is it does not interact
@@ -1258,7 +1347,7 @@ Chapter 13. DHCPv6 Server
    started directly, but rather via bind10. Please be aware of this planned
    change.
 
-13.2. DHCPv6 Server Configuration
+13.2. DHCPv6 Server Configuration
 
    The DHCPv6 server does not have lease database implemented yet or any
    support for configuration, so every time the same set of configuration
@@ -1278,50 +1367,50 @@ Chapter 13. DHCPv6 Server
 
    Lease database and configuration support is planned for 2012.
 
-13.3. Supported DHCPv6 Standards
+13.3. Supported DHCPv6 Standards
 
    The following standards and draft standards are currently supported:
 
-     o RFC3315: Supported messages are SOLICIT, ADVERTISE, REQUEST, and
+     o RFC3315: Supported messages are SOLICIT, ADVERTISE, REQUEST, and
        REPLY. Supported options are SERVER_ID, CLIENT_ID, IA_NA, and
        IAADDRESS.
-     o RFC3646: Supported option is DNS_SERVERS.
+     o RFC3646: Supported option is DNS_SERVERS.
 
-13.4. DHCPv6 Server Limitations
+13.4. DHCPv6 Server Limitations
 
    These are the current limitations of the DHCPv6 server software. Most of
    them are reflections of the early stage of development and should be
-   treated as "not implemented yet", rather than actual limitations.
+   treated as “not implemented yet”, rather than actual limitations.
 
-     o Relayed traffic is not supported.
-     o b10-dhcp6 provides a single, fixed, hardcoded lease to any client that
+     o Relayed traffic is not supported.
+     o b10-dhcp6 provides a single, fixed, hardcoded lease to any client that
        asks. There is no lease manager implemented. If two clients request
        addresses, they will both get the same fixed address.
-     o b10-dhcp6 does not support any configuration mechanisms yet. The whole
+     o b10-dhcp6 does not support any configuration mechanisms yet. The whole
        configuration is currently hardcoded. The only way to tweak
-       configuration is to directly modify source code. See see Section 13.2,
-       "DHCPv6 Server Configuration" for details.
-     o Upon start, the server will open sockets on all interfaces that are
+       configuration is to directly modify source code. See see Section 13.2,
+       “DHCPv6 Server Configuration” for details.
+     o Upon start, the server will open sockets on all interfaces that are
        not loopback, are up, running and are multicast capable and have IPv6
        address. Support for multiple interfaces is not coded in reception
        routines yet, so if you are running this code on a machine that has
        many interfaces and b10-dhcp6 happens to listen on wrong interface,
        the easiest way to work around this problem is to turn down other
        interfaces. This limitation will be fixed shortly.
-     o ORO (Option Request Option, a list of options requested by a client)
+     o ORO (Option Request Option, a list of options requested by a client)
        is currently ignored and server assigns DNS SERVER option.
-     o Temporary addresses are not supported yet.
-     o Prefix delegation is not supported yet.
-     o Address renewal (RENEW), rebinding (REBIND), confirmation (CONFIRM),
+     o Temporary addresses are not supported yet.
+     o Prefix delegation is not supported yet.
+     o Address renewal (RENEW), rebinding (REBIND), confirmation (CONFIRM),
        duplication report (DECLINE) and release (RELEASE) are not supported
        yet.
-     o DNS Update is not supported yet.
-     o Interface detection is currently working on Linux only. See
-       Section 14.1, "Interface detection" for details.
-     o -v (verbose) command line option is currently the default, and cannot
+     o DNS Update is not supported yet.
+     o Interface detection is currently working on Linux only. See
+       Section 14.1, “Interface detection” for details.
+     o -v (verbose) command line option is currently the default, and cannot
        be disabled.
 
-Chapter 14. libdhcp++ library
+Chapter 14. libdhcp++ library
 
    Table of Contents
 
@@ -1339,7 +1428,7 @@ Chapter 14. libdhcp++ library
    is designed to be portable, universal library useful for any kind of
    DHCP-related software.
 
-14.1. Interface detection
+14.1. Interface detection
 
    Both DHCPv4 and DHCPv6 components share network interface detection
    routines. Interface detection is currently only supported on Linux
@@ -1361,11 +1450,11 @@ Chapter 14. libdhcp++ library
  # For DHCPv4, please use following format:
  #eth0 192.0.2.5
 
-14.2. DHCPv4/DHCPv6 packet handling
+14.2. DHCPv4/DHCPv6 packet handling
 
    TODO: Describe packet handling here, with pointers to wiki
 
-Chapter 15. Statistics
+Chapter 15. Statistics
 
    The b10-stats process is started by bind10. It periodically collects
    statistics data from various modules and aggregates it.
@@ -1397,7 +1486,7 @@ Chapter 15. Statistics
  }
 
 
-Chapter 16. Logging
+Chapter 16. Logging
 
    Table of Contents
 
@@ -1411,97 +1500,97 @@ Chapter 16. Logging
 
    16.2. Logging Message Format
 
-16.1. Logging configuration
+16.1. Logging configuration
 
    The logging system in BIND 10 is configured through the Logging module.
    All BIND 10 modules will look at the configuration in Logging to see what
    should be logged and to where.
 
-  16.1.1. Loggers
+  16.1.1. Loggers
 
    Within BIND 10, a message is logged through a component called a "logger".
    Different parts of BIND 10 log messages through different loggers, and
    each logger can be configured independently of one another.
 
    In the Logging module, you can specify the configuration for zero or more
-   loggers; any that are not specified will take appropriate default values..
+   loggers; any that are not specified will take appropriate default values.
 
    The three most important elements of a logger configuration are the name
    (the component that is generating the messages), the severity (what to
    log), and the output_options (where to log).
 
-    16.1.1.1. name (string)
+    16.1.1.1. name (string)
 
    Each logger in the system has a name, the name being that of the component
    using it to log messages. For instance, if you want to configure logging
-   for the resolver module, you add an entry for a logger named "Resolver".
+   for the resolver module, you add an entry for a logger named “Resolver”.
    This configuration will then be used by the loggers in the Resolver
    module, and all the libraries used by it.
 
    If you want to specify logging for one specific library within the module,
    you set the name to module.library. For example, the logger used by the
-   nameserver address store component has the full name of "Resolver.nsas".
+   nameserver address store component has the full name of “Resolver.nsas”.
    If there is no entry in Logging for a particular library, it will use the
    configuration given for the module.
 
    To illustrate this, suppose you want the cache library to log messages of
    severity DEBUG, and the rest of the resolver code to log messages of
    severity INFO. To achieve this you specify two loggers, one with the name
-   "Resolver" and severity INFO, and one with the name "Resolver.cache" with
+   “Resolver” and severity INFO, and one with the name “Resolver.cache” with
    severity DEBUG. As there are no entries for other libraries (e.g. the
-   nsas), they will use the configuration for the module ("Resolver"), so
+   nsas), they will use the configuration for the module (“Resolver”), so
    giving the desired behavior.
 
-   One special case is that of a module name of "*" (asterisks), which is
+   One special case is that of a module name of “*” (asterisks), which is
    interpreted as any module. You can set global logging options by using
    this, including setting the logging configuration for a library that is
-   used by multiple modules (e.g. "*.config" specifies the configuration
+   used by multiple modules (e.g. “*.config” specifies the configuration
    library code in whatever module is using it).
 
    If there are multiple logger specifications in the configuration that
    might match a particular logger, the specification with the more specific
    logger name takes precedence. For example, if there are entries for for
-   both "*" and "Resolver", the resolver module -- and all libraries it uses
-   -- will log messages according to the configuration in the second entry
-   ("Resolver"). All other modules will use the configuration of the first
-   entry ("*"). If there was also a configuration entry for "Resolver.cache",
+   both “*” and “Resolver”, the resolver module — and all libraries it uses —
+   will log messages according to the configuration in the second entry
+   (“Resolver”). All other modules will use the configuration of the first
+   entry (“*”). If there was also a configuration entry for “Resolver.cache”,
    the cache library within the resolver would use that in preference to the
-   entry for "Resolver".
+   entry for “Resolver”.
 
    One final note about the naming. When specifying the module name within a
    logger, use the name of the module as specified in bindctl, e.g.
-   "Resolver" for the resolver module, "Xfrout" for the xfrout module, etc.
+   “Resolver” for the resolver module, “Xfrout” for the xfrout module, etc.
    When the message is logged, the message will include the name of the
    logger generating the message, but with the module name replaced by the
    name of the process implementing the module (so for example, a message
-   generated by the "Auth.cache" logger will appear in the output with a
-   logger name of "b10-auth.cache").
+   generated by the “Auth.cache” logger will appear in the output with a
+   logger name of “b10-auth.cache”).
 
-    16.1.1.2. severity (string)
+    16.1.1.2. severity (string)
 
    This specifies the category of messages logged. Each message is logged
    with an associated severity which may be one of the following (in
    descending order of severity):
 
-     o FATAL
-     o ERROR
-     o WARN
-     o INFO
-     o DEBUG
+     o FATAL
+     o ERROR
+     o WARN
+     o INFO
+     o DEBUG
 
    When the severity of a logger is set to one of these values, it will only
    log messages of that severity, and the severities above it. The severity
    may also be set to NONE, in which case all messages from that logger are
    inhibited.
 
-    16.1.1.3. output_options (list)
+    16.1.1.3. output_options (list)
 
    Each logger can have zero or more output_options. These specify where log
    messages are sent to. These are explained in detail below.
 
    The other options for a logger are:
 
-    16.1.1.4. debuglevel (integer)
+    16.1.1.4. debuglevel (integer)
 
    When a logger's severity is set to DEBUG, this value specifies what debug
    messages should be printed. It ranges from 0 (least verbose) to 99 (most
@@ -1509,69 +1598,80 @@ Chapter 16. Logging
 
    If severity for the logger is not DEBUG, this value is ignored.
 
-    16.1.1.5. additive (true or false)
+    16.1.1.5. additive (true or false)
 
    If this is true, the output_options from the parent will be used. For
-   example, if there are two loggers configured; "Resolver" and
-   "Resolver.cache", and additive is true in the second, it will write the
-   log messages not only to the destinations specified for "Resolver.cache",
+   example, if there are two loggers configured; “Resolver” and
+   “Resolver.cache”, and additive is true in the second, it will write the
+   log messages not only to the destinations specified for “Resolver.cache”,
    but also to the destinations as specified in the output_options in the
-   logger named "Resolver".
+   logger named “Resolver”.
 
-  16.1.2. Output Options
+  16.1.2. Output Options
 
    The main settings for an output option are the destination and a value
    called output, the meaning of which depends on the destination that is
    set.
 
-    16.1.2.1. destination (string)
+    16.1.2.1. destination (string)
 
    The destination is the type of output. It can be one of:
 
-     o console
-     o file
-     o syslog
+     o console
+     o file
+     o syslog
 
-    16.1.2.2. output (string)
+    16.1.2.2. output (string)
 
    Depending on what is set as the output destination, this value is
    interpreted as follows:
 
-   destination is "console"
-           The value of output must be one of "stdout" (messages printed to
-           standard output) or "stderr" (messages printed to standard error).
+   destination is “console”
+
+           The value of output must be one of “stdout” (messages printed to
+           standard output) or “stderr” (messages printed to standard error).
+
+           Note: if output is set to “stderr” and a lot of messages are
+           produced in a short time (e.g. if the logging level is set to
+           DEBUG), you may occasionally see some messages jumbled up
+           together. This is due to a combination of the way that messages
+           are written to the screen and the unbuffered nature of the
+           standard error stream. If this occurs, it is recommended that
+           output be set to “stdout”.
+
+   destination is “file”
 
-   destination is "file"
            The value of output is interpreted as a file name; log messages
            will be appended to this file.
 
-   destination is "syslog"
+   destination is “syslog”
+
            The value of output is interpreted as the syslog facility (e.g.
            local0) that should be used for log messages.
 
    The other options for output_options are:
 
-      16.1.2.2.1. flush (true of false)
+      16.1.2.2.1. flush (true of false)
 
    Flush buffers after each log message. Doing this will reduce performance
    but will ensure that if the program terminates abnormally, all messages up
    to the point of termination are output.
 
-      16.1.2.2.2. maxsize (integer)
+      16.1.2.2.2. maxsize (integer)
 
    Only relevant when destination is file, this is maximum file size of
    output files in bytes. When the maximum size is reached, the file is
    renamed and a new file opened. (For example, a ".1" is appended to the
-   name -- if a ".1" file exists, it is renamed ".2", etc.)
+   name — if a ".1" file exists, it is renamed ".2", etc.)
 
    If this is 0, no maximum file size is used.
 
-      16.1.2.2.3. maxver (integer)
+      16.1.2.2.3. maxver (integer)
 
    Maximum number of old log files to keep around when rolling the output
-   file. Only relevant when destination is "file".
+   file. Only relevant when destination is “file”.
 
-  16.1.3. Example session
+  16.1.3. Example session
 
    In this example we want to set the global logging to write to the file
    /var/log/my_bind10.log, at severity WARN. We want the authoritative server
@@ -1630,7 +1730,7 @@ Chapter 16. Logging
 
  >  config set Logging/loggers[0]/output_options[0]/destination file
  >  config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log
- >  config set Logging/loggers[0]/output_options[0]/maxsize 30000
+ >  config set Logging/loggers[0]/output_options[0]/maxsize 204800
  >  config set Logging/loggers[0]/output_options[0]/maxver 8
 
    Which would make the entire configuration for this logger look like:
@@ -1643,7 +1743,7 @@ Chapter 16. Logging
  Logging/loggers[0]/output_options[0]/destination        "file"  string  (modified)
  Logging/loggers[0]/output_options[0]/output     "/var/log/bind10.log"   string  (modified)
  Logging/loggers[0]/output_options[0]/flush      false   boolean (default)
- Logging/loggers[0]/output_options[0]/maxsize    30000   integer (modified)
+ Logging/loggers[0]/output_options[0]/maxsize    204800  integer (modified)
  Logging/loggers[0]/output_options[0]/maxver     8       integer (modified)
 
    That looks OK, so let's commit it before we add the configuration for the
@@ -1670,9 +1770,9 @@ Chapter 16. Logging
  >  config remove Logging/loggers[1]
  >  config commit
 
-   And every module will now be using the values from the logger named "*".
+   And every module will now be using the values from the logger named “*”.
 
-16.2. Logging Message Format
+16.2. Logging Message Format
 
    Each message written by BIND 10 to the configured logging destinations
    comprises a number of components that identify the origin of the message
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index eafbbd8..630c0c4 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -172,15 +172,6 @@
 
           <listitem>
             <simpara>
-              <command>b10-msgq</command> —
-              Message bus daemon.
-              This process coordinates communication between all of the other
-              BIND 10 processes.
-            </simpara>
-          </listitem>
-
-          <listitem>
-            <simpara>
               <command>b10-auth</command> —
               Authoritative DNS server.
               This process serves DNS requests.
@@ -205,6 +196,15 @@
 
           <listitem>
             <simpara>
+              <command>b10-msgq</command> —
+              Message bus daemon.
+              This process coordinates communication between all of the other
+              BIND 10 processes.
+            </simpara>
+          </listitem>
+
+          <listitem>
+            <simpara>
               <command>b10-resolver</command> —
               Recursive name server.
               This process handles incoming queries.
@@ -214,6 +214,15 @@
 
           <listitem>
             <simpara>
+              <command>b10-sockcreator</command> —
+              Socket creator daemon.
+              This process creates sockets used by
+              network-listening BIND 10 processes.
+            </simpara>
+          </listitem>
+
+          <listitem>
+            <simpara>
               <command>b10-stats</command> —
               Statistics collection daemon.
               This process collects and reports statistics data.
@@ -222,6 +231,14 @@
 
           <listitem>
             <simpara>
+              <command>b10-stats-httpd</command> —
+              HTTP server for statistics reporting.
+              This process reports statistics data in XML format over HTTP.
+            </simpara>
+          </listitem>
+
+          <listitem>
+            <simpara>
               <command>b10-xfrin</command> —
               Incoming zone transfer service.
               This process is used to transfer a new copy
@@ -269,8 +286,9 @@
             <simpara>
               <command>bindctl</command> —
               interactive administration interface.
-              This is a command-line tool which allows an administrator
-              to control BIND 10.
+              This is a low-level command-line tool which allows
+              a developer or an experienced administrator to control
+              BIND 10.
             </simpara>
           </listitem>
           <listitem>
@@ -751,12 +769,9 @@ as a dependency earlier -->
     <para>
       In its default configuration, the <command>bind10</command>
       master process will also start up
-      <command>b10-cmdctl</command> for admins to communicate with the
-      system, <command>b10-auth</command> for authoritative DNS service,
-      <command>b10-stats</command> for statistics collection,
-      <command>b10-xfrin</command> for inbound DNS zone transfers,
-      <command>b10-xfrout</command> for outbound DNS zone transfers,
-      and <command>b10-zonemgr</command> for secondary service.
+      <command>b10-cmdctl</command> for administration tools to
+      communicate with the system, and
+      <command>b10-stats</command> for statistics collection.
     </para>
 
     <section id="start">
@@ -790,12 +805,7 @@ as a dependency earlier -->
         The configuration is in the Boss/components section. Each element
         represents one component, which is an abstraction of a process
         (currently there's also one component which doesn't represent
-        a process). If you didn't want to transfer out at all (your server
-        is a slave only), you would just remove the corresponding component
-        from the set, like this and the process would be stopped immediately
-        (and not started on the next startup):
-      <screen>> <userinput>config remove Boss/components b10-xfrout</userinput>
-> <userinput>config commit</userinput></screen>
+        a process).
       </para>
 
       <para>
@@ -889,7 +899,7 @@ address, but the usual ones don't." mean? -->
           This system allows you to start the same component multiple times
           (by including it in the configuration with different names, but the
           same process setting). However, the rest of the system doesn't expect
-          such situation, so it would probably not do what you want. Such
+          such a situation, so it would probably not do what you want. Such
           support is yet to be implemented.
         </para>
       </note>
@@ -901,10 +911,10 @@ address, but the usual ones don't." mean? -->
           <command>b10-cmdctl</command>, but then you couldn't
           change it back the usual way, as it would require it to
           be running (you would have to find and edit the configuration
-          directly).  Also, some modules might have dependencies
-          -- <command>b10-stats-httpd</command> need
+          directly).  Also, some modules might have dependencies:
+          <command>b10-stats-httpd</command> needs
           <command>b10-stats</command>, <command>b10-xfrout</command>
-          needs the <command>b10-auth</command> to be running, etc.
+          needs <command>b10-auth</command> to be running, etc.
 
 <!-- TODO: should we define dependencies? -->
 
@@ -999,7 +1009,7 @@ Unix domain sockets
 <!-- TODO -->
       <note>
         <para>
-          The development prototype release only provides the
+          The development prototype release only provides
           <command>bindctl</command> as a user interface to
           <command>b10-cmdctl</command>.
           Upcoming releases will provide another interactive command-line
@@ -1190,7 +1200,7 @@ or accounts database -->
       The port can be set by using the <option>--port</option> command line option.
       The address to listen on can be set using the <option>--address</option> command
       line argument.
-      Each HTTPS connection is stateless and timesout in 1200 seconds
+      Each HTTPS connection is stateless and times out in 1200 seconds
       by default.  This can be
       redefined by using the <option>--idle-timeout</option> command line argument.
     </para>
@@ -1281,7 +1291,7 @@ since we used bind10 -->
         <command>b10-auth</command> is configured via the
         <command>b10-cfgmgr</command> configuration manager.
         The module name is <quote>Auth</quote>.
-        The configuration data item is:
+        The configuration data items are:
 
         <variablelist>
 
@@ -1297,22 +1307,119 @@ This may be a temporary setting until then.
             </listitem>
           </varlistentry>
 
+<!-- NOTE: docs pulled in verbatim from the b10-auth.xml manual page.
+     TODO: automate this if want this or rewrite
+-->
+          <varlistentry>
+            <term>datasources</term>
+            <listitem>
+              <simpara>
+      <varname>datasources</varname> configures data sources.
+      The list items include:
+      <varname>type</varname> to define the required data source type
+      (such as <quote>memory</quote>);
+      <varname>class</varname> to optionally select the class
+      (it defaults to <quote>IN</quote>);
+      and
+      <varname>zones</varname> to define the
+      <varname>file</varname> path name and the
+      <varname>origin</varname> (default domain).
+
+      By default, this is empty.
+
+      <note><simpara>
+        In this development version, currently this is only used for the
+        memory data source.
+        Only the IN class is supported at this time.
+        By default, the memory data source is disabled.
+        Also, currently the zone file must be canonical such as
+        generated by <command>named-compilezone -D</command>.
+      </simpara></note>
+
+              </simpara>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>listen_on</term>
+            <listitem>
+              <simpara>
+      <varname>listen_on</varname> is a list of addresses and ports for
+      <command>b10-auth</command> to listen on.
+      The list items are the <varname>address</varname> string
+      and <varname>port</varname> number.
+      By default, <command>b10-auth</command> listens on port 53
+      on the IPv6 (::) and IPv4 (0.0.0.0) wildcard addresses.
+              </simpara>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>statistics-interval</term>
+            <listitem>
+              <simpara>
+      <varname>statistics-interval</varname> is the timer interval
+      in seconds for <command>b10-auth</command> to share its
+      statistics information to
+      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+      Statistics updates can be disabled by setting this to 0.
+      The default is 60.
+              </simpara>
+            </listitem>
+          </varlistentry>
+
         </variablelist>
 
       </para>
 
       <para>
 
-        The configuration command is:
+        The configuration commands are:
 
         <variablelist>
 
           <varlistentry>
+            <term>loadzone</term>
+            <listitem>
+              <simpara>
+      <command>loadzone</command> tells <command>b10-auth</command>
+      to load or reload a zone file. The arguments include:
+      <varname>class</varname> which optionally defines the class
+      (it defaults to <quote>IN</quote>);
+      <varname>origin</varname> is the domain name of the zone;
+      and
+      <varname>datasrc</varname> optionally defines the type of datasource
+      (it defaults to <quote>memory</quote>).
+
+      <note><simpara>
+        In this development version, currently this only supports the
+        IN class and the memory data source.
+      </simpara></note>
+              </simpara>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>sendstats</term>
+            <listitem>
+              <simpara>
+      <command>sendstats</command> tells <command>b10-auth</command>
+      to send its statistics data to
+      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      immediately.
+              </simpara>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
             <term>shutdown</term>
             <listitem>
               <simpara>Stop the authoritative DNS server.
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
               </simpara>
-<!-- TODO: what happens when this is sent, will bind10 restart? -->
             </listitem>
           </varlistentry>
 
@@ -1342,10 +1449,79 @@ This may be a temporary setting until then.
         (The full path is what was defined at build configure time for
         <option>--localstatedir</option>.
         The default is <filename>/usr/local/var/</filename>.)
-  This data file location may be changed by defining the
-  <quote>database_file</quote> configuration.
+	This data file location may be changed by defining the
+	<quote>database_file</quote> configuration.
       </para>
 
+      <section id="in-memory-datasource">
+	<title>In-memory Data Source</title>
+
+	<para>
+<!--	  How to configure it. -->
+	  The following commands to <command>bindctl</command>
+	  provide an example of configuring an in-memory data
+	  source containing the <quote>example.com</quote> zone
+	  with the zone file named <quote>example.com.zone</quote>:
+
+<!--
+	  <screen>> <userinput> config set Auth/datasources/ [{"type": "memory", "zones": [{"origin": "example.com", "file": "example.com.zone"}]}]</userinput></screen>
+-->
+
+          <screen>> <userinput>config add Auth/datasources</userinput>
+> <userinput>config set Auth/datasources[0]/type "<option>memory</option>"</userinput>
+> <userinput>config add Auth/datasources[0]/zones</userinput>
+> <userinput>config set Auth/datasources[0]/zones[0]/origin "<option>example.com</option>"</userinput>
+> <userinput>config set Auth/datasources[0]/zones[0]/file "<option>example.com.zone</option>"</userinput>
+> <userinput>config commit</userinput></screen>
+
+	  The authoritative server will begin serving it immediately
+	  after it is loaded.
+	</para>
+
+	<para>
+	  Use the <command>Auth loadzone</command> command in
+	  <command>bindctl</command> to reload a changed master
+	  file into memory; for example:
+
+	  <screen>> <userinput>Auth loadzone origin="example.com"</userinput>
+</screen>
+
+	</para>
+
+<!--
+        <para>
+          The <varname>file</varname> may be an absolute path to the
+          master zone file or it is relative to the directory BIND 10 is
+          started from.
+	</para>
+-->
+
+        <para>
+	By default, the memory data source is disabled; it must be
+	configured explicitly.  To disable all the in-memory zones,
+	specify a null list for <varname>Auth/datasources</varname>:
+
+<!-- TODO: this assumes that Auth/datasources is for memory only -->
+
+	  <screen>> <userinput>config set Auth/datasources/ []</userinput>
+> <userinput>config commit</userinput></screen>
+	</para>
+
+	<para>
+          The following example stops serving a specific zone:
+
+	  <screen>> <userinput>config remove Auth/datasources[<option>0</option>]/zones[<option>0</option>]</userinput>
+> <userinput>config commit</userinput></screen>
+
+	  (Replace the list number(s) in
+	  <varname>datasources[<replaceable>0</replaceable>]</varname>
+	  and/or <varname>zones[<replaceable>0</replaceable>]</varname>
+	  for the relevant zone as needed.)
+
+	</para>
+
+      </section>
+
     </section>
 
     <section>
@@ -1353,7 +1529,7 @@ This may be a temporary setting until then.
 
       <para>
         RFC 1035 style DNS master zone files may imported
-        into a BIND 10 data source by using the
+        into a BIND 10 SQLite3 data source by using the
         <command>b10-loadzone</command> utility.
       </para>
 
@@ -1400,7 +1576,7 @@ This may be a temporary setting until then.
       <note>
       <para>
         In the development prototype release, only the SQLite3 back
-        end is used.
+        end is used by <command>b10-loadzone</command>.
         By default, it stores the zone data in
         <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>
         unless the <option>-d</option> switch is used to set the
@@ -1629,31 +1805,23 @@ Xfrout/transfer_acl[0]	{"action": "ACCEPT"}	any	(default)</screen>
     </simpara></note>
 
     <para>
-      If you want to require TSIG in access control, a separate TSIG
-      "key ring" must be configured specifically
-      for <command>b10-xfrout</command> as well as a system wide
-      key ring, both containing a consistent set of keys.
+      If you want to require TSIG in access control, a system wide TSIG
+      "key ring" must be configured.
       For example, to change the previous example to allowing requests
       from 192.0.2.1 signed by a TSIG with a key name of
       "key.example", you'll need to do this:
     </para>
 
     <screen>> <userinput>config set tsig_keys/keys ["key.example:<base64-key>"]</userinput>
-> <userinput>config set Xfrout/tsig_keys/keys ["key.example:<base64-key>"]</userinput>
 > <userinput>config set Xfrout/zone_config[0]/transfer_acl [{"action": "ACCEPT", "from": "192.0.2.1", "key": "key.example"}]</userinput>
 > <userinput>config commit</userinput></screen>
 
-    <para>
-      The first line of configuration defines a system wide key ring.
-      This is necessary because the <command>b10-auth</command> server
-      also checks TSIGs and it uses the system wide configuration.
-    </para>
+    <para>Both Xfrout and Auth will use the system wide keyring to check
+    TSIGs in the incoming messages and to sign responses.</para>
 
     <note><simpara>
-        In a future version, <command>b10-xfrout</command> will also
-        use the system wide TSIG configuration.
         The way to specify zone specific configuration (ACLs, etc) is
-        likely to be changed, too.
+        likely to be changed.
     </simpara></note>
 
 <!--
@@ -1683,15 +1851,10 @@ what is XfroutClient xfr_client??
     <para>
       The main <command>bind10</command> process can be configured
       to select to run either the authoritative or resolver or both.
-      By default, it starts the authoritative service.
-<!-- TODO: later both -->
-
-      You may change this using <command>bindctl</command>, for example:
+      By default, it doesn't start either one. You may change this using
+      <command>bindctl</command>, for example:
 
       <screen>
-> <userinput>config remove Boss/components b10-xfrout</userinput>
-> <userinput>config remove Boss/components b10-xfrin</userinput>
-> <userinput>config remove Boss/components b10-auth</userinput>
 > <userinput>config add Boss/components b10-resolver</userinput>
 > <userinput>config set Boss/components/b10-resolver/special resolver</userinput>
 > <userinput>config set Boss/components/b10-resolver/kind needed</userinput>
@@ -2359,7 +2522,7 @@ eth0 fe80::21e:8cff:fe9b:7349
 
           In the Logging module, you can specify the configuration
           for zero or more loggers; any that are not specified will
-          take appropriate default values..
+          take appropriate default values.
 
         </para>
 
@@ -2645,34 +2808,43 @@ TODO; there's a ticket to determine these levels, see #1074
           <varlistentry>
             <term><option>destination</option> is <quote>console</quote></term>
             <listitem>
-              <simpara>
+              <para>
                  The value of output must be one of <quote>stdout</quote>
                  (messages printed to standard output) or
                  <quote>stderr</quote> (messages printed to standard
                  error).
-              </simpara>
+              </para>
+              <para>
+                Note: if output is set to <quote>stderr</quote> and a lot of
+                messages are produced in a short time (e.g. if the logging
+                level is set to DEBUG), you may occasionally see some messages
+                jumbled up together.  This is due to a combination of the way
+                that messages are written to the screen and the unbuffered
+                nature of the standard error stream.  If this occurs, it is
+                recommended that output be set to <quote>stdout</quote>.
+              </para>
             </listitem>
           </varlistentry>
 
           <varlistentry>
             <term><option>destination</option> is <quote>file</quote></term>
             <listitem>
-              <simpara>
+              <para>
                 The value of output is interpreted as a file name;
                 log messages will be appended to this file.
-              </simpara>
+              </para>
             </listitem>
           </varlistentry>
 
           <varlistentry>
             <term><option>destination</option> is <quote>syslog</quote></term>
             <listitem>
-              <simpara>
+              <para>
                 The value of output is interpreted as the
                 <command>syslog</command> facility (e.g.
                 <emphasis>local0</emphasis>) that should be used
                 for log messages.
-              </simpara>
+              </para>
             </listitem>
           </varlistentry>
 
@@ -2853,7 +3025,7 @@ Logging/loggers[0]/output_options[0]/maxver	0	integer	(default)
 
           <screen>> <userinput> config set Logging/loggers[0]/output_options[0]/destination file</userinput>
 > <userinput> config set Logging/loggers[0]/output_options[0]/output /var/log/bind10.log</userinput>
-> <userinput> config set Logging/loggers[0]/output_options[0]/maxsize 30000</userinput>
+> <userinput> config set Logging/loggers[0]/output_options[0]/maxsize 204800</userinput>
 > <userinput> config set Logging/loggers[0]/output_options[0]/maxver 8</userinput>
 </screen>
 
@@ -2876,7 +3048,7 @@ Logging/loggers[0]/additive	false	boolean	(default)
 Logging/loggers[0]/output_options[0]/destination	"file"	string	(modified)
 Logging/loggers[0]/output_options[0]/output	"/var/log/bind10.log"	string	(modified)
 Logging/loggers[0]/output_options[0]/flush	false	boolean	(default)
-Logging/loggers[0]/output_options[0]/maxsize	30000	integer	(modified)
+Logging/loggers[0]/output_options[0]/maxsize	204800	integer	(modified)
 Logging/loggers[0]/output_options[0]/maxver	8	integer	(modified)
 </screen>
 
diff --git a/doc/guide/bind10-messages.html b/doc/guide/bind10-messages.html
index b82b485..d3bcb7c 100644
--- a/doc/guide/bind10-messages.html
+++ b/doc/guide/bind10-messages.html
@@ -1,10 +1,10 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Messages Manual</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the messages manual for BIND 10 version 20111129. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Messages Manual"><div class="titlepage"><div><div><h1 class="title"><a name="id1168229451102"></a>BIND 10 Messages Manual</h1></div><div><p class="releaseinfo">This is the messages manual for BIND 10 version
-        20111129.</p></div><div><p class="copyright">Copyright © 2011-2012 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BIND 10 Messages Manual</title><link rel="stylesheet" href="./bind10-guide.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.75.2"><meta name="description" content="BIND 10 is a Domain Name System (DNS) suite managed by Internet Systems Consortium (ISC). It includes DNS libraries and modular components for controlling authoritative and recursive DNS servers. This is the messages manual for BIND 10 version 20120127. The most up-to-date version of this document, along with other documents for BIND 10, can be found at ."></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" title="BIND 10 Messages Manual"><div class="titlepage"><div><div><h1 class="title"><a name="id1168229451102"></a>BIND 10 Messages Manual</h1></div><div><p class="releaseinfo">This is the messages manual for BIND 10 version
+        20120127.</p></div><div><p class="copyright">Copyright © 2011-2012 Internet Systems Consortium, Inc.</p></div><div><div class="abstract" title="Abstract"><p class="title"><b>Abstract</b></p><p>BIND 10 is a Domain Name System (DNS) suite managed by
 	  Internet Systems Consortium (ISC). It includes DNS libraries
 	  and modular components for controlling authoritative and
 	  recursive DNS servers.
       </p><p>
-        This is the messages manual for BIND 10 version 20111129.
+        This is the messages manual for BIND 10 version 20120127.
 	    The most up-to-date version of this document, along with
 	    other documents for BIND 10, can be found at
         <a class="ulink" href="http://bind10.isc.org/docs" target="_top">http://bind10.isc.org/docs</a>.
@@ -215,6 +215,9 @@ reason for the failure is included in the message.
 </p></dd><dt><a name="AUTH_SERVER_STARTED"></a><span class="term">AUTH_SERVER_STARTED server started</span></dt><dd><p>
 Initialization of the authoritative server has completed successfully
 and it is entering the main loop, waiting for queries to arrive.
+</p></dd><dt><a name="AUTH_SHUTDOWN"></a><span class="term">AUTH_SHUTDOWN asked to stop, doing so</span></dt><dd><p>
+This is a debug message indicating the server was asked to shut down and it is
+complying to the request.
 </p></dd><dt><a name="AUTH_SQLITE3"></a><span class="term">AUTH_SQLITE3 nothing to do for loading sqlite3</span></dt><dd><p>
 This is a debug message indicating that the authoritative server has
 found that the data source it is loading is an SQLite3 data source,
@@ -754,6 +757,16 @@ but will not send back an answer.
 </p><p>
 The most likely cause of this error is a programming error.  Please raise
 a bug report.
+</p></dd><dt><a name="CONFIG_CCSESSION_STOPPING"></a><span class="term">CONFIG_CCSESSION_STOPPING error sending stopping message: %1</span></dt><dd><p>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</p></dd><dt><a name="CONFIG_CCSESSION_STOPPING_UNKNOWN"></a><span class="term">CONFIG_CCSESSION_STOPPING_UNKNOWN unknown error sending stopping message</span></dt><dd><p>
+Similar to CONFIG_CCSESSION_STOPPING, but in this case the exception that
+is seen is not a standard exception, and further information is unknown.
+This is a bug.
 </p></dd><dt><a name="CONFIG_GET_FAIL"></a><span class="term">CONFIG_GET_FAIL error getting configuration from cfgmgr: %1</span></dt><dd><p>
 The configuration manager returned an error when this module requested
 the configuration. The full error message answer from the configuration
@@ -806,6 +819,18 @@ manager.
 </p></dd><dt><a name="CONFIG_OPEN_FAIL"></a><span class="term">CONFIG_OPEN_FAIL error opening %1: %2</span></dt><dd><p>
 There was an error opening the given file. The reason for the failure
 is included in the message.
+</p></dd><dt><a name="CONFIG_SESSION_STOPPING_FAILED"></a><span class="term">CONFIG_SESSION_STOPPING_FAILED error sending stopping message: %1</span></dt><dd><p>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</p></dd><dt><a name="DATASRC_BAD_NSEC3_NAME"></a><span class="term">DATASRC_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'</span></dt><dd><p>
+The software refuses to load NSEC3 records into a wildcard domain or
+the owner name has two or more labels below the zone origin.
+It isn't explicitly forbidden, but no sane zone wouldn have such names
+for NSEC3.  BIND 9 also refuses NSEC3 at wildcard, so this behavior is
+compatible with BIND 9.
 </p></dd><dt><a name="DATASRC_CACHE_CREATE"></a><span class="term">DATASRC_CACHE_CREATE creating the hotspot cache</span></dt><dd><p>
 This is a debug message issued during startup when the hotspot cache
 is created.
@@ -935,24 +960,13 @@ name/type/class in the data source.
 Debug information.  A set of updates to a zone has been successfully
 committed to the corresponding database backend.  The zone name,
 its class and the database name are printed.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_COMMIT%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_COMMIT (1) updates committed for '%1/%2' on %3</span></dt><dd><p>
-Debug information.  A set of updates to a zone has been successfully
-committed to the corresponding database backend.  The zone name,
-its class and the database name are printed.
 </p></dd><dt><a name="DATASRC_DATABASE_UPDATER_CREATED"></a><span class="term">DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3</span></dt><dd><p>
 Debug information.  A zone updater object is created to make updates to
 the shown zone on the shown backend database.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_CREATED%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_CREATED (1) zone updater created for '%1/%2' on %3</span></dt><dd><p>
-Debug information.  A zone updater object is created to make updates to
-the shown zone on the shown backend database.
 </p></dd><dt><a name="DATASRC_DATABASE_UPDATER_DESTROYED"></a><span class="term">DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3</span></dt><dd><p>
 Debug information.  A zone updater object is destroyed, either successfully
 or after failure of, making updates to the shown zone on the shown backend
 database.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_DESTROYED%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_DESTROYED (1) zone updater destroyed for '%1/%2' on %3</span></dt><dd><p>
-Debug information.  A zone updater object is destroyed, either successfully
-or after failure of, making updates to the shown zone on the shown backend
-database.
 </p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACK"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3</span></dt><dd><p>
 A zone updater is being destroyed without committing the changes.
 This would typically mean the update attempt was aborted due to some
@@ -960,13 +974,6 @@ error, but may also be a bug of the application that forgets committing
 the changes.  The intermediate changes made through the updater won't
 be applied to the underlying database.  The zone name, its class, and
 the underlying database name are shown in the log message.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACK%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACK (1) zone updates roll-backed for '%1/%2' on %3</span></dt><dd><p>
-A zone updater is being destroyed without committing the changes.
-This would typically mean the update attempt was aborted due to some
-error, but may also be a bug of the application that forgets committing
-the changes.  The intermediate changes made through the updater won't
-be applied to the underlying database.  The zone name, its class, and
-the underlying database name are shown in the log message.
 </p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4</span></dt><dd><p>
 A zone updater is being destroyed without committing the changes to
 the database, and attempts to rollback incomplete updates, but it
@@ -979,18 +986,6 @@ examine the underlying data source to see what exactly happens and
 whether the data is still valid.  The zone name, its class, and the
 underlying database name as well as the error message thrown from the
 database module are shown in the log message.
-</p></dd><dt><a name="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL%20(1)"></a><span class="term">DATASRC_DATABASE_UPDATER_ROLLBACKFAIL (1) failed to roll back zone updates for '%1/%2' on %3: %4</span></dt><dd><p>
-A zone updater is being destroyed without committing the changes to
-the database, and attempts to rollback incomplete updates, but it
-unexpectedly fails.  The higher level implementation does not expect
-it to fail, so this means either a serious operational error in the
-underlying data source (such as a system failure of a database) or
-software bug in the underlying data source implementation.  In either
-case if this message is logged the administrator should carefully
-examine the underlying data source to see what exactly happens and
-whether the data is still valid.  The zone name, its class, and the
-underlying database name as well as the error message thrown from the
-database module are shown in the log message.
 </p></dd><dt><a name="DATASRC_DATABASE_WILDCARD_ANY"></a><span class="term">DATASRC_DATABASE_WILDCARD_ANY search in datasource %1 resulted in wildcard match type ANY on %2</span></dt><dd><p>
 The database doesn't contain directly matching name.  When searching
 for a wildcard match, a wildcard record matching the name of the query
@@ -1080,6 +1075,26 @@ this zone is not authoritative for the requested domain, but a delegation
 should be followed. The requested domain is an apex of some zone.
 </p></dd><dt><a name="DATASRC_MEM_FIND"></a><span class="term">DATASRC_MEM_FIND find '%1/%2'</span></dt><dd><p>
 Debug information. A search for the requested RRset is being started.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3"></a><span class="term">DATASRC_MEM_FINDNSEC3 finding NSEC3 for %1, mode %2</span></dt><dd><p>
+Debug information. A search in an in-memory data source for NSEC3 that
+matches or covers the given name is being started.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3_COVER"></a><span class="term">DATASRC_MEM_FINDNSEC3_COVER found a covering NSEC3 for %1: %2</span></dt><dd><p>
+Debug information. An NSEC3 that covers the given name is found and
+being returned.  The found NSEC3 RRset is also displayed.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3_MATCH"></a><span class="term">DATASRC_MEM_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3</span></dt><dd><p>
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned.  When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_MEM_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+</p></dd><dt><a name="DATASRC_MEM_FINDNSEC3_TRYHASH"></a><span class="term">DATASRC_MEM_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)</span></dt><dd><p>
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space.  When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried).
 </p></dd><dt><a name="DATASRC_MEM_FIND_ZONE"></a><span class="term">DATASRC_MEM_FIND_ZONE looking for zone '%1'</span></dt><dd><p>
 Debug information. A zone object for this zone is being searched for in the
 in-memory data source.
@@ -1087,6 +1102,13 @@ in-memory data source.
 Debug information. The content of master file is being loaded into the memory.
 </p></dd><dt><a name="DATASRC_MEM_NOT_FOUND"></a><span class="term">DATASRC_MEM_NOT_FOUND requested domain '%1' not found</span></dt><dd><p>
 Debug information. The requested domain does not exist.
+</p></dd><dt><a name="DATASRC_MEM_NO_NSEC3PARAM"></a><span class="term">DATASRC_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2</span></dt><dd><p>
+The in-memory data source has loaded a zone signed with NSEC3 RRs,
+but it doesn't have a NSEC3PARAM RR at the zone origin.  It's likely that
+the zone is somehow broken, but this RR is not necessarily needed for
+handling lookups with NSEC3 in this data source, so it accepts the given
+content of the zone.  Nevertheless the administrator should look into
+the integrity of the zone data.
 </p></dd><dt><a name="DATASRC_MEM_NS_ENCOUNTERED"></a><span class="term">DATASRC_MEM_NS_ENCOUNTERED encountered a NS</span></dt><dd><p>
 Debug information. While searching for the requested domain, a NS was
 encountered on the way (a delegation). This may lead to stop of the search.
@@ -1107,10 +1129,12 @@ Some resource types are singletons -- only one is allowed in a domain
 (for example CNAME or SOA). This indicates a problem with provided data.
 </p></dd><dt><a name="DATASRC_MEM_SUCCESS"></a><span class="term">DATASRC_MEM_SUCCESS query for '%1/%2' successful</span></dt><dd><p>
 Debug information. The requested record was found.
-</p></dd><dt><a name="DATASRC_MEM_SUPER_STOP"></a><span class="term">DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty</span></dt><dd><p>
-Debug information. The search stopped at a superdomain of the requested
-domain. The domain is an empty nonterminal, therefore it is treated  as NXRRSET
-case (eg. the domain exists, but it doesn't have the requested record type).
+</p></dd><dt><a name="DATASRC_MEM_SUPER_STOP"></a><span class="term">DATASRC_MEM_SUPER_STOP stopped as '%1' is superdomain of a zone node, meaning it's empty</span></dt><dd><p>
+Debug information. The search stopped because the requested domain was
+detected to be a superdomain of some existing node of zone (while there
+was no exact match).  This means that the domain is an empty nonterminal,
+therefore it is treated  as NXRRSET case (eg. the domain exists, but it
+doesn't have the requested record type).
 </p></dd><dt><a name="DATASRC_MEM_SWAP"></a><span class="term">DATASRC_MEM_SWAP swapping contents of two zone representations ('%1' and '%2')</span></dt><dd><p>
 Debug information. The contents of two in-memory zones are being exchanged.
 This is usual practice to do some manipulation in exception-safe manner -- the
@@ -1247,7 +1271,7 @@ to prove the nonexistence.
 The underlying data source failed to answer the query for referral information.
 1 means some error, 2 is not implemented. The data source should have logged
 the specific error already.
-</p></dd><dt><a name="DATASRC_QUERY_RRSIG"></a><span class="term">DATASRC_QUERY_RRSIG unable to answer RRSIG query</span></dt><dd><p>
+</p></dd><dt><a name="DATASRC_QUERY_RRSIG"></a><span class="term">DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1</span></dt><dd><p>
 The server is unable to answer a direct query for RRSIG type, but was asked
 to do so.
 </p></dd><dt><a name="DATASRC_QUERY_SIMPLE_FAIL"></a><span class="term">DATASRC_QUERY_SIMPLE_FAIL the underlying data source failed with %1</span></dt><dd><p>
@@ -1365,6 +1389,11 @@ data source.
 </p></dd><dt><a name="DATASRC_UNEXPECTED_QUERY_STATE"></a><span class="term">DATASRC_UNEXPECTED_QUERY_STATE unexpected query state</span></dt><dd><p>
 This indicates a programming error. An internal task of unknown type was
 generated.
+</p></dd><dt><a name="DDNS_ACCEPT_FAILURE"></a><span class="term">DDNS_ACCEPT_FAILURE error accepting a connection: %1</span></dt><dd><p>
+There was a low-level error when we tried to accept an incoming connection
+(probably coming from b10-auth). We continue serving on whatever other
+connections we already have, but this connection is dropped. The reason
+is logged.
 </p></dd><dt><a name="DDNS_CC_SESSION_ERROR"></a><span class="term">DDNS_CC_SESSION_ERROR error reading from cc channel: %1</span></dt><dd><p>
 There was a problem reading from the command and control channel. The
 most likely cause is that the msgq process is not running.
@@ -1375,18 +1404,33 @@ configuration manager b10-cfgmgr is not running.
 </p></dd><dt><a name="DDNS_CONFIG_ERROR"></a><span class="term">DDNS_CONFIG_ERROR error found in configuration data: %1</span></dt><dd><p>
 The ddns process encountered an error when installing the configuration at
 startup time.  Details of the error are included in the log message.
+</p></dd><dt><a name="DDNS_DROP_CONN"></a><span class="term">DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</span></dt><dd><p>
+There was an error on a connection with the b10-auth server (or whatever
+connects to the ddns daemon). This might be OK, for example when the
+authoritative server shuts down, the connection would get closed. It also
+can mean the system is busy and can't keep up or that the other side got
+confused and sent bad data.
 </p></dd><dt><a name="DDNS_MODULECC_SESSION_ERROR"></a><span class="term">DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</span></dt><dd><p>
 There was a problem in the lower level module handling configuration and
 control commands.  This could happen for various reasons, but the most likely
 cause is that the configuration database contains a syntax error and ddns
 failed to start at initialization.  A detailed error message from the module
 will also be displayed.
+</p></dd><dt><a name="DDNS_NEW_CONN"></a><span class="term">DDNS_NEW_CONN new connection on file descriptor %1 from %2</span></dt><dd><p>
+Debug message. We received a connection and we are going to start handling
+requests from it. The file descriptor number and the address where the request
+comes from is logged. The connection is over a unix domain socket and is likely
+coming from a b10-auth process.
 </p></dd><dt><a name="DDNS_RECEIVED_SHUTDOWN_COMMAND"></a><span class="term">DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</span></dt><dd><p>
 The ddns process received a shutdown command from the command channel
 and will now shut down.
 </p></dd><dt><a name="DDNS_RUNNING"></a><span class="term">DDNS_RUNNING ddns server is running and listening for updates</span></dt><dd><p>
 The ddns process has successfully started and is now ready to receive commands
 and updates.
+</p></dd><dt><a name="DDNS_SESSION"></a><span class="term">DDNS_SESSION session arrived on file descriptor %1</span></dt><dd><p>
+A debug message, informing there's some activity on the given file descriptor.
+It will be either a request or the file descriptor will be closed. See
+following log messages to see what of it.
 </p></dd><dt><a name="DDNS_SHUTDOWN"></a><span class="term">DDNS_SHUTDOWN ddns server shutting down</span></dt><dd><p>
 The ddns process is shutting down. It will no longer listen for new commands
 or updates. Any command or update that is being addressed at this moment will
@@ -1659,6 +1703,17 @@ an answer with a different given type and class.
 </p><p>
 This message indicates an internal error in the NSAS.  Please raise a
 bug report.
+</p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_DEINIT"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</span></dt><dd><p>
+A debug message noting that the global TSIG keyring is being removed from
+memory. Most programs don't do that, they just exit, which is OK.
+</p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_INIT"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_INIT Initializing global TSIG keyring</span></dt><dd><p>
+A debug message noting the TSIG keyring storage is being prepared. It should
+appear at most once in the lifetime of a program. The keyring still needs
+to be loaded from configuration.
+</p></dd><dt><a name="PYSERVER_COMMON_TSIG_KEYRING_UPDATE"></a><span class="term">PYSERVER_COMMON_TSIG_KEYRING_UPDATE Updating global TSIG keyring</span></dt><dd><p>
+A debug message. The TSIG keyring is being (re)loaded from configuration.
+This happens at startup or when the configuration changes. The old keyring
+is removed and new one created with all the keys.
 </p></dd><dt><a name="RESLIB_ANSWER"></a><span class="term">RESLIB_ANSWER answer received in response to query for <%1></span></dt><dd><p>
 A debug message reporting that an answer has been received to an upstream
 query for the specified question.  Previous debug messages will have
@@ -2009,6 +2064,9 @@ resolver.  It is output during startup and may appear multiple times,
 once for each root server address.
 </p></dd><dt><a name="RESOLVER_SHUTDOWN"></a><span class="term">RESOLVER_SHUTDOWN resolver shutdown complete</span></dt><dd><p>
 This informational message is output when the resolver has shut down.
+</p></dd><dt><a name="RESOLVER_SHUTDOWN%20(1)"></a><span class="term">RESOLVER_SHUTDOWN (1) asked to shut down, doing so</span></dt><dd><p>
+A debug message noting that the server was asked to terminate and is
+complying to the request.
 </p></dd><dt><a name="RESOLVER_STARTED"></a><span class="term">RESOLVER_STARTED resolver started</span></dt><dd><p>
 This informational message is output by the resolver when all initialization
 has been completed and it is entering its main loop.
@@ -2022,7 +2080,7 @@ has been ignored.
 This is debug message output when the resolver received a message with an
 unsupported opcode (it can only process QUERY opcodes).  It will return
 a message to the sender with the RCODE set to NOTIMP.
-</p></dd><dt><a name="SOCKETREQUESTOR_CREATED"></a><span class="term">SOCKETREQUESTOR_CREATED Socket requestor created</span></dt><dd><p>
+</p></dd><dt><a name="SOCKETREQUESTOR_CREATED"></a><span class="term">SOCKETREQUESTOR_CREATED Socket requestor created for application %1</span></dt><dd><p>
 Debug message.  A socket requesor (client of the socket creator) is created
 for the corresponding application.  Normally this should happen at most
 one time throughout the lifetime of the application.
@@ -2074,6 +2132,16 @@ Debug message. This lists one address and port value of the set of
 addresses we are going to listen on (eg. there will be one log message
 per pair). This appears only after SRVCOMM_SET_LISTEN, but might
 be hidden, as it has higher debug level.
+</p></dd><dt><a name="SRVCOMM_EXCEPTION_ALLOC"></a><span class="term">SRVCOMM_EXCEPTION_ALLOC exception when allocating a socket: %1</span></dt><dd><p>
+The process tried to allocate a socket using the socket creator, but an error
+occurred. But it is not one of the errors we are sure are "safe". In this case
+it is unclear if the unsuccessful communication left the process and the bind10
+process in inconsistent state, so the process is going to abort to prevent
+further problems in that area.
+</p><p>
+This is probably a bug in the code, but it could be caused by other unusual
+conditions (like insufficient memory, deleted socket file used for
+communication).
 </p></dd><dt><a name="SRVCOMM_KEYS_DEINIT"></a><span class="term">SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring</span></dt><dd><p>
 Debug message indicating that the server is deinitializing the TSIG keyring.
 </p></dd><dt><a name="SRVCOMM_KEYS_INIT"></a><span class="term">SRVCOMM_KEYS_INIT initializing TSIG keyring</span></dt><dd><p>
@@ -2088,6 +2156,10 @@ specification is outside the valid range of 0 to 65535.
 </p></dd><dt><a name="SRVCOMM_SET_LISTEN"></a><span class="term">SRVCOMM_SET_LISTEN setting addresses to listen to</span></dt><dd><p>
 Debug message, noting that the server is about to start listening on a
 different set of IP addresses and ports than before.
+</p></dd><dt><a name="SRVCOMM_UNKNOWN_EXCEPTION_ALLOC"></a><span class="term">SRVCOMM_UNKNOWN_EXCEPTION_ALLOC unknown exception when allocating a socket</span></dt><dd><p>
+The situation is the same as in the SRVCOMM_EXCEPTION_ALLOC case, but further
+details about the error are unknown, because it was signaled by throwing
+something not being an exception. This is definitely a bug.
 </p></dd><dt><a name="STATHTTPD_BAD_OPTION_VALUE"></a><span class="term">STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1</span></dt><dd><p>
 The stats-httpd module was called with a bad command-line argument
 and will not start.
diff --git a/doc/guide/bind10-messages.xml b/doc/guide/bind10-messages.xml
index c085cb1..60f9665 100644
--- a/doc/guide/bind10-messages.xml
+++ b/doc/guide/bind10-messages.xml
@@ -405,6 +405,27 @@ message associated with it has its own code.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="AUTH_RESPONSE_FAILURE">
+<term>AUTH_RESPONSE_FAILURE exception while building response to query: %1</term>
+<listitem><para>
+This is a debug message, generated by the authoritative server when an
+attempt to create a response to a received DNS packet has failed. The
+reason for the failure is given in the log message. A SERVFAIL response
+is sent back. The most likely cause of this is an error in the data
+source implementation; it is either creating bad responses or raising
+exceptions itself.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="AUTH_RESPONSE_FAILURE_UNKNOWN">
+<term>AUTH_RESPONSE_FAILURE_UNKNOWN unknown exception while building response to query</term>
+<listitem><para>
+This debug message is similar to AUTH_RESPONSE_FAILURE, but further
+details about the error are unknown, because it was signaled by something
+which is not an exception. This is definitely a bug.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="AUTH_RESPONSE_RECEIVED">
 <term>AUTH_RESPONSE_RECEIVED received response message, ignoring</term>
 <listitem><para>
@@ -467,6 +488,14 @@ and it is entering the main loop, waiting for queries to arrive.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="AUTH_SHUTDOWN">
+<term>AUTH_SHUTDOWN asked to stop, doing so</term>
+<listitem><para>
+This is a debug message indicating the server was asked to shut down and it is
+complying to the request.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="AUTH_SQLITE3">
 <term>AUTH_SQLITE3 nothing to do for loading sqlite3</term>
 <listitem><para>
@@ -590,7 +619,7 @@ needs a dedicated message bus.
 </varlistentry>
 
 <varlistentry id="BIND10_COMPONENT_FAILED">
-<term>BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status</term>
+<term>BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3</term>
 <listitem><para>
 The process terminated, but the bind10 boss didn't expect it to, which means
 it must have failed.
@@ -1610,6 +1639,15 @@ configuration is not stored.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="CFGMGR_RENAMED_CONFIG_FILE">
+<term>CFGMGR_RENAMED_CONFIG_FILE renamed configuration file %1 to %2, will create new %1</term>
+<listitem><para>
+BIND 10 has been started with the command to clear the configuration file.
+The existing file is backed up to the given file name, so that data is not
+immediately lost if this was done by accident.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="CFGMGR_STOPPED_BY_KEYBOARD">
 <term>CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down</term>
 <listitem><para>
@@ -1766,6 +1804,26 @@ a bug report.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="CONFIG_CCSESSION_STOPPING">
+<term>CONFIG_CCSESSION_STOPPING error sending stopping message: %1</term>
+<listitem><para>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="CONFIG_CCSESSION_STOPPING_UNKNOWN">
+<term>CONFIG_CCSESSION_STOPPING_UNKNOWN unknown error sending stopping message</term>
+<listitem><para>
+Similar to CONFIG_CCSESSION_STOPPING, but in this case the exception that
+is seen is not a standard exception, and further information is unknown.
+This is a bug.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="CONFIG_GET_FAIL">
 <term>CONFIG_GET_FAIL error getting configuration from cfgmgr: %1</term>
 <listitem><para>
@@ -1873,6 +1931,28 @@ is included in the message.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="CONFIG_SESSION_STOPPING_FAILED">
+<term>CONFIG_SESSION_STOPPING_FAILED error sending stopping message: %1</term>
+<listitem><para>
+There was a problem when sending a message signaling that the module using
+this CCSession is stopping. This message is sent so that the rest of the
+system is aware that the module is no longer running. Apart from logging
+this message, the error itself is ignored, and the ModuleCCSession is
+still stopped. The specific exception message is printed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_BAD_NSEC3_NAME">
+<term>DATASRC_BAD_NSEC3_NAME NSEC3 record has a bad owner name '%1'</term>
+<listitem><para>
+The software refuses to load NSEC3 records into a wildcard domain or
+the owner name has two or more labels below the zone origin.
+It isn't explicitly forbidden, but no sane zone wouldn have such names
+for NSEC3.  BIND 9 also refuses NSEC3 at wildcard, so this behavior is
+compatible with BIND 9.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DATASRC_CACHE_CREATE">
 <term>DATASRC_CACHE_CREATE creating the hotspot cache</term>
 <listitem><para>
@@ -2177,15 +2257,6 @@ its class and the database name are printed.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="DATASRC_DATABASE_UPDATER_COMMIT (1)">
-<term>DATASRC_DATABASE_UPDATER_COMMIT (1) updates committed for '%1/%2' on %3</term>
-<listitem><para>
-Debug information.  A set of updates to a zone has been successfully
-committed to the corresponding database backend.  The zone name,
-its class and the database name are printed.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="DATASRC_DATABASE_UPDATER_CREATED">
 <term>DATASRC_DATABASE_UPDATER_CREATED zone updater created for '%1/%2' on %3</term>
 <listitem><para>
@@ -2194,14 +2265,6 @@ the shown zone on the shown backend database.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="DATASRC_DATABASE_UPDATER_CREATED (1)">
-<term>DATASRC_DATABASE_UPDATER_CREATED (1) zone updater created for '%1/%2' on %3</term>
-<listitem><para>
-Debug information.  A zone updater object is created to make updates to
-the shown zone on the shown backend database.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="DATASRC_DATABASE_UPDATER_DESTROYED">
 <term>DATASRC_DATABASE_UPDATER_DESTROYED zone updater destroyed for '%1/%2' on %3</term>
 <listitem><para>
@@ -2211,15 +2274,6 @@ database.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="DATASRC_DATABASE_UPDATER_DESTROYED (1)">
-<term>DATASRC_DATABASE_UPDATER_DESTROYED (1) zone updater destroyed for '%1/%2' on %3</term>
-<listitem><para>
-Debug information.  A zone updater object is destroyed, either successfully
-or after failure of, making updates to the shown zone on the shown backend
-database.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACK">
 <term>DATASRC_DATABASE_UPDATER_ROLLBACK zone updates roll-backed for '%1/%2' on %3</term>
 <listitem><para>
@@ -2232,18 +2286,6 @@ the underlying database name are shown in the log message.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACK (1)">
-<term>DATASRC_DATABASE_UPDATER_ROLLBACK (1) zone updates roll-backed for '%1/%2' on %3</term>
-<listitem><para>
-A zone updater is being destroyed without committing the changes.
-This would typically mean the update attempt was aborted due to some
-error, but may also be a bug of the application that forgets committing
-the changes.  The intermediate changes made through the updater won't
-be applied to the underlying database.  The zone name, its class, and
-the underlying database name are shown in the log message.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL">
 <term>DATASRC_DATABASE_UPDATER_ROLLBACKFAIL failed to roll back zone updates for '%1/%2' on %3: %4</term>
 <listitem><para>
@@ -2261,23 +2303,6 @@ database module are shown in the log message.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="DATASRC_DATABASE_UPDATER_ROLLBACKFAIL (1)">
-<term>DATASRC_DATABASE_UPDATER_ROLLBACKFAIL (1) failed to roll back zone updates for '%1/%2' on %3: %4</term>
-<listitem><para>
-A zone updater is being destroyed without committing the changes to
-the database, and attempts to rollback incomplete updates, but it
-unexpectedly fails.  The higher level implementation does not expect
-it to fail, so this means either a serious operational error in the
-underlying data source (such as a system failure of a database) or
-software bug in the underlying data source implementation.  In either
-case if this message is logged the administrator should carefully
-examine the underlying data source to see what exactly happens and
-whether the data is still valid.  The zone name, its class, and the
-underlying database name as well as the error message thrown from the
-database module are shown in the log message.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="DATASRC_DATABASE_WILDCARD_ANY">
 <term>DATASRC_DATABASE_WILDCARD_ANY search in datasource %1 resulted in wildcard match type ANY on %2</term>
 <listitem><para>
@@ -2497,6 +2522,46 @@ Debug information. A search for the requested RRset is being started.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DATASRC_MEM_FINDNSEC3">
+<term>DATASRC_MEM_FINDNSEC3 finding NSEC3 for %1, mode %2</term>
+<listitem><para>
+Debug information. A search in an in-memory data source for NSEC3 that
+matches or covers the given name is being started.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FINDNSEC3_COVER">
+<term>DATASRC_MEM_FINDNSEC3_COVER found a covering NSEC3 for %1: %2</term>
+<listitem><para>
+Debug information. An NSEC3 that covers the given name is found and
+being returned.  The found NSEC3 RRset is also displayed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FINDNSEC3_MATCH">
+<term>DATASRC_MEM_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3</term>
+<listitem><para>
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned.  When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_MEM_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DATASRC_MEM_FINDNSEC3_TRYHASH">
+<term>DATASRC_MEM_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)</term>
+<listitem><para>
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space.  When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried).
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DATASRC_MEM_FIND_ZONE">
 <term>DATASRC_MEM_FIND_ZONE looking for zone '%1'</term>
 <listitem><para>
@@ -2519,6 +2584,18 @@ Debug information. The requested domain does not exist.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DATASRC_MEM_NO_NSEC3PARAM">
+<term>DATASRC_MEM_NO_NSEC3PARAM NSEC3PARAM is missing for NSEC3-signed zone %1/%2</term>
+<listitem><para>
+The in-memory data source has loaded a zone signed with NSEC3 RRs,
+but it doesn't have a NSEC3PARAM RR at the zone origin.  It's likely that
+the zone is somehow broken, but this RR is not necessarily needed for
+handling lookups with NSEC3 in this data source, so it accepts the given
+content of the zone.  Nevertheless the administrator should look into
+the integrity of the zone data.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DATASRC_MEM_NS_ENCOUNTERED">
 <term>DATASRC_MEM_NS_ENCOUNTERED encountered a NS</term>
 <listitem><para>
@@ -2570,11 +2647,13 @@ Debug information. The requested record was found.
 </varlistentry>
 
 <varlistentry id="DATASRC_MEM_SUPER_STOP">
-<term>DATASRC_MEM_SUPER_STOP stopped at superdomain '%1', domain '%2' is empty</term>
+<term>DATASRC_MEM_SUPER_STOP stopped as '%1' is superdomain of a zone node, meaning it's empty</term>
 <listitem><para>
-Debug information. The search stopped at a superdomain of the requested
-domain. The domain is an empty nonterminal, therefore it is treated  as NXRRSET
-case (eg. the domain exists, but it doesn't have the requested record type).
+Debug information. The search stopped because the requested domain was
+detected to be a superdomain of some existing node of zone (while there
+was no exact match).  This means that the domain is an empty nonterminal,
+therefore it is treated  as NXRRSET case (eg. the domain exists, but it
+doesn't have the requested record type).
 </para></listitem>
 </varlistentry>
 
@@ -2894,8 +2973,10 @@ not have any DS record. This indicates problem with the provided data.
 <varlistentry id="DATASRC_QUERY_NO_ZONE">
 <term>DATASRC_QUERY_NO_ZONE no zone containing '%1' in class '%2'</term>
 <listitem><para>
-Lookup of domain failed because the data have no zone that contain the
-domain. Maybe someone sent a query to the wrong server for some reason.
+Debug information. Lookup of domain failed because the datasource
+has no zone that contains the domain. Maybe someone sent a query
+to the wrong server for some reason. This may also happen when
+looking in the datasource for addresses for NS records.
 </para></listitem>
 </varlistentry>
 
@@ -2925,7 +3006,7 @@ the specific error already.
 </varlistentry>
 
 <varlistentry id="DATASRC_QUERY_RRSIG">
-<term>DATASRC_QUERY_RRSIG unable to answer RRSIG query</term>
+<term>DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1</term>
 <listitem><para>
 The server is unable to answer a direct query for RRSIG type, but was asked
 to do so.
@@ -3232,6 +3313,210 @@ generated.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DBUTIL_BACKUP">
+<term>DBUTIL_BACKUP created backup of %1 in %2</term>
+<listitem><para>
+A backup for the given database file was created. Same of original file and
+backup are given in the output message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_CHECK_ERROR">
+<term>DBUTIL_CHECK_ERROR unable to check database version: %1</term>
+<listitem><para>
+There was an error while trying to check the current version of the database
+schema. The error is shown in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_CHECK_NOCONFIRM">
+<term>DBUTIL_CHECK_NOCONFIRM --noconfirm is not compatible with --check</term>
+<listitem><para>
+b10-dbutil was called with --check and --noconfirm. --noconfirm only has
+meaning with --upgrade, so this is considered an error.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_CHECK_OK">
+<term>DBUTIL_CHECK_OK this is the latest version of the database schema. No upgrade is required</term>
+<listitem><para>
+The database schema version has been checked, and is up to date.
+No action is required.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_CHECK_UPGRADE_NEEDED">
+<term>DBUTIL_CHECK_UPGRADE_NEEDED re-run this program with the --upgrade switch to upgrade</term>
+<listitem><para>
+The database schema version is not up to date, and an update is required.
+Please run the dbutil tool again, with the --upgrade argument.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_COMMAND_NONE">
+<term>DBUTIL_COMMAND_NONE must select one of --check or --upgrade</term>
+<listitem><para>
+b10-dbutil was called with neither --check nor --upgrade. One action must be
+provided.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_COMMAND_UPGRADE_CHECK">
+<term>DBUTIL_COMMAND_UPGRADE_CHECK --upgrade is not compatible with --check</term>
+<listitem><para>
+b10-dbutil was called with both the commands --upgrade and --check. Only one
+action can be performed at a time.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_DATABASE_MAY_BE_CORRUPT">
+<term>DBUTIL_DATABASE_MAY_BE_CORRUPT database file %1 may be corrupt, restore it from backup (%2)</term>
+<listitem><para>
+The upgrade failed while it was in progress; the database may now be in an
+inconsistent state, and it is advised to restore it from the backup that was
+created when b10-dbutil started.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_EXECUTE">
+<term>DBUTIL_EXECUTE Executing SQL statement: %1</term>
+<listitem><para>
+Debug message; the given SQL statement is executed
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_FILE">
+<term>DBUTIL_FILE Database file: %1</term>
+<listitem><para>
+The database file that is being checked.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_NO_FILE">
+<term>DBUTIL_NO_FILE must supply name of the database file to upgrade</term>
+<listitem><para>
+b10-dbutil was called without a database file. Currently, it cannot find this
+file on its own, and it must be provided.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_STATEMENT_ERROR">
+<term>DBUTIL_STATEMENT_ERROR failed to execute %1: %2</term>
+<listitem><para>
+The given database statement failed to execute. The error is shown in the
+message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_TOO_MANY_ARGUMENTS">
+<term>DBUTIL_TOO_MANY_ARGUMENTS too many arguments to the command, maximum of one expected</term>
+<listitem><para>
+There were too many command-line arguments to b10-dbutil
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_CANCELED">
+<term>DBUTIL_UPGRADE_CANCELED upgrade canceled; database has not been changed</term>
+<listitem><para>
+The user aborted the upgrade, and b10-dbutil will now exit.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_DBUTIL">
+<term>DBUTIL_UPGRADE_DBUTIL please get the latest version of b10-dbutil and re-run</term>
+<listitem><para>
+A database schema was found that was newer than this version of dbutil, which
+is apparently out of date and should be upgraded itself.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_FAILED">
+<term>DBUTIL_UPGRADE_FAILED upgrade failed: %1</term>
+<listitem><para>
+While the upgrade was in progress, an unexpected error occurred. The error
+is shown in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_NOT_ATTEMPTED">
+<term>DBUTIL_UPGRADE_NOT_ATTEMPTED database upgrade was not attempted</term>
+<listitem><para>
+Due to the earlier failure, the database schema upgrade was not attempted,
+and b10-dbutil will now exit.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_NOT_NEEDED">
+<term>DBUTIL_UPGRADE_NOT_NEEDED database already at latest version, no upgrade necessary</term>
+<listitem><para>
+b10-dbutil was told to upgrade the database schema, but it is already at the
+latest version.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_NOT_POSSIBLE">
+<term>DBUTIL_UPGRADE_NOT_POSSIBLE database at a later version than this utility can support</term>
+<listitem><para>
+b10-dbutil was told to upgrade the database schema, but it is at a higher
+version than this tool currently supports. Please update b10-dbutil and try
+again.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_PREPARATION_FAILED">
+<term>DBUTIL_UPGRADE_PREPARATION_FAILED upgrade preparation failed: %1</term>
+<listitem><para>
+An unexpected error occurred while b10-dbutil was preparing to upgrade the
+database schema. The error is shown in the message
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADE_SUCCESFUL">
+<term>DBUTIL_UPGRADE_SUCCESFUL database upgrade successfully completed</term>
+<listitem><para>
+The database schema update was completed successfully.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_UPGRADING">
+<term>DBUTIL_UPGRADING upgrading database from %1 to %2</term>
+<listitem><para>
+An upgrade is in progress, the versions of the current upgrade action are shown.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_VERSION_CURRENT">
+<term>DBUTIL_VERSION_CURRENT database version %1</term>
+<listitem><para>
+The current version of the database schema.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_VERSION_HIGH">
+<term>DBUTIL_VERSION_HIGH database is at a later version (%1) than this program can cope with (%2)</term>
+<listitem><para>
+The database schema is at a higher version than b10-dbutil knows about.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DBUTIL_VERSION_LOW">
+<term>DBUTIL_VERSION_LOW database version %1, latest version is %2.</term>
+<listitem><para>
+The database schema is not up to date, the current version and the latest
+version are in the message.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="DDNS_ACCEPT_FAILURE">
+<term>DDNS_ACCEPT_FAILURE error accepting a connection: %1</term>
+<listitem><para>
+There was a low-level error when we tried to accept an incoming connection
+(probably coming from b10-auth). We continue serving on whatever other
+connections we already have, but this connection is dropped. The reason
+is logged.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_CC_SESSION_ERROR">
 <term>DDNS_CC_SESSION_ERROR error reading from cc channel: %1</term>
 <listitem><para>
@@ -3257,6 +3542,17 @@ startup time.  Details of the error are included in the log message.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_DROP_CONN">
+<term>DDNS_DROP_CONN dropping connection on file descriptor %1 because of error %2</term>
+<listitem><para>
+There was an error on a connection with the b10-auth server (or whatever
+connects to the ddns daemon). This might be OK, for example when the
+authoritative server shuts down, the connection would get closed. It also
+can mean the system is busy and can't keep up or that the other side got
+confused and sent bad data.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_MODULECC_SESSION_ERROR">
 <term>DDNS_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1</term>
 <listitem><para>
@@ -3268,6 +3564,16 @@ will also be displayed.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_NEW_CONN">
+<term>DDNS_NEW_CONN new connection on file descriptor %1 from %2</term>
+<listitem><para>
+Debug message. We received a connection and we are going to start handling
+requests from it. The file descriptor number and the address where the request
+comes from is logged. The connection is over a unix domain socket and is likely
+coming from a b10-auth process.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_RECEIVED_SHUTDOWN_COMMAND">
 <term>DDNS_RECEIVED_SHUTDOWN_COMMAND shutdown command received</term>
 <listitem><para>
@@ -3284,6 +3590,15 @@ and updates.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="DDNS_SESSION">
+<term>DDNS_SESSION session arrived on file descriptor %1</term>
+<listitem><para>
+A debug message, informing there's some activity on the given file descriptor.
+It will be either a request or the file descriptor will be closed. See
+following log messages to see what of it.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="DDNS_SHUTDOWN">
 <term>DDNS_SHUTDOWN ddns server shutting down</term>
 <listitem><para>
@@ -3826,6 +4141,32 @@ bug report.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_DEINIT">
+<term>PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring</term>
+<listitem><para>
+A debug message noting that the global TSIG keyring is being removed from
+memory. Most programs don't do that, they just exit, which is OK.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_INIT">
+<term>PYSERVER_COMMON_TSIG_KEYRING_INIT Initializing global TSIG keyring</term>
+<listitem><para>
+A debug message noting the TSIG keyring storage is being prepared. It should
+appear at most once in the lifetime of a program. The keyring still needs
+to be loaded from configuration.
+</para></listitem>
+</varlistentry>
+
+<varlistentry id="PYSERVER_COMMON_TSIG_KEYRING_UPDATE">
+<term>PYSERVER_COMMON_TSIG_KEYRING_UPDATE Updating global TSIG keyring</term>
+<listitem><para>
+A debug message. The TSIG keyring is being (re)loaded from configuration.
+This happens at startup or when the configuration changes. The old keyring
+is removed and new one created with all the keys.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="RESLIB_ANSWER">
 <term>RESLIB_ANSWER answer received in response to query for <%1></term>
 <listitem><para>
@@ -4571,6 +4912,14 @@ This informational message is output when the resolver has shut down.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="RESOLVER_SHUTDOWN_RECEIVED">
+<term>RESOLVER_SHUTDOWN_RECEIVED received command to shut down</term>
+<listitem><para>
+A debug message noting that the server was asked to terminate and is
+complying to the request.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="RESOLVER_STARTED">
 <term>RESOLVER_STARTED resolver started</term>
 <listitem><para>
@@ -4605,7 +4954,7 @@ a message to the sender with the RCODE set to NOTIMP.
 </varlistentry>
 
 <varlistentry id="SOCKETREQUESTOR_CREATED">
-<term>SOCKETREQUESTOR_CREATED Socket requestor created</term>
+<term>SOCKETREQUESTOR_CREATED Socket requestor created for application %1</term>
 <listitem><para>
 Debug message.  A socket requesor (client of the socket creator) is created
 for the corresponding application.  Normally this should happen at most
@@ -4706,6 +5055,21 @@ be hidden, as it has higher debug level.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="SRVCOMM_EXCEPTION_ALLOC">
+<term>SRVCOMM_EXCEPTION_ALLOC exception when allocating a socket: %1</term>
+<listitem><para>
+The process tried to allocate a socket using the socket creator, but an error
+occurred. But it is not one of the errors we are sure are "safe". In this case
+it is unclear if the unsuccessful communication left the process and the bind10
+process in inconsistent state, so the process is going to abort to prevent
+further problems in that area.
+</para><para>
+This is probably a bug in the code, but it could be caused by other unusual
+conditions (like insufficient memory, deleted socket file used for
+communication).
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="SRVCOMM_KEYS_DEINIT">
 <term>SRVCOMM_KEYS_DEINIT deinitializing TSIG keyring</term>
 <listitem><para>
@@ -4745,6 +5109,15 @@ different set of IP addresses and ports than before.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="SRVCOMM_UNKNOWN_EXCEPTION_ALLOC">
+<term>SRVCOMM_UNKNOWN_EXCEPTION_ALLOC unknown exception when allocating a socket</term>
+<listitem><para>
+The situation is the same as in the SRVCOMM_EXCEPTION_ALLOC case, but further
+details about the error are unknown, because it was signaled by throwing
+something not being an exception. This is definitely a bug.
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="STATHTTPD_BAD_OPTION_VALUE">
 <term>STATHTTPD_BAD_OPTION_VALUE bad command line argument: %1</term>
 <listitem><para>
@@ -5117,6 +5490,35 @@ likely cause is a PYTHONPATH problem.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="XFRIN_IXFR_TRANSFER_SUCCESS">
+<term>XFRIN_IXFR_TRANSFER_SUCCESS incremental IXFR transfer of zone %1 succeeded (messages: %2, changesets: %3, deletions: %4, additions: %5, bytes: %6, run time: %7 seconds, %8 bytes/second)</term>
+<listitem><para>
+The IXFR transfer for the given zone was successful.
+The provided information contains the following values:
+</para><para>
+messages: Number of overhead DNS messages in the transfer.
+</para><para>
+changesets: Number of difference sequences.
+</para><para>
+deletions: Number of Resource Records deleted by all the changesets combined,
+including the SOA records.
+</para><para>
+additions: Number of Resource Records added by all the changesets combined,
+including the SOA records.
+</para><para>
+bytes: Full size of the transfer data on the wire.
+</para><para>
+run time: Time (in seconds) the complete ixfr took.
+</para><para>
+bytes/second: Transfer speed.
+</para><para>
+Note that there is no cross-checking of additions and deletions; if the same
+RR gets added and deleted in multiple changesets, it is counted each time;
+therefore, for each changeset, there should at least be 1 deletion and 1
+addition (the updated SOA record).
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="XFRIN_IXFR_UPTODATE">
 <term>XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating</term>
 <listitem><para>
@@ -5183,6 +5585,25 @@ daemon will now shut down.
 </para></listitem>
 </varlistentry>
 
+<varlistentry id="XFRIN_TRANSFER_SUCCESS">
+<term>XFRIN_TRANSFER_SUCCESS full %1 transfer of zone %2 succeeded (messages: %3, records: %4, bytes: %5, run time: %6 seconds, %7 bytes/second)</term>
+<listitem><para>
+The AXFR transfer of the given zone was successful.
+The provided information contains the following values:
+</para><para>
+messages: Number of overhead DNS messages in the transfer
+</para><para>
+records: Number of Resource Records in the full transfer, excluding the
+final SOA record that marks the end of the AXFR.
+</para><para>
+bytes: Full size of the transfer data on the wire.
+</para><para>
+run time: Time (in seconds) the complete axfr took
+</para><para>
+bytes/second: Transfer speed
+</para></listitem>
+</varlistentry>
+
 <varlistentry id="XFRIN_UNKNOWN_ERROR">
 <term>XFRIN_UNKNOWN_ERROR unknown error: %1</term>
 <listitem><para>
@@ -5259,13 +5680,6 @@ the SOA record has been checked, and a zone transfer has been started.
 </para></listitem>
 </varlistentry>
 
-<varlistentry id="XFRIN_XFR_TRANSFER_SUCCESS">
-<term>XFRIN_XFR_TRANSFER_SUCCESS %1 transfer of zone %2 succeeded</term>
-<listitem><para>
-The XFR transfer of the given zone was successfully completed.
-</para></listitem>
-</varlistentry>
-
 <varlistentry id="XFRIN_ZONE_CREATED">
 <term>XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created</term>
 <listitem><para>
@@ -5865,9 +6279,11 @@ a bug report.
 <term>ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager</term>
 <listitem><para>
 An XFRIN operation has failed but the zone that was the subject of the
-operation is not being managed by the zone manager.  This may indicate
-an error in the program (as the operation should not have been initiated
-if this were the case).  Please submit a bug report.
+operation is not being managed by the zone manager. This can be either the
+result of a bindctl command to transfer in a currently unknown (or mistyped)
+zone, or, if this error appears without the administrator giving transfer
+commands, it can indicate an error in the program, as it should not have
+initiated transfers of unknown zones on its own.
 </para></listitem>
 </varlistentry>
 
diff --git a/doc/images/isc-logo.png b/doc/images/isc-logo.png
new file mode 100644
index 0000000..1f7af96
Binary files /dev/null and b/doc/images/isc-logo.png differ
diff --git a/ext/LICENSE_1_0.txt b/ext/LICENSE_1_0.txt
new file mode 100644
index 0000000..36b7cd9
--- /dev/null
+++ b/ext/LICENSE_1_0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/ext/asio/asio/detail/impl/kqueue_reactor.ipp b/ext/asio/asio/detail/impl/kqueue_reactor.ipp
index 8db77cb..00ebfeb 100644
--- a/ext/asio/asio/detail/impl/kqueue_reactor.ipp
+++ b/ext/asio/asio/detail/impl/kqueue_reactor.ipp
@@ -301,12 +301,14 @@ void kqueue_reactor::run(bool block, op_queue<operation>& ops)
               EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data);
         else
           continue;
+        break;
       case EVFILT_WRITE:
         if (!descriptor_data->op_queue_[write_op].empty())
           ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE,
               EV_ADD | EV_ONESHOT, 0, 0, descriptor_data);
         else
           continue;
+        break;
       default:
         break;
       }
diff --git a/ext/asio/asio/detail/impl/socket_ops.ipp b/ext/asio/asio/detail/impl/socket_ops.ipp
index 66006ea..a1b0ec6 100644
--- a/ext/asio/asio/detail/impl/socket_ops.ipp
+++ b/ext/asio/asio/detail/impl/socket_ops.ipp
@@ -282,15 +282,26 @@ int close(socket_type s, state_type& state,
   int result = 0;
   if (s != invalid_socket)
   {
-#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-    if ((state & non_blocking) && (state & user_set_linger))
+    if (destruction && (state & user_set_linger))
     {
-      ioctl_arg_type arg = 0;
-      ::ioctlsocket(s, FIONBIO, &arg);
-      state &= ~non_blocking;
+      ::linger opt;
+      opt.l_onoff = 0;
+      opt.l_linger = 0;
+      asio::error_code ignored_ec;
+      socket_ops::setsockopt(s, state, SOL_SOCKET,
+          SO_LINGER, &opt, sizeof(opt), ignored_ec);
     }
+
+    clear_last_error();
+#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+    result = error_wrapper(::closesocket(s), ec);
 #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-    if (state & non_blocking)
+    result = error_wrapper(::close(s), ec);
+#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+
+    if (result != 0
+        && (ec == asio::error::would_block
+          || ec == asio::error::try_again))
     {
 #if defined(__SYMBIAN32__)
       int flags = ::fcntl(s, F_GETFL, 0);
@@ -301,18 +312,6 @@ int close(socket_type s, state_type& state,
       ::ioctl(s, FIONBIO, &arg);
 #endif // defined(__SYMBIAN32__)
       state &= ~non_blocking;
-    }
-#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
-
-    if (destruction && (state & user_set_linger))
-    {
-      ::linger opt;
-      opt.l_onoff = 0;
-      opt.l_linger = 0;
-      asio::error_code ignored_ec;
-      socket_ops::setsockopt(s, state, SOL_SOCKET,
-          SO_LINGER, &opt, sizeof(opt), ignored_ec);
-    }
 
     clear_last_error();
 #if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
@@ -320,6 +319,7 @@ int close(socket_type s, state_type& state,
 #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
     result = error_wrapper(::close(s), ec);
 #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
+    }
   }
 
   if (result == 0)
diff --git a/install-sh b/install-sh
deleted file mode 100755
index 6781b98..0000000
--- a/install-sh
+++ /dev/null
@@ -1,520 +0,0 @@
-#!/bin/sh
-# install - install a program, script, or datafile
-
-scriptversion=2009-04-28.21; # UTC
-
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.
-
-nl='
-'
-IFS=" ""	$nl"
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit=${DOITPROG-}
-if test -z "$doit"; then
-  doit_exec=exec
-else
-  doit_exec=$doit
-fi
-
-# Put in absolute file names if you don't have them in your path;
-# or use environment vars.
-
-chgrpprog=${CHGRPPROG-chgrp}
-chmodprog=${CHMODPROG-chmod}
-chownprog=${CHOWNPROG-chown}
-cmpprog=${CMPPROG-cmp}
-cpprog=${CPPROG-cp}
-mkdirprog=${MKDIRPROG-mkdir}
-mvprog=${MVPROG-mv}
-rmprog=${RMPROG-rm}
-stripprog=${STRIPPROG-strip}
-
-posix_glob='?'
-initialize_posix_glob='
-  test "$posix_glob" != "?" || {
-    if (set -f) 2>/dev/null; then
-      posix_glob=
-    else
-      posix_glob=:
-    fi
-  }
-'
-
-posix_mkdir=
-
-# Desired mode of installed file.
-mode=0755
-
-chgrpcmd=
-chmodcmd=$chmodprog
-chowncmd=
-mvcmd=$mvprog
-rmcmd="$rmprog -f"
-stripcmd=
-
-src=
-dst=
-dir_arg=
-dst_arg=
-
-copy_on_change=false
-no_target_directory=
-
-usage="\
-Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
-   or: $0 [OPTION]... SRCFILES... DIRECTORY
-   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
-   or: $0 [OPTION]... -d DIRECTORIES...
-
-In the 1st form, copy SRCFILE to DSTFILE.
-In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
-In the 4th, create DIRECTORIES.
-
-Options:
-     --help     display this help and exit.
-     --version  display version info and exit.
-
-  -c            (ignored)
-  -C            install only if different (preserve the last data modification time)
-  -d            create directories instead of installing files.
-  -g GROUP      $chgrpprog installed files to GROUP.
-  -m MODE       $chmodprog installed files to MODE.
-  -o USER       $chownprog installed files to USER.
-  -s            $stripprog installed files.
-  -t DIRECTORY  install into DIRECTORY.
-  -T            report an error if DSTFILE is a directory.
-
-Environment variables override the default commands:
-  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
-  RMPROG STRIPPROG
-"
-
-while test $# -ne 0; do
-  case $1 in
-    -c) ;;
-
-    -C) copy_on_change=true;;
-
-    -d) dir_arg=true;;
-
-    -g) chgrpcmd="$chgrpprog $2"
-	shift;;
-
-    --help) echo "$usage"; exit $?;;
-
-    -m) mode=$2
-	case $mode in
-	  *' '* | *'	'* | *'
-'*	  | *'*'* | *'?'* | *'['*)
-	    echo "$0: invalid mode: $mode" >&2
-	    exit 1;;
-	esac
-	shift;;
-
-    -o) chowncmd="$chownprog $2"
-	shift;;
-
-    -s) stripcmd=$stripprog;;
-
-    -t) dst_arg=$2
-	shift;;
-
-    -T) no_target_directory=true;;
-
-    --version) echo "$0 $scriptversion"; exit $?;;
-
-    --)	shift
-	break;;
-
-    -*)	echo "$0: invalid option: $1" >&2
-	exit 1;;
-
-    *)  break;;
-  esac
-  shift
-done
-
-if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
-  # When -d is used, all remaining arguments are directories to create.
-  # When -t is used, the destination is already specified.
-  # Otherwise, the last argument is the destination.  Remove it from $@.
-  for arg
-  do
-    if test -n "$dst_arg"; then
-      # $@ is not empty: it contains at least $arg.
-      set fnord "$@" "$dst_arg"
-      shift # fnord
-    fi
-    shift # arg
-    dst_arg=$arg
-  done
-fi
-
-if test $# -eq 0; then
-  if test -z "$dir_arg"; then
-    echo "$0: no input file specified." >&2
-    exit 1
-  fi
-  # It's OK to call `install-sh -d' without argument.
-  # This can happen when creating conditional directories.
-  exit 0
-fi
-
-if test -z "$dir_arg"; then
-  trap '(exit $?); exit' 1 2 13 15
-
-  # Set umask so as not to create temps with too-generous modes.
-  # However, 'strip' requires both read and write access to temps.
-  case $mode in
-    # Optimize common cases.
-    *644) cp_umask=133;;
-    *755) cp_umask=22;;
-
-    *[0-7])
-      if test -z "$stripcmd"; then
-	u_plus_rw=
-      else
-	u_plus_rw='% 200'
-      fi
-      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
-    *)
-      if test -z "$stripcmd"; then
-	u_plus_rw=
-      else
-	u_plus_rw=,u+rw
-      fi
-      cp_umask=$mode$u_plus_rw;;
-  esac
-fi
-
-for src
-do
-  # Protect names starting with `-'.
-  case $src in
-    -*) src=./$src;;
-  esac
-
-  if test -n "$dir_arg"; then
-    dst=$src
-    dstdir=$dst
-    test -d "$dstdir"
-    dstdir_status=$?
-  else
-
-    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
-    # might cause directories to be created, which would be especially bad
-    # if $src (and thus $dsttmp) contains '*'.
-    if test ! -f "$src" && test ! -d "$src"; then
-      echo "$0: $src does not exist." >&2
-      exit 1
-    fi
-
-    if test -z "$dst_arg"; then
-      echo "$0: no destination specified." >&2
-      exit 1
-    fi
-
-    dst=$dst_arg
-    # Protect names starting with `-'.
-    case $dst in
-      -*) dst=./$dst;;
-    esac
-
-    # If destination is a directory, append the input filename; won't work
-    # if double slashes aren't ignored.
-    if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
-	echo "$0: $dst_arg: Is a directory" >&2
-	exit 1
-      fi
-      dstdir=$dst
-      dst=$dstdir/`basename "$src"`
-      dstdir_status=0
-    else
-      # Prefer dirname, but fall back on a substitute if dirname fails.
-      dstdir=`
-	(dirname "$dst") 2>/dev/null ||
-	expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-	     X"$dst" : 'X\(//\)[^/]' \| \
-	     X"$dst" : 'X\(//\)$' \| \
-	     X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
-	echo X"$dst" |
-	    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\/\)[^/].*/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\/\)$/{
-		   s//\1/
-		   q
-		 }
-		 /^X\(\/\).*/{
-		   s//\1/
-		   q
-		 }
-		 s/.*/./; q'
-      `
-
-      test -d "$dstdir"
-      dstdir_status=$?
-    fi
-  fi
-
-  obsolete_mkdir_used=false
-
-  if test $dstdir_status != 0; then
-    case $posix_mkdir in
-      '')
-	# Create intermediate dirs using mode 755 as modified by the umask.
-	# This is like FreeBSD 'install' as of 1997-10-28.
-	umask=`umask`
-	case $stripcmd.$umask in
-	  # Optimize common cases.
-	  *[2367][2367]) mkdir_umask=$umask;;
-	  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-	  *[0-7])
-	    mkdir_umask=`expr $umask + 22 \
-	      - $umask % 100 % 40 + $umask % 20 \
-	      - $umask % 10 % 4 + $umask % 2
-	    `;;
-	  *) mkdir_umask=$umask,go-w;;
-	esac
-
-	# With -d, create the new directory with the user-specified mode.
-	# Otherwise, rely on $mkdir_umask.
-	if test -n "$dir_arg"; then
-	  mkdir_mode=-m$mode
-	else
-	  mkdir_mode=
-	fi
-
-	posix_mkdir=false
-	case $umask in
-	  *[123567][0-7][0-7])
-	    # POSIX mkdir -p sets u+wx bits regardless of umask, which
-	    # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-	    ;;
-	  *)
-	    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-	    trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-	    if (umask $mkdir_umask &&
-		exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
-	    then
-	      if test -z "$dir_arg" || {
-		   # Check for POSIX incompatibilities with -m.
-		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-		   # other-writeable bit of parent directory when it shouldn't.
-		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-		   ls_ld_tmpdir=`ls -ld "$tmpdir"`
-		   case $ls_ld_tmpdir in
-		     d????-?r-*) different_mode=700;;
-		     d????-?--*) different_mode=755;;
-		     *) false;;
-		   esac &&
-		   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
-		     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
-		     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-		   }
-		 }
-	      then posix_mkdir=:
-	      fi
-	      rmdir "$tmpdir/d" "$tmpdir"
-	    else
-	      # Remove any dirs left behind by ancient mkdir implementations.
-	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
-	    fi
-	    trap '' 0;;
-	esac;;
-    esac
-
-    if
-      $posix_mkdir && (
-	umask $mkdir_umask &&
-	$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
-      )
-    then :
-    else
-
-      # The umask is ridiculous, or mkdir does not conform to POSIX,
-      # or it failed possibly due to a race condition.  Create the
-      # directory the slow way, step by step, checking for races as we go.
-
-      case $dstdir in
-	/*) prefix='/';;
-	-*) prefix='./';;
-	*)  prefix='';;
-      esac
-
-      eval "$initialize_posix_glob"
-
-      oIFS=$IFS
-      IFS=/
-      $posix_glob set -f
-      set fnord $dstdir
-      shift
-      $posix_glob set +f
-      IFS=$oIFS
-
-      prefixes=
-
-      for d
-      do
-	test -z "$d" && continue
-
-	prefix=$prefix$d
-	if test -d "$prefix"; then
-	  prefixes=
-	else
-	  if $posix_mkdir; then
-	    (umask=$mkdir_umask &&
-	     $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
-	    # Don't fail if two instances are running concurrently.
-	    test -d "$prefix" || exit 1
-	  else
-	    case $prefix in
-	      *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
-	      *) qprefix=$prefix;;
-	    esac
-	    prefixes="$prefixes '$qprefix'"
-	  fi
-	fi
-	prefix=$prefix/
-      done
-
-      if test -n "$prefixes"; then
-	# Don't fail if two instances are running concurrently.
-	(umask $mkdir_umask &&
-	 eval "\$doit_exec \$mkdirprog $prefixes") ||
-	  test -d "$dstdir" || exit 1
-	obsolete_mkdir_used=true
-      fi
-    fi
-  fi
-
-  if test -n "$dir_arg"; then
-    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
-    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
-    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
-      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
-  else
-
-    # Make a couple of temp file names in the proper directory.
-    dsttmp=$dstdir/_inst.$$_
-    rmtmp=$dstdir/_rm.$$_
-
-    # Trap to clean up those temp files at exit.
-    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
-
-    # Copy the file name to the temp name.
-    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
-
-    # and set any options; do chmod last to preserve setuid bits.
-    #
-    # If any of these fail, we abort the whole thing.  If we want to
-    # ignore errors from any of these, just make sure not to ignore
-    # errors from the above "$doit $cpprog $src $dsttmp" command.
-    #
-    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
-    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
-    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
-    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
-
-    # If -C, don't bother to copy if it wouldn't change the file.
-    if $copy_on_change &&
-       old=`LC_ALL=C ls -dlL "$dst"	2>/dev/null` &&
-       new=`LC_ALL=C ls -dlL "$dsttmp"	2>/dev/null` &&
-
-       eval "$initialize_posix_glob" &&
-       $posix_glob set -f &&
-       set X $old && old=:$2:$4:$5:$6 &&
-       set X $new && new=:$2:$4:$5:$6 &&
-       $posix_glob set +f &&
-
-       test "$old" = "$new" &&
-       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
-    then
-      rm -f "$dsttmp"
-    else
-      # Rename the file to the real destination.
-      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
-
-      # The rename failed, perhaps because mv can't rename something else
-      # to itself, or perhaps because mv is so ancient that it does not
-      # support -f.
-      {
-	# Now remove or move aside any old file at destination location.
-	# We try this two ways since rm can't unlink itself on some
-	# systems and the destination file might be busy for other
-	# reasons.  In this case, the final cleanup might fail but the new
-	# file should still install successfully.
-	{
-	  test ! -f "$dst" ||
-	  $doit $rmcmd -f "$dst" 2>/dev/null ||
-	  { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
-	    { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
-	  } ||
-	  { echo "$0: cannot unlink or rename $dst" >&2
-	    (exit 1); exit 1
-	  }
-	} &&
-
-	# Now rename the file to the real destination.
-	$doit $mvcmd "$dsttmp" "$dst"
-      }
-    fi || exit 1
-
-    trap '' 0
-  fi
-done
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/m4macros/.gitignore b/m4macros/.gitignore
new file mode 100644
index 0000000..764b428
--- /dev/null
+++ b/m4macros/.gitignore
@@ -0,0 +1,5 @@
+/libtool.m4
+/lt~obsolete.m4
+/ltoptions.m4
+/ltsugar.m4
+/ltversion.m4
diff --git a/missing b/missing
deleted file mode 100755
index 28055d2..0000000
--- a/missing
+++ /dev/null
@@ -1,376 +0,0 @@
-#! /bin/sh
-# Common stub for a few missing GNU programs while installing.
-
-scriptversion=2009-04-28.21; # UTC
-
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
-# 2008, 2009 Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard at iro.umontreal.ca>, 1996.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-if test $# -eq 0; then
-  echo 1>&2 "Try \`$0 --help' for more information"
-  exit 1
-fi
-
-run=:
-sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
-sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
-
-# In the cases where this matters, `missing' is being run in the
-# srcdir already.
-if test -f configure.ac; then
-  configure_ac=configure.ac
-else
-  configure_ac=configure.in
-fi
-
-msg="missing on your system"
-
-case $1 in
---run)
-  # Try to run requested program, and just exit if it succeeds.
-  run=
-  shift
-  "$@" && exit 0
-  # Exit code 63 means version mismatch.  This often happens
-  # when the user try to use an ancient version of a tool on
-  # a file that requires a minimum version.  In this case we
-  # we should proceed has if the program had been absent, or
-  # if --run hadn't been passed.
-  if test $? = 63; then
-    run=:
-    msg="probably too old"
-  fi
-  ;;
-
-  -h|--h|--he|--hel|--help)
-    echo "\
-$0 [OPTION]... PROGRAM [ARGUMENT]...
-
-Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
-error status if there is no known handling for PROGRAM.
-
-Options:
-  -h, --help      display this help and exit
-  -v, --version   output version information and exit
-  --run           try to run the given command, and emulate it if it fails
-
-Supported PROGRAM values:
-  aclocal      touch file \`aclocal.m4'
-  autoconf     touch file \`configure'
-  autoheader   touch file \`config.h.in'
-  autom4te     touch the output file, or create a stub one
-  automake     touch all \`Makefile.in' files
-  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
-  flex         create \`lex.yy.c', if possible, from existing .c
-  help2man     touch the output file
-  lex          create \`lex.yy.c', if possible, from existing .c
-  makeinfo     touch the output file
-  tar          try tar, gnutar, gtar, then tar without non-portable flags
-  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
-
-Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
-\`g' are ignored when checking the name.
-
-Send bug reports to <bug-automake at gnu.org>."
-    exit $?
-    ;;
-
-  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
-    echo "missing $scriptversion (GNU Automake)"
-    exit $?
-    ;;
-
-  -*)
-    echo 1>&2 "$0: Unknown \`$1' option"
-    echo 1>&2 "Try \`$0 --help' for more information"
-    exit 1
-    ;;
-
-esac
-
-# normalize program name to check for.
-program=`echo "$1" | sed '
-  s/^gnu-//; t
-  s/^gnu//; t
-  s/^g//; t'`
-
-# Now exit if we have it, but it failed.  Also exit now if we
-# don't have it and --version was passed (most likely to detect
-# the program).  This is about non-GNU programs, so use $1 not
-# $program.
-case $1 in
-  lex*|yacc*)
-    # Not GNU programs, they don't have --version.
-    ;;
-
-  tar*)
-    if test -n "$run"; then
-       echo 1>&2 "ERROR: \`tar' requires --run"
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       exit 1
-    fi
-    ;;
-
-  *)
-    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
-       # We have it, but it failed.
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       # Could not run --version or --help.  This is probably someone
-       # running `$TOOL --version' or `$TOOL --help' to check whether
-       # $TOOL exists and not knowing $TOOL uses missing.
-       exit 1
-    fi
-    ;;
-esac
-
-# If it does not exist, or fails to run (possibly an outdated version),
-# try to emulate it.
-case $program in
-  aclocal*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
-         to install the \`Automake' and \`Perl' packages.  Grab them from
-         any GNU archive site."
-    touch aclocal.m4
-    ;;
-
-  autoconf*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`${configure_ac}'.  You might want to install the
-         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
-         archive site."
-    touch configure
-    ;;
-
-  autoheader*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
-         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
-         from any GNU archive site."
-    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
-    test -z "$files" && files="config.h"
-    touch_files=
-    for f in $files; do
-      case $f in
-      *:*) touch_files="$touch_files "`echo "$f" |
-				       sed -e 's/^[^:]*://' -e 's/:.*//'`;;
-      *) touch_files="$touch_files $f.in";;
-      esac
-    done
-    touch $touch_files
-    ;;
-
-  automake*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
-         You might want to install the \`Automake' and \`Perl' packages.
-         Grab them from any GNU archive site."
-    find . -type f -name Makefile.am -print |
-	   sed 's/\.am$/.in/' |
-	   while read f; do touch "$f"; done
-    ;;
-
-  autom4te*)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, but is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.
-         You can get \`$1' as part of \`Autoconf' from any GNU
-         archive site."
-
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -f "$file"; then
-	touch $file
-    else
-	test -z "$file" || exec >$file
-	echo "#! /bin/sh"
-	echo "# Created by GNU Automake missing as a replacement of"
-	echo "#  $ $@"
-	echo "exit 0"
-	chmod +x $file
-	exit 1
-    fi
-    ;;
-
-  bison*|yacc*)
-    echo 1>&2 "\
-WARNING: \`$1' $msg.  You should only need it if
-         you modified a \`.y' file.  You may need the \`Bison' package
-         in order for those modifications to take effect.  You can get
-         \`Bison' from any GNU archive site."
-    rm -f y.tab.c y.tab.h
-    if test $# -ne 1; then
-        eval LASTARG="\${$#}"
-	case $LASTARG in
-	*.y)
-	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" y.tab.c
-	    fi
-	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" y.tab.h
-	    fi
-	  ;;
-	esac
-    fi
-    if test ! -f y.tab.h; then
-	echo >y.tab.h
-    fi
-    if test ! -f y.tab.c; then
-	echo 'main() { return 0; }' >y.tab.c
-    fi
-    ;;
-
-  lex*|flex*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.l' file.  You may need the \`Flex' package
-         in order for those modifications to take effect.  You can get
-         \`Flex' from any GNU archive site."
-    rm -f lex.yy.c
-    if test $# -ne 1; then
-        eval LASTARG="\${$#}"
-	case $LASTARG in
-	*.l)
-	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
-	    if test -f "$SRCFILE"; then
-	         cp "$SRCFILE" lex.yy.c
-	    fi
-	  ;;
-	esac
-    fi
-    if test ! -f lex.yy.c; then
-	echo 'main() { return 0; }' >lex.yy.c
-    fi
-    ;;
-
-  help2man*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-	 you modified a dependency of a manual page.  You may need the
-	 \`Help2man' package in order for those modifications to take
-	 effect.  You can get \`Help2man' from any GNU archive site."
-
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -f "$file"; then
-	touch $file
-    else
-	test -z "$file" || exec >$file
-	echo ".ab help2man is required to generate this page"
-	exit $?
-    fi
-    ;;
-
-  makeinfo*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.texi' or \`.texinfo' file, or any other file
-         indirectly affecting the aspect of the manual.  The spurious
-         call might also be the consequence of using a buggy \`make' (AIX,
-         DU, IRIX).  You might want to install the \`Texinfo' package or
-         the \`GNU make' package.  Grab either from any GNU archive site."
-    # The file to touch is that specified with -o ...
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -z "$file"; then
-      # ... or it is the one specified with @setfilename ...
-      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
-      file=`sed -n '
-	/^@setfilename/{
-	  s/.* \([^ ]*\) *$/\1/
-	  p
-	  q
-	}' $infile`
-      # ... or it is derived from the source name (dir/f.texi becomes f.info)
-      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
-    fi
-    # If the file does not exist, the user really needs makeinfo;
-    # let's fail without touching anything.
-    test -f $file || exit 1
-    touch $file
-    ;;
-
-  tar*)
-    shift
-
-    # We have already tried tar in the generic part.
-    # Look for gnutar/gtar before invocation to avoid ugly error
-    # messages.
-    if (gnutar --version > /dev/null 2>&1); then
-       gnutar "$@" && exit 0
-    fi
-    if (gtar --version > /dev/null 2>&1); then
-       gtar "$@" && exit 0
-    fi
-    firstarg="$1"
-    if shift; then
-	case $firstarg in
-	*o*)
-	    firstarg=`echo "$firstarg" | sed s/o//`
-	    tar "$firstarg" "$@" && exit 0
-	    ;;
-	esac
-	case $firstarg in
-	*h*)
-	    firstarg=`echo "$firstarg" | sed s/h//`
-	    tar "$firstarg" "$@" && exit 0
-	    ;;
-	esac
-    fi
-
-    echo 1>&2 "\
-WARNING: I can't seem to be able to run \`tar' with the given arguments.
-         You may want to install GNU tar or Free paxutils, or check the
-         command line arguments."
-    exit 1
-    ;;
-
-  *)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, and is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.  Check the \`README' file,
-         it often tells you about the needed prerequisites for installing
-         this package.  You may also peek at any GNU archive site, in case
-         some other package would contain this missing \`$1' program."
-    exit 1
-    ;;
-esac
-
-exit 0
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-time-zone: "UTC"
-# time-stamp-end: "; # UTC"
-# End:
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 7c6cdb8..499e209 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -1,4 +1,4 @@
 SUBDIRS = bind10 bindctl cfgmgr ddns loadzone msgq host cmdctl auth xfrin \
-	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6
+	xfrout usermgr zonemgr stats tests resolver sockcreator dhcp4 dhcp6 dbutil
 
 check-recursive: all-recursive
diff --git a/src/bin/auth/.gitignore b/src/bin/auth/.gitignore
new file mode 100644
index 0000000..64c3fd7
--- /dev/null
+++ b/src/bin/auth/.gitignore
@@ -0,0 +1,7 @@
+/auth.spec
+/auth.spec.pre
+/auth_messages.cc
+/auth_messages.h
+/b10-auth
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/auth/auth.spec.pre.in b/src/bin/auth/auth.spec.pre.in
index 97b0e79..3eeb35e 100644
--- a/src/bin/auth/auth.spec.pre.in
+++ b/src/bin/auth/auth.spec.pre.in
@@ -47,6 +47,10 @@
                 "item_type": "string",
                 "item_optional": false,
                 "item_default": ""
+              },
+              { "item_name": "filetype",
+                "item_type": "string",
+                "item_optional": true
               }]
             }
           }]
diff --git a/src/bin/auth/auth_config.cc b/src/bin/auth/auth_config.cc
index 2ae520c..3a04dc8 100644
--- a/src/bin/auth/auth_config.cc
+++ b/src/bin/auth/auth_config.cc
@@ -12,14 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <boost/foreach.hpp>
-#include <boost/shared_ptr.hpp>
-
 #include <dns/name.h>
 #include <dns/rrclass.h>
 
@@ -27,6 +19,7 @@
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/zonetable.h>
+#include <datasrc/factory.h>
 
 #include <auth/auth_srv.h>
 #include <auth/auth_config.h>
@@ -34,6 +27,15 @@
 
 #include <server_common/portconfig.h>
 
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
 using namespace std;
 using namespace isc::dns;
 using namespace isc::data;
@@ -155,17 +157,48 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) {
 
     BOOST_FOREACH(ConstElementPtr zone_config, zones_config->listValue()) {
         ConstElementPtr origin = zone_config->get("origin");
-        if (!origin) {
+        const string origin_txt = origin ? origin->stringValue() : "";
+        if (origin_txt.empty()) {
             isc_throw(AuthConfigError, "Missing zone origin");
         }
         ConstElementPtr file = zone_config->get("file");
-        if (!file) {
+        const string file_txt = file ? file->stringValue() : "";
+        if (file_txt.empty()) {
             isc_throw(AuthConfigError, "Missing zone file for zone: "
-                      << origin->str());
+                      << origin_txt);
         }
-        boost::shared_ptr<InMemoryZoneFinder> zone_finder(new
-                                                   InMemoryZoneFinder(rrclass_,
-            Name(origin->stringValue())));
+
+        // We support the traditional text type and SQLite3 backend.  For the
+        // latter we create a client for the underlying SQLite3 data source,
+        // and build the in-memory zone using an iterator of the underlying
+        // zone.
+        ConstElementPtr filetype = zone_config->get("filetype");
+        const string filetype_txt = filetype ? filetype->stringValue() :
+            "text";
+        boost::scoped_ptr<DataSourceClientContainer> container;
+        if (filetype_txt == "sqlite3") {
+            container.reset(new DataSourceClientContainer(
+                                "sqlite3",
+                                Element::fromJSON("{\"database_file\": \"" +
+                                                  file_txt + "\"}")));
+        } else if (filetype_txt != "text") {
+            isc_throw(AuthConfigError, "Invalid filetype for zone "
+                      << origin_txt << ": " << filetype_txt);
+        }
+
+        // Note: we don't want to have such small try-catch blocks for each
+        // specific error.  We may eventually want to introduce some unified
+        // error handling framework as we have more configuration parameters.
+        // See bug #1627 for the relevant discussion.
+        InMemoryZoneFinder* imzf = NULL;
+        try {
+            imzf = new InMemoryZoneFinder(rrclass_, Name(origin_txt));
+        } catch (const isc::dns::NameParserException& ex) {
+            isc_throw(AuthConfigError, "unable to parse zone's origin: " <<
+                      ex.what());
+        }
+
+        boost::shared_ptr<InMemoryZoneFinder> zone_finder(imzf);
         const result::Result result = memory_client_->addZone(zone_finder);
         if (result == result::EXIST) {
             isc_throw(AuthConfigError, "zone "<< origin->str()
@@ -178,7 +211,12 @@ MemoryDatasourceConfig::build(ConstElementPtr config_value) {
          * need the load method to be split into some kind of build and
          * commit/abort parts.
          */
-        zone_finder->load(file->stringValue());
+        if (filetype_txt == "text") {
+            zone_finder->load(file_txt);
+        } else {
+            zone_finder->load(*container->getInstance().getIterator(
+                                  Name(origin_txt)));
+        }
     }
 }
 
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index 44f8d4b..b18feb1 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -73,6 +73,10 @@ attempt to parse the header of a received DNS packet has failed. (The
 reason for the failure is given in the message.) The server will drop the
 packet.
 
+% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified
+An error was encountered when the authoritiative server specified
+statistics data which is invalid for the auth specification file.
+
 % AUTH_LOAD_TSIG loading TSIG keys
 This is a debug message indicating that the authoritative server
 has requested the keyring holding TSIG keys from the configuration
@@ -92,6 +96,18 @@ discovered that the memory data source is disabled for the given class.
 This is a debug message reporting that the authoritative server has
 discovered that the memory data source is enabled for the given class.
 
+% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that contains zero or more than one question. (A valid
+NOTIFY packet contains one question.) The server will return a FORMERR
+error to the sender.
+
+% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
+This debug message is logged by the authoritative server when it receives
+a NOTIFY packet that an RR type of something other than SOA in the
+question section. (The RR type received is included in the message.) The
+server will return a FORMERR error to the sender.
+
 % AUTH_NO_STATS_SESSION session interface for statistics is not available
 The authoritative server had no session with the statistics module at the
 time it attempted to send it data: the attempt has been abandoned. This
@@ -102,18 +118,6 @@ This is a debug message produced by the authoritative server when it receives
 a NOTIFY packet but the XFRIN process is not running. The packet will be
 dropped and nothing returned to the sender.
 
-% AUTH_NOTIFY_RRTYPE invalid question RR type (%1) in incoming NOTIFY
-This debug message is logged by the authoritative server when it receives
-a NOTIFY packet that an RR type of something other than SOA in the
-question section. (The RR type received is included in the message.) The
-server will return a FORMERR error to the sender.
-
-% AUTH_NOTIFY_QUESTIONS invalid number of questions (%1) in incoming NOTIFY
-This debug message is logged by the authoritative server when it receives
-a NOTIFY packet that contains zero or more than one question. (A valid
-NOTIFY packet contains one question.) The server will return a FORMERR
-error to the sender.
-
 % AUTH_PACKET_PARSE_ERROR unable to parse received DNS packet: %1
 This is a debug message, generated by the authoritative server when an
 attempt to parse a received DNS packet has failed due to something other
@@ -154,6 +158,19 @@ a command from the statistics module to send it data. The 'sendstats'
 command is handled differently to other commands, which is why the debug
 message associated with it has its own code.
 
+% AUTH_RESPONSE_FAILURE exception while building response to query: %1
+This is a debug message, generated by the authoritative server when an
+attempt to create a response to a received DNS packet has failed. The
+reason for the failure is given in the log message. A SERVFAIL response
+is sent back. The most likely cause of this is an error in the data
+source implementation; it is either creating bad responses or raising
+exceptions itself.
+
+% AUTH_RESPONSE_FAILURE_UNKNOWN unknown exception while building response to query
+This debug message is similar to AUTH_RESPONSE_FAILURE, but further
+details about the error are unknown, because it was signaled by something
+which is not an exception. This is definitely a bug.
+
 % AUTH_RESPONSE_RECEIVED received response message, ignoring
 This is a debug message, this is output if the authoritative server
 receives a DNS packet with the QR bit set, i.e. a DNS response. The
@@ -260,7 +277,3 @@ This is a debug message output during the processing of a NOTIFY
 request. The zone manager component has been informed of the request,
 but has returned an error response (which is included in the message). The
 NOTIFY request will not be honored.
-
-% AUTH_INVALID_STATISTICS_DATA invalid specification of statistics data specified
-An error was encountered when the authoritiative server specified
-statistics data which is invalid for the auth specification file.
diff --git a/src/bin/auth/auth_srv.cc b/src/bin/auth/auth_srv.cc
index 1487e13..9f5642e 100644
--- a/src/bin/auth/auth_srv.cc
+++ b/src/bin/auth/auth_srv.cc
@@ -14,6 +14,7 @@
 
 #include <config.h>
 
+#include <sys/types.h>
 #include <netinet/in.h>
 
 #include <algorithm>
@@ -46,6 +47,8 @@
 #include <dns/message.h>
 #include <dns/tsig.h>
 
+#include <asiodns/dns_service.h>
+
 #include <datasrc/query.h>
 #include <datasrc/data_source.h>
 #include <datasrc/memory_datasrc.h>
@@ -77,6 +80,35 @@ using namespace isc::asiolink;
 using namespace isc::asiodns;
 using namespace isc::server_common::portconfig;
 
+namespace {
+// A helper class for cleaning up message renderer.
+//
+// A temporary object of this class is expected to be created before starting
+// response message rendering.  On construction, it (re)initialize the given
+// message renderer with the given buffer.  On destruction, it releases
+// the previously set buffer and then release any internal resource in the
+// renderer, no matter what happened during the rendering, especially even
+// when it resulted in an exception.
+//
+// Note: if we need this helper in many other places we might consider making
+// it visible to other modules.  As of this implementation this is the only
+// user of this class, so we hide it within the implementation.
+class RendererHolder {
+public:
+    RendererHolder(MessageRenderer& renderer, OutputBuffer* buffer) :
+        renderer_(renderer)
+    {
+        renderer.setBuffer(buffer);
+    }
+    ~RendererHolder() {
+        renderer_.setBuffer(NULL);
+        renderer_.clear();
+    }
+private:
+    MessageRenderer& renderer_;
+};
+}
+
 class AuthSrvImpl {
 private:
     // prohibit copy
@@ -87,18 +119,19 @@ public:
     ~AuthSrvImpl();
     isc::data::ConstElementPtr setDbFile(isc::data::ConstElementPtr config);
 
-    bool processNormalQuery(const IOMessage& io_message, MessagePtr message,
-                            OutputBufferPtr buffer,
+    bool processNormalQuery(const IOMessage& io_message, Message& message,
+                            OutputBuffer& buffer,
                             auto_ptr<TSIGContext> tsig_context);
-    bool processXfrQuery(const IOMessage& io_message, MessagePtr message,
-                         OutputBufferPtr buffer,
+    bool processXfrQuery(const IOMessage& io_message, Message& message,
+                         OutputBuffer& buffer,
                          auto_ptr<TSIGContext> tsig_context);
-    bool processNotify(const IOMessage& io_message, MessagePtr message,
-                       OutputBufferPtr buffer,
+    bool processNotify(const IOMessage& io_message, Message& message,
+                       OutputBuffer& buffer,
                        auto_ptr<TSIGContext> tsig_context);
 
     IOService io_service_;
 
+    MessageRenderer renderer_;
     /// Currently non-configurable, but will be.
     static const uint16_t DEFAULT_LOCAL_UDPSIZE = 4096;
 
@@ -142,7 +175,7 @@ public:
     /// \param done If true, the Rcode from the given message is counted,
     ///             this value is then passed to server->resume(bool)
     void resumeServer(isc::asiodns::DNSServer* server,
-                      isc::dns::MessagePtr message,
+                      isc::dns::Message& message,
                       bool done);
 private:
     std::string db_file_;
@@ -161,6 +194,8 @@ private:
 
     // validateStatistics
     bool validateStatistics(isc::data::ConstElementPtr data) const;
+
+    auth::Query query_;
 };
 
 AuthSrvImpl::AuthSrvImpl(const bool use_cache,
@@ -200,12 +235,11 @@ public:
     MessageLookup(AuthSrv* srv) : server_(srv) {}
     virtual void operator()(const IOMessage& io_message,
                             MessagePtr message,
-                            MessagePtr answer_message,
+                            MessagePtr, // Not used here
                             OutputBufferPtr buffer,
                             DNSServer* server) const
     {
-        (void) answer_message;
-        server_->processMessage(io_message, message, buffer, server);
+        server_->processMessage(io_message, *message, *buffer, server);
     }
 private:
     AuthSrv* server_;
@@ -266,55 +300,56 @@ AuthSrv::~AuthSrv() {
 namespace {
 class QuestionInserter {
 public:
-    QuestionInserter(MessagePtr message) : message_(message) {}
+    QuestionInserter(Message& message) : message_(message) {}
     void operator()(const QuestionPtr question) {
-        message_->addQuestion(question);
+        message_.addQuestion(question);
     }
-    MessagePtr message_;
+    Message& message_;
 };
 
 void
-makeErrorMessage(MessagePtr message, OutputBufferPtr buffer,
-                 const Rcode& rcode, 
+makeErrorMessage(MessageRenderer& renderer, Message& message,
+                 OutputBuffer& buffer, const Rcode& rcode,
                  std::auto_ptr<TSIGContext> tsig_context =
                  std::auto_ptr<TSIGContext>())
 {
     // extract the parameters that should be kept.
     // XXX: with the current implementation, it's not easy to set EDNS0
     // depending on whether the query had it.  So we'll simply omit it.
-    const qid_t qid = message->getQid();
-    const bool rd = message->getHeaderFlag(Message::HEADERFLAG_RD);
-    const bool cd = message->getHeaderFlag(Message::HEADERFLAG_CD);
-    const Opcode& opcode = message->getOpcode();
+    const qid_t qid = message.getQid();
+    const bool rd = message.getHeaderFlag(Message::HEADERFLAG_RD);
+    const bool cd = message.getHeaderFlag(Message::HEADERFLAG_CD);
+    const Opcode& opcode = message.getOpcode();
     vector<QuestionPtr> questions;
 
     // If this is an error to a query or notify, we should also copy the
     // question section.
     if (opcode == Opcode::QUERY() || opcode == Opcode::NOTIFY()) {
-        questions.assign(message->beginQuestion(), message->endQuestion());
+        questions.assign(message.beginQuestion(), message.endQuestion());
     }
 
-    message->clear(Message::RENDER);
-    message->setQid(qid);
-    message->setOpcode(opcode);
-    message->setHeaderFlag(Message::HEADERFLAG_QR);
+    message.clear(Message::RENDER);
+    message.setQid(qid);
+    message.setOpcode(opcode);
+    message.setHeaderFlag(Message::HEADERFLAG_QR);
     if (rd) {
-        message->setHeaderFlag(Message::HEADERFLAG_RD);
+        message.setHeaderFlag(Message::HEADERFLAG_RD);
     }
     if (cd) {
-        message->setHeaderFlag(Message::HEADERFLAG_CD);
+        message.setHeaderFlag(Message::HEADERFLAG_CD);
     }
     for_each(questions.begin(), questions.end(), QuestionInserter(message));
-    message->setRcode(rcode);
 
-    MessageRenderer renderer(*buffer);
+    message.setRcode(rcode);
+    
+    RendererHolder holder(renderer, &buffer);
     if (tsig_context.get() != NULL) {
-        message->toWire(renderer, *tsig_context);
+        message.toWire(renderer, *tsig_context);
     } else {
-        message->toWire(renderer);
+        message.toWire(renderer);
     }
     LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_ERROR_RESPONSE)
-              .arg(renderer.getLength()).arg(*message);
+              .arg(renderer.getLength()).arg(message);
 }
 }
 
@@ -412,18 +447,18 @@ AuthSrv::setStatisticsTimerInterval(uint32_t interval) {
 }
 
 void
-AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
-                        OutputBufferPtr buffer, DNSServer* server)
+AuthSrv::processMessage(const IOMessage& io_message, Message& message,
+                        OutputBuffer& buffer, DNSServer* server)
 {
     InputBuffer request_buffer(io_message.getData(), io_message.getDataSize());
 
     // First, check the header part.  If we fail even for the base header,
     // just drop the message.
     try {
-        message->parseHeader(request_buffer);
+        message.parseHeader(request_buffer);
 
         // Ignore all responses.
-        if (message->getHeaderFlag(Message::HEADERFLAG_QR)) {
+        if (message.getHeaderFlag(Message::HEADERFLAG_QR)) {
             LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_RECEIVED);
             impl_->resumeServer(server, message, false);
             return;
@@ -437,29 +472,29 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
 
     try {
         // Parse the message.
-        message->fromWire(request_buffer);
+        message.fromWire(request_buffer);
     } catch (const DNSProtocolError& error) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PROTOCOL_ERROR)
                   .arg(error.getRcode().toText()).arg(error.what());
-        makeErrorMessage(message, buffer, error.getRcode());
+        makeErrorMessage(impl_->renderer_, message, buffer, error.getRcode());
         impl_->resumeServer(server, message, true);
         return;
     } catch (const Exception& ex) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_PACKET_PARSE_ERROR)
                   .arg(ex.what());
-        makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
         impl_->resumeServer(server, message, true);
         return;
     } // other exceptions will be handled at a higher layer.
 
     LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_PACKET_RECEIVED)
-              .arg(message->toText());
+              .arg(message);
 
     // Perform further protocol-level validation.
     // TSIG first
     // If this is set to something, we know we need to answer with TSIG as well
     std::auto_ptr<TSIGContext> tsig_context;
-    const TSIGRecord* tsig_record(message->getTSIGRecord());
+    const TSIGRecord* tsig_record(message.getTSIGRecord());
     TSIGError tsig_error(TSIGError::NOERROR());
 
     // Do we do TSIG?
@@ -474,56 +509,67 @@ AuthSrv::processMessage(const IOMessage& io_message, MessagePtr message,
     }
 
     if (tsig_error != TSIGError::NOERROR()) {
-        makeErrorMessage(message, buffer, tsig_error.toRcode(), tsig_context);
+        makeErrorMessage(impl_->renderer_, message, buffer,
+                         tsig_error.toRcode(), tsig_context);
         impl_->resumeServer(server, message, true);
         return;
     }
 
-    // update per opcode statistics counter.  This can only be reliable after
-    // TSIG check succeeds.
-    impl_->counters_.inc(message->getOpcode());
-
     bool send_answer = true;
-    if (message->getOpcode() == Opcode::NOTIFY()) {
-        send_answer = impl_->processNotify(io_message, message, buffer,
-                                           tsig_context);
-    } else if (message->getOpcode() != Opcode::QUERY()) {
-        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
-                  .arg(message->getOpcode().toText());
-        makeErrorMessage(message, buffer, Rcode::NOTIMP(), tsig_context);
-    } else if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
-    } else {
-        ConstQuestionPtr question = *message->beginQuestion();
-        const RRType &qtype = question->getType();
-        if (qtype == RRType::AXFR()) {
-            send_answer = impl_->processXfrQuery(io_message, message, buffer,
-                                                 tsig_context);
-        } else if (qtype == RRType::IXFR()) {
-            send_answer = impl_->processXfrQuery(io_message, message, buffer,
-                                                 tsig_context);
+    try {
+        // update per opcode statistics counter.  This can only be reliable
+        // after TSIG check succeeds.
+        impl_->counters_.inc(message.getOpcode());
+
+        if (message.getOpcode() == Opcode::NOTIFY()) {
+            send_answer = impl_->processNotify(io_message, message, buffer,
+                                               tsig_context);
+        } else if (message.getOpcode() != Opcode::QUERY()) {
+            LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_UNSUPPORTED_OPCODE)
+                      .arg(message.getOpcode().toText());
+            makeErrorMessage(impl_->renderer_, message, buffer,
+                             Rcode::NOTIMP(), tsig_context);
+        } else if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
+            makeErrorMessage(impl_->renderer_, message, buffer,
+                             Rcode::FORMERR(), tsig_context);
         } else {
-            send_answer = impl_->processNormalQuery(io_message, message,
-                                                    buffer, tsig_context);
+            ConstQuestionPtr question = *message.beginQuestion();
+            const RRType &qtype = question->getType();
+            if (qtype == RRType::AXFR()) {
+                send_answer = impl_->processXfrQuery(io_message, message,
+                                                     buffer, tsig_context);
+            } else if (qtype == RRType::IXFR()) {
+                send_answer = impl_->processXfrQuery(io_message, message,
+                                                     buffer, tsig_context);
+            } else {
+                send_answer = impl_->processNormalQuery(io_message, message,
+                                                        buffer, tsig_context);
+            }
         }
+    } catch (const std::exception& ex) {
+        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE)
+                  .arg(ex.what());
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
+    } catch (...) {
+        LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_RESPONSE_FAILURE_UNKNOWN);
+        makeErrorMessage(impl_->renderer_, message, buffer, Rcode::SERVFAIL());
     }
-
     impl_->resumeServer(server, message, send_answer);
 }
 
 bool
-AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
-                                OutputBufferPtr buffer,
+AuthSrvImpl::processNormalQuery(const IOMessage& io_message, Message& message,
+                                OutputBuffer& buffer,
                                 auto_ptr<TSIGContext> tsig_context)
 {
-    ConstEDNSPtr remote_edns = message->getEDNS();
+    ConstEDNSPtr remote_edns = message.getEDNS();
     const bool dnssec_ok = remote_edns && remote_edns->getDNSSECAwareness();
     const uint16_t remote_bufsize = remote_edns ? remote_edns->getUDPSize() :
         Message::DEFAULT_MAX_UDPSIZE;
 
-    message->makeResponse();
-    message->setHeaderFlag(Message::HEADERFLAG_AA);
-    message->setRcode(Rcode::NOERROR());
+    message.makeResponse();
+    message.setHeaderFlag(Message::HEADERFLAG_AA);
+    message.setRcode(Rcode::NOERROR());
 
     // Increment query counter.
     incCounter(io_message.getSocket().getProtocol());
@@ -532,46 +578,44 @@ AuthSrvImpl::processNormalQuery(const IOMessage& io_message, MessagePtr message,
         EDNSPtr local_edns = EDNSPtr(new EDNS());
         local_edns->setDNSSECAwareness(dnssec_ok);
         local_edns->setUDPSize(AuthSrvImpl::DEFAULT_LOCAL_UDPSIZE);
-        message->setEDNS(local_edns);
+        message.setEDNS(local_edns);
     }
 
     try {
         // If a memory data source is configured call the separate
         // Query::process()
-        const ConstQuestionPtr question = *message->beginQuestion();
+        const ConstQuestionPtr question = *message.beginQuestion();
         if (memory_client_ && memory_client_class_ == question->getClass()) {
             const RRType& qtype = question->getType();
             const Name& qname = question->getName();
-            auth::Query(*memory_client_, qname, qtype, *message,
-                        dnssec_ok).process();
+            query_.process(*memory_client_, qname, qtype, message, dnssec_ok);
         } else {
-            datasrc::Query query(*message, cache_, dnssec_ok);
+            datasrc::Query query(message, cache_, dnssec_ok);
             data_sources_.doQuery(query);
         }
     } catch (const Exception& ex) {
         LOG_ERROR(auth_logger, AUTH_PROCESS_FAIL).arg(ex.what());
-        makeErrorMessage(message, buffer, Rcode::SERVFAIL());
+        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL());
         return (true);
     }
 
-    MessageRenderer renderer(*buffer);
+    RendererHolder holder(renderer_, &buffer);
     const bool udp_buffer =
         (io_message.getSocket().getProtocol() == IPPROTO_UDP);
-    renderer.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
+    renderer_.setLengthLimit(udp_buffer ? remote_bufsize : 65535);
     if (tsig_context.get() != NULL) {
-        message->toWire(renderer, *tsig_context);
+        message.toWire(renderer_, *tsig_context);
     } else {
-        message->toWire(renderer);
+        message.toWire(renderer_);
     }
     LOG_DEBUG(auth_logger, DBG_AUTH_MESSAGES, AUTH_SEND_NORMAL_RESPONSE)
-              .arg(renderer.getLength()).arg(message->toText());
-
+              .arg(renderer_.getLength()).arg(message);
     return (true);
 }
 
 bool
-AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
-                             OutputBufferPtr buffer,
+AuthSrvImpl::processXfrQuery(const IOMessage& io_message, Message& message,
+                             OutputBuffer& buffer,
                              auto_ptr<TSIGContext> tsig_context)
 {
     // Increment query counter.
@@ -579,7 +623,8 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
 
     if (io_message.getSocket().getProtocol() == IPPROTO_UDP) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_UDP);
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
+        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
+                         tsig_context);
         return (true);
     }
 
@@ -604,7 +649,8 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
 
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_AXFR_ERROR)
                   .arg(err.what());
-        makeErrorMessage(message, buffer, Rcode::SERVFAIL(), tsig_context);
+        makeErrorMessage(renderer_, message, buffer, Rcode::SERVFAIL(),
+                         tsig_context);
         return (true);
     }
 
@@ -612,23 +658,25 @@ AuthSrvImpl::processXfrQuery(const IOMessage& io_message, MessagePtr message,
 }
 
 bool
-AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message, 
-                           OutputBufferPtr buffer,
+AuthSrvImpl::processNotify(const IOMessage& io_message, Message& message,
+                           OutputBuffer& buffer,
                            std::auto_ptr<TSIGContext> tsig_context)
 {
     // The incoming notify must contain exactly one question for SOA of the
     // zone name.
-    if (message->getRRCount(Message::SECTION_QUESTION) != 1) {
+    if (message.getRRCount(Message::SECTION_QUESTION) != 1) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_QUESTIONS)
-                  .arg(message->getRRCount(Message::SECTION_QUESTION));
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
+                  .arg(message.getRRCount(Message::SECTION_QUESTION));
+        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
+                         tsig_context);
         return (true);
     }
-    ConstQuestionPtr question = *message->beginQuestion();
+    ConstQuestionPtr question = *message.beginQuestion();
     if (question->getType() != RRType::SOA()) {
         LOG_DEBUG(auth_logger, DBG_AUTH_DETAIL, AUTH_NOTIFY_RRTYPE)
                   .arg(question->getType().toText());
-        makeErrorMessage(message, buffer, Rcode::FORMERR(), tsig_context);
+        makeErrorMessage(renderer_, message, buffer, Rcode::FORMERR(),
+                         tsig_context);
         return (true);
     }
 
@@ -679,15 +727,15 @@ AuthSrvImpl::processNotify(const IOMessage& io_message, MessagePtr message,
         return (false);
     }
 
-    message->makeResponse();
-    message->setHeaderFlag(Message::HEADERFLAG_AA);
-    message->setRcode(Rcode::NOERROR());
+    message.makeResponse();
+    message.setHeaderFlag(Message::HEADERFLAG_AA);
+    message.setRcode(Rcode::NOERROR());
 
-    MessageRenderer renderer(*buffer);
+    RendererHolder holder(renderer_, &buffer);
     if (tsig_context.get() != NULL) {
-        message->toWire(renderer, *tsig_context);
+        message.toWire(renderer_, *tsig_context);
     } else {
-        message->toWire(renderer);
+        message.toWire(renderer_);
     }
     return (true);
 }
@@ -772,9 +820,9 @@ AuthSrvImpl::setDbFile(ConstElementPtr config) {
 }
 
 void
-AuthSrvImpl::resumeServer(DNSServer* server, MessagePtr message, bool done) {
+AuthSrvImpl::resumeServer(DNSServer* server, Message& message, bool done) {
     if (done) {
-        counters_.inc(message->getRcode());
+        counters_.inc(message.getRcode());
     }
     server->resume(done);
 }
@@ -820,11 +868,14 @@ AuthSrv::getListenAddresses() const {
 
 void
 AuthSrv::setListenAddresses(const AddressList& addresses) {
-    installListenAddresses(addresses, impl_->listen_addresses_, *dnss_);
+    // For UDP servers we specify the "SYNC_OK" option because in our usage
+    // it can act in the synchronous mode.
+    installListenAddresses(addresses, impl_->listen_addresses_, *dnss_,
+                           DNSService::SERVER_SYNC_OK);
 }
 
 void
-AuthSrv::setDNSService(isc::asiodns::DNSService& dnss) {
+AuthSrv::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
     dnss_ = &dnss;
 }
 
diff --git a/src/bin/auth/auth_srv.h b/src/bin/auth/auth_srv.h
index c1a69f1..3be711b 100644
--- a/src/bin/auth/auth_srv.h
+++ b/src/bin/auth/auth_srv.h
@@ -28,6 +28,7 @@
 #include <util/buffer.h>
 
 #include <asiodns/dns_server.h>
+#include <asiodns/dns_service.h>
 #include <asiodns/dns_lookup.h>
 #include <asiodns/dns_answer.h>
 #include <asiolink/io_message.h>
@@ -115,14 +116,14 @@ public:
     /// send the reply.
     ///
     /// \param io_message The raw message received
-    /// \param message Pointer to the \c Message object
-    /// \param buffer Pointer to an \c OutputBuffer for the resposne
+    /// \param message the \c Message object
+    /// \param buffer an \c OutputBuffer for the resposne
     /// \param server Pointer to the \c DNSServer
     ///
     /// \throw isc::Unexpected Protocol type of \a message is unexpected
     void processMessage(const isc::asiolink::IOMessage& io_message,
-                        isc::dns::MessagePtr message,
-                        isc::util::OutputBufferPtr buffer,
+                        isc::dns::Message& message,
+                        isc::util::OutputBuffer& buffer,
                         isc::asiodns::DNSServer* server);
 
     /// \brief Updates the data source for the \c AuthSrv object.
@@ -276,7 +277,7 @@ public:
     /// in-memory data source.
     ///
     /// \param rrclass The RR class of the in-memory data source to be set.
-    /// \param memory_datasrc A (shared) pointer to \c InMemoryClient to be set.
+    /// \param memory_client A (shared) pointer to \c InMemoryClient to be set.
     void setInMemoryClient(const isc::dns::RRClass& rrclass,
                            InMemoryClientPtr memory_client);
 
@@ -384,7 +385,7 @@ public:
         const;
 
     /// \brief Assign an ASIO DNS Service queue to this Auth object
-    void setDNSService(isc::asiodns::DNSService& dnss);
+    void setDNSService(isc::asiodns::DNSServiceBase& dnss);
 
     /// \brief Sets the keyring used for verifying and signing
     ///
@@ -400,7 +401,7 @@ private:
     isc::asiolink::SimpleCallback* checkin_;
     isc::asiodns::DNSLookup* dns_lookup_;
     isc::asiodns::DNSAnswer* dns_answer_;
-    isc::asiodns::DNSService* dnss_;
+    isc::asiodns::DNSServiceBase* dnss_;
 };
 
 #endif // __AUTH_SRV_H
diff --git a/src/bin/auth/b10-auth.8 b/src/bin/auth/b10-auth.8
index 56b1732..a5ef4fb 100644
--- a/src/bin/auth/b10-auth.8
+++ b/src/bin/auth/b10-auth.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-auth
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 28, 2011
+.\"      Date: March 28, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-AUTH" "8" "December 28, 2011" "BIND10" "BIND10"
+.TH "B10\-AUTH" "8" "March 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -64,7 +64,7 @@ defines the path to the SQLite3 zone file when using the sqlite datasource\&. Th
 \fIdatasources\fR
 configures data sources\&. The list items include:
 \fItype\fR
-to optionally choose the data source type (such as
+to define the required data source type (such as
 \(lqmemory\(rq);
 \fIclass\fR
 to optionally select the class (it defaults to
@@ -154,21 +154,25 @@ immediately\&.
 
 \fBshutdown\fR
 exits
-\fBb10\-auth\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-auth\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .SH "STATISTICS DATA"
 .PP
 The statistics data collected by the
 \fBb10\-stats\fR
-daemon include:
+daemon for
+\(lqAuth\(rq
+include:
 .PP
-auth\&.queries\&.tcp
+queries\&.tcp
 .RS 4
 Total count of queries received by the
 \fBb10\-auth\fR
 server over TCP since startup\&.
 .RE
 .PP
-auth\&.queries\&.udp
+queries\&.udp
 .RS 4
 Total count of queries received by the
 \fBb10\-auth\fR
@@ -198,5 +202,5 @@ The
 daemon was first coded in October 2009\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml
index 947c316..7f3a492 100644
--- a/src/bin/auth/b10-auth.xml
+++ b/src/bin/auth/b10-auth.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010-2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 28, 2011</date>
+    <date>March 28, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -119,7 +119,7 @@
     <para>
       <varname>datasources</varname> configures data sources.
       The list items include:
-      <varname>type</varname> to optionally choose the data source type
+      <varname>type</varname> to define the required data source type
       (such as <quote>memory</quote>);
       <varname>class</varname> to optionally select the class
       (it defaults to <quote>IN</quote>);
@@ -188,7 +188,10 @@
 
     <para>
       <command>shutdown</command> exits <command>b10-auth</command>.
-      (Note that the BIND 10 boss process will restart this service.)
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
     </para>
 
   </refsect1>
@@ -198,20 +201,20 @@
 
     <para>
       The statistics data collected by the <command>b10-stats</command>
-      daemon include:
+      daemon for <quote>Auth</quote> include:
     </para>
 
     <variablelist>
 
       <varlistentry>
-        <term>auth.queries.tcp</term>
+        <term>queries.tcp</term>
         <listitem><simpara>Total count of queries received by the
           <command>b10-auth</command> server over TCP since startup.
         </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>auth.queries.udp</term>
+        <term>queries.udp</term>
         <listitem><simpara>Total count of queries received by the
           <command>b10-auth</command> server over UDP since startup.
         </simpara></listitem>
@@ -219,6 +222,8 @@
 
     </variablelist>
 
+<!-- TODO: missing stats docs. See ticket #1721 -->
+
   </refsect1>
 
   <refsect1>
diff --git a/src/bin/auth/benchmarks/.gitignore b/src/bin/auth/benchmarks/.gitignore
new file mode 100644
index 0000000..24e9944
--- /dev/null
+++ b/src/bin/auth/benchmarks/.gitignore
@@ -0,0 +1 @@
+/query_bench
diff --git a/src/bin/auth/benchmarks/query_bench.cc b/src/bin/auth/benchmarks/query_bench.cc
index a365085..aa238c0 100644
--- a/src/bin/auth/benchmarks/query_bench.cc
+++ b/src/bin/auth/benchmarks/query_bench.cc
@@ -76,8 +76,8 @@ private:
     typedef boost::shared_ptr<const IOEndpoint> IOEndpointPtr;
 protected:
     QueryBenchMark(const bool enable_cache,
-                   const BenchQueries& queries, MessagePtr query_message,
-                   OutputBufferPtr buffer) :
+                   const BenchQueries& queries, Message& query_message,
+                   OutputBuffer& buffer) :
         server_(new AuthSrv(enable_cache, xfrout_client)),
         queries_(queries),
         query_message_(query_message),
@@ -95,8 +95,8 @@ public:
         for (query = queries_.begin(); query != query_end; ++query) {
             IOMessage io_message(&(*query)[0], (*query).size(), dummy_socket,
                                  *dummy_endpoint);
-            query_message_->clear(Message::PARSE);
-            buffer_->clear();
+            query_message_.clear(Message::PARSE);
+            buffer_.clear();
             server_->processMessage(io_message, query_message_, buffer_,
                                     &server);
         }
@@ -107,8 +107,8 @@ protected:
     AuthSrvPtr server_;
 private:
     const BenchQueries& queries_;
-    MessagePtr query_message_;
-    OutputBufferPtr buffer_;
+    Message& query_message_;
+    OutputBuffer& buffer_;
     IOSocket& dummy_socket;
     IOEndpointPtr dummy_endpoint;
 };
@@ -118,8 +118,8 @@ public:
     Sqlite3QueryBenchMark(const int cache_slots,
                           const char* const datasrc_file,
                           const BenchQueries& queries,
-                          MessagePtr query_message,
-                          OutputBufferPtr buffer) :
+                          Message& query_message,
+                          OutputBuffer& buffer) :
         QueryBenchMark(cache_slots >= 0 ? true : false, queries,
                        query_message, buffer)
     {
@@ -136,8 +136,8 @@ public:
     MemoryQueryBenchMark(const char* const zone_file,
                          const char* const zone_origin,
                           const BenchQueries& queries,
-                          MessagePtr query_message,
-                          OutputBufferPtr buffer) :
+                          Message& query_message,
+                          OutputBuffer& buffer) :
         QueryBenchMark(false, queries, query_message, buffer)
     {
         configureAuthServer(*server_,
@@ -255,8 +255,8 @@ main(int argc, char* argv[]) {
 
     BenchQueries queries;
     loadQueryData(query_data_file, queries, RRClass::IN());
-    OutputBufferPtr buffer(new OutputBuffer(4096));
-    MessagePtr message(new Message(Message::PARSE));
+    OutputBuffer buffer(4096);
+    Message message(Message::PARSE);
 
     cout << "Parameters:" << endl;
     cout << "  Iterations: " << iteration << endl;
diff --git a/src/bin/auth/command.cc b/src/bin/auth/command.cc
index ad0d134..055c73a 100644
--- a/src/bin/auth/command.cc
+++ b/src/bin/auth/command.cc
@@ -18,6 +18,7 @@
 
 #include <cc/data.h>
 #include <datasrc/memory_datasrc.h>
+#include <datasrc/factory.h>
 #include <config/ccsession.h>
 #include <exceptions/exceptions.h>
 #include <dns/rrclass.h>
@@ -149,26 +150,39 @@ public:
             return;
         }
 
+        const ConstElementPtr zone_config = getZoneConfig(server);
+
         // Load a new zone and replace the current zone with the new one.
         // TODO: eventually this should be incremental or done in some way
         // that doesn't block other server operations.
         // TODO: we may (should?) want to check the "last load time" and
         // the timestamp of the file and skip loading if the file isn't newer.
+        const ConstElementPtr type(zone_config->get("filetype"));
         boost::shared_ptr<InMemoryZoneFinder> zone_finder(
-            new InMemoryZoneFinder(old_zone_finder->getClass(),
-                                   old_zone_finder->getOrigin()));
-        zone_finder->load(old_zone_finder->getFileName());
-        old_zone_finder->swap(*zone_finder);
+            new InMemoryZoneFinder(old_zone_finder_->getClass(),
+                                   old_zone_finder_->getOrigin()));
+        if (type && type->stringValue() == "sqlite3") {
+            scoped_ptr<DataSourceClientContainer>
+                container(new DataSourceClientContainer("sqlite3",
+                                                        Element::fromJSON(
+                    "{\"database_file\": \"" +
+                    zone_config->get("file")->stringValue() + "\"}")));
+            zone_finder->load(*container->getInstance().getIterator(
+                old_zone_finder_->getOrigin()));
+        } else {
+            zone_finder->load(old_zone_finder_->getFileName());
+        }
+        old_zone_finder_->swap(*zone_finder);
         LOG_DEBUG(auth_logger, DBG_AUTH_OPS, AUTH_LOAD_ZONE)
                   .arg(zone_finder->getOrigin()).arg(zone_finder->getClass());
     }
 
 private:
     // zone finder to be updated with the new file.
-    boost::shared_ptr<InMemoryZoneFinder> old_zone_finder;
+    boost::shared_ptr<InMemoryZoneFinder> old_zone_finder_;
 
     // A helper private method to parse and validate command parameters.
-    // On success, it sets 'old_zone_finder' to the zone to be updated.
+    // On success, it sets 'old_zone_finder_' to the zone to be updated.
     // It returns true if everything is okay; and false if the command is
     // valid but there's no need for further process.
     bool validate(AuthSrv& server, isc::data::ConstElementPtr args) {
@@ -193,10 +207,11 @@ private:
         }
 
         ConstElementPtr class_elem = args->get("class");
-        const RRClass zone_class = class_elem ?
-            RRClass(class_elem->stringValue()) : RRClass::IN();
+        const RRClass zone_class =
+            class_elem ? RRClass(class_elem->stringValue()) : RRClass::IN();
 
-        AuthSrv::InMemoryClientPtr datasrc(server.getInMemoryClient(zone_class));
+        AuthSrv::InMemoryClientPtr datasrc(server.
+                                           getInMemoryClient(zone_class));
         if (datasrc == NULL) {
             isc_throw(AuthCommandError, "Memory data source is disabled");
         }
@@ -205,7 +220,7 @@ private:
         if (!origin_elem) {
             isc_throw(AuthCommandError, "Zone origin is missing");
         }
-        const Name origin(origin_elem->stringValue());
+        const Name origin = Name(origin_elem->stringValue());
 
         // Get the current zone
         const InMemoryClient::FindResult result = datasrc->findZone(origin);
@@ -214,11 +229,70 @@ private:
                       " is not found in data source");
         }
 
-        old_zone_finder = boost::dynamic_pointer_cast<InMemoryZoneFinder>(
+        old_zone_finder_ = boost::dynamic_pointer_cast<InMemoryZoneFinder>(
             result.zone_finder);
 
         return (true);
     }
+
+    ConstElementPtr getZoneConfig(const AuthSrv &server) {
+        if (!server.getConfigSession()) {
+            // FIXME: This is a hack to make older tests pass. We should
+            // update these tests as well sometime and remove this hack.
+            // (note that under normal situation, the
+            // server.getConfigSession() does not return NULL)
+
+            // We provide an empty map, which means no configuration --
+            // defaults.
+            return (ConstElementPtr(new MapElement()));
+        }
+
+        // Find the config corresponding to the zone.
+        // We expect the configuration to be valid, as we have it and we
+        // accepted it before, therefore it must be validated.
+        const ConstElementPtr config(server.getConfigSession()->
+                                     getValue("datasources"));
+        ConstElementPtr zone_list;
+        // Unfortunately, we need to walk the list to find the correct data
+        // source.
+        // TODO: Make it named sets. These lists are uncomfortable.
+        for (size_t i(0); i < config->size(); ++i) {
+            // We use the getValue to get defaults as well
+            const ConstElementPtr dsrc_config(config->get(i));
+            const ConstElementPtr class_config(dsrc_config->get("class"));
+            const string class_type(class_config ?
+                                    class_config->stringValue() : "IN");
+            // It is in-memory and our class matches.
+            // FIXME: Is it allowed to have two datasources for the same
+            // type and class at once? It probably would not work now
+            // anyway and we may want to change the configuration of
+            // datasources somehow.
+            if (dsrc_config->get("type")->stringValue() == "memory" &&
+                RRClass(class_type) == old_zone_finder_->getClass()) {
+                zone_list = dsrc_config->get("zones");
+                break;
+            }
+        }
+
+        if (!zone_list) {
+            isc_throw(AuthCommandError,
+                      "Corresponding data source configuration was not found");
+        }
+
+        // Now we need to walk the zones and find the correct one.
+        for (size_t i(0); i < zone_list->size(); ++i) {
+            const ConstElementPtr zone_config(zone_list->get(i));
+            if (Name(zone_config->get("origin")->stringValue()) ==
+                old_zone_finder_->getOrigin()) {
+                // The origins are the same, so we consider this config to be
+                // for the zone.
+                return (zone_config);
+            }
+        }
+
+        isc_throw(AuthCommandError,
+                  "Corresponding zone configuration was not found");
+    }
 };
 
 // The factory of command objects.
diff --git a/src/bin/auth/query.cc b/src/bin/auth/query.cc
index 73681e4..f215c04 100644
--- a/src/bin/auth/query.cc
+++ b/src/bin/auth/query.cc
@@ -12,99 +12,103 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <algorithm>            // for std::max
-#include <vector>
-#include <boost/foreach.hpp>
-#include <boost/bind.hpp>
-#include <boost/function.hpp>
-
 #include <dns/message.h>
 #include <dns/rcode.h>
+#include <dns/rrtype.h>
+#include <dns/rrset.h>
 #include <dns/rdataclass.h>
 
 #include <datasrc/client.h>
 
 #include <auth/query.h>
 
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+#include <cassert>
+#include <algorithm>            // for std::max
+#include <functional>
+#include <vector>
+
+using namespace std;
 using namespace isc::dns;
 using namespace isc::datasrc;
 using namespace isc::dns::rdata;
 
+// This is a "constant" vector storing desired RR types for the additional
+// section.  The vector is filled first time it's used.
+namespace {
+const vector<RRType>&
+A_AND_AAAA() {
+    static vector<RRType> needed_types;
+    if (needed_types.empty()) {
+        needed_types.push_back(RRType::A());
+        needed_types.push_back(RRType::AAAA());
+    }
+    return (needed_types);
+}
+}
+
 namespace isc {
 namespace auth {
 
 void
-Query::addAdditional(ZoneFinder& zone, const AbstractRRset& rrset) {
-    RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
-    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
-        const Rdata& rdata(rdata_iterator->getCurrent());
-        if (rrset.getType() == RRType::NS()) {
-            // Need to perform the search in the "GLUE OK" mode.
-            const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
-            addAdditionalAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
-        } else if (rrset.getType() == RRType::MX()) {
-            const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
-            addAdditionalAddrs(zone, mx.getMXName());
-        }
+Query::ResponseCreator::addRRset(isc::dns::Message& message,
+                                 const isc::dns::Message::Section section,
+                                 const ConstRRsetPtr& rrset, const bool dnssec)
+{
+    /// Is this RRset already in the list of RRsets added to the message?
+    const std::vector<const AbstractRRset*>::const_iterator i =
+        std::find_if(added_.begin(), added_.end(),
+                     std::bind1st(Query::ResponseCreator::IsSameKind(),
+                                  rrset.get()));
+    if (i == added_.end()) {
+        // No - add it to both the message and the list of RRsets processed.
+        // The const-cast is wrong, but the message interface seems to insist.
+        message.addRRset(section,
+                         boost::const_pointer_cast<AbstractRRset>(rrset),
+                         dnssec);
+        added_.push_back(rrset.get());
     }
 }
 
 void
-Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
-                          const ZoneFinder::FindOptions options)
+Query::ResponseCreator::create(Message& response,
+                               const vector<ConstRRsetPtr>& answers,
+                               const vector<ConstRRsetPtr>& authorities,
+                               const vector<ConstRRsetPtr>& additionals,
+                               const bool dnssec)
 {
-    // Out of zone name
-    NameComparisonResult result = zone.getOrigin().compare(qname);
-    if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
-        (result.getRelation() != NameComparisonResult::EQUAL))
-        return;
-
-    // Omit additional data which has already been provided in the answer
-    // section from the additional.
-    //
-    // All the address rrset with the owner name of qname have been inserted
-    // into ANSWER section.
-    if (qname_ == qname && qtype_ == RRType::ANY())
-        return;
-
-    // Find A rrset
-    if (qname_ != qname || qtype_ != RRType::A()) {
-        ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
-                                                    options | dnssec_opt_);
-        if (a_result.code == ZoneFinder::SUCCESS) {
-            response_.addRRset(Message::SECTION_ADDITIONAL,
-                    boost::const_pointer_cast<AbstractRRset>(a_result.rrset), dnssec_);
-        }
+    // Inserter should be reset each time the query is reset, so should be
+    // empty at this point.
+    assert(added_.empty());
+
+    // Add the RRsets to the message.  The order of sections is important,
+    // as the ResponseCreator remembers RRsets added and will not add
+    // duplicates.  Adding in the order answer, authory, additional will
+    // guarantee that if there are duplicates, the single RRset added will
+    // appear in the most important section.
+    BOOST_FOREACH(const ConstRRsetPtr& rrset, answers) {
+        addRRset(response, Message::SECTION_ANSWER, rrset, dnssec);
     }
-
-    // Find AAAA rrset
-    if (qname_ != qname || qtype_ != RRType::AAAA()) {
-        ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(),
-                                                       options | dnssec_opt_);
-        if (aaaa_result.code == ZoneFinder::SUCCESS) {
-            response_.addRRset(Message::SECTION_ADDITIONAL,
-                    boost::const_pointer_cast<AbstractRRset>(aaaa_result.rrset),
-                    dnssec_);
-        }
+    BOOST_FOREACH(const ConstRRsetPtr& rrset, authorities) {
+        addRRset(response, Message::SECTION_AUTHORITY, rrset, dnssec);
+    }
+    BOOST_FOREACH(const ConstRRsetPtr& rrset, additionals) {
+        addRRset(response, Message::SECTION_ADDITIONAL, rrset, dnssec);
     }
 }
 
 void
 Query::addSOA(ZoneFinder& finder) {
-    ZoneFinder::FindResult soa_result = finder.find(finder.getOrigin(),
-                                                    RRType::SOA(),
-                                                    dnssec_opt_);
-    if (soa_result.code != ZoneFinder::SUCCESS) {
+    ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
+                                               RRType::SOA(), dnssec_opt_);
+    if (soa_ctx->code != ZoneFinder::SUCCESS) {
         isc_throw(NoSOA, "There's no SOA record in zone " <<
             finder.getOrigin().toText());
     } else {
-        /*
-         * FIXME:
-         * The const-cast is wrong, but the Message interface seems
-         * to insist.
-         */
-        response_.addRRset(Message::SECTION_AUTHORITY,
-            boost::const_pointer_cast<AbstractRRset>(soa_result.rrset), dnssec_);
+        authorities_.push_back(soa_ctx->rrset);
     }
 }
 
@@ -123,8 +127,7 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     }
 
     // Add the NSEC proving NXDOMAIN to the authority section.
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(nsec), dnssec_);
+    authorities_.push_back(nsec);
 
     // Next, identify the best possible wildcard name that would match
     // the query name.  It's the longer common suffix with the qname
@@ -135,109 +138,111 @@ Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
     // and the best possible wildcard is *.b.example.com.  If the NXDOMAIN
     // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
     // is the next domain of the NSEC, and we get the same wildcard name.
-    const int qlabels = qname_.getLabelCount();
-    const int olabels = qname_.compare(nsec->getName()).getCommonLabels();
-    const int nlabels = qname_.compare(
+    const int qlabels = qname_->getLabelCount();
+    const int olabels = qname_->compare(nsec->getName()).getCommonLabels();
+    const int nlabels = qname_->compare(
         dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
                                            getCurrent()).
         getNextName()).getCommonLabels();
     const int common_labels = std::max(olabels, nlabels);
-    const Name wildname(Name("*").concatenate(qname_.split(qlabels -
+    const Name wildname(Name("*").concatenate(qname_->split(qlabels -
                                                            common_labels)));
 
     // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
     // otherwise we shouldn't have got NXDOMAIN for the original query in
     // the first place).
-    const ZoneFinder::FindResult fresult =
+    ConstZoneFinderContextPtr fcontext =
         finder.find(wildname, RRType::NSEC(), dnssec_opt_);
-    if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
-        fresult.rrset->getRdataCount() == 0) {
+    if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+        fcontext->rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
     }
 
-    // Add the (no-) wildcard proof only when it's different from the NSEC
-    // that proves NXDOMAIN; sometimes they can be the same.
-    // Note: name comparison is relatively expensive.  When we are at the
-    // stage of performance optimization, we should consider optimizing this
-    // for some optimized data source implementations.
-    if (nsec->getName() != fresult.rrset->getName()) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
-                           dnssec_);
+    // Add the (no-) wildcard proof.  This can be the same NSEC we already
+    // added, but we'd add it here anyway; duplicate checks will take place
+    // later in a unified manner.
+    authorities_.push_back(fcontext->rrset);
+}
+
+uint8_t
+Query::addClosestEncloserProof(ZoneFinder& finder, const Name& name,
+                               bool exact_ok, bool add_closest)
+{
+    const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, true);
+
+    // Validity check (see the method description).  Note that a completely
+    // broken findNSEC3 implementation could even return NULL RRset in
+    // closest_proof.  We don't explicitly check such case; addRRset() will
+    // throw an exception, and it will be converted to SERVFAIL at the caller.
+    if (!exact_ok && !result.next_proof) {
+        isc_throw(BadNSEC3, "Matching NSEC3 found for a non existent name: "
+                  << qname_);
+    }
+
+    if (add_closest) {
+        authorities_.push_back(result.closest_proof);
     }
+    if (result.next_proof) {
+        authorities_.push_back(result.next_proof);
+    }
+    return (result.closest_labels);
+}
+
+void
+Query::addNSEC3ForName(ZoneFinder& finder, const Name& name, bool match) {
+    const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, false);
+
+    // See the comment for addClosestEncloserProof().  We don't check a
+    // totally bogus case where closest_proof is NULL here.
+    if (match != result.matched) {
+        isc_throw(BadNSEC3, "Unexpected "
+                  << (result.matched ? "matching" : "covering")
+                  << " NSEC3 found for " << name);
+    }
+    authorities_.push_back(result.closest_proof);
 }
 
 void
 Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
     // Firstly get the NSEC3 proves for Closest Encloser Proof
-    // See section 7.2.1 of RFC 5155.
-    // Since this is a Name Error case both closest and next proofs should
-    // be available (see addNXRRsetProof).
-    const ZoneFinder::FindNSEC3Result fresult1 = finder.findNSEC3(qname_,
-                                                                  true);
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(
-                           fresult1.closest_proof),
-                       dnssec_);
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(
-                       fresult1.next_proof),
-                       dnssec_);
+    // See Section 7.2.1 of RFC 5155.
+    const uint8_t closest_labels =
+        addClosestEncloserProof(finder, *qname_, false);
 
     // Next, construct the wildcard name at the closest encloser, i.e.,
     // '*' followed by the closest encloser, and add NSEC3 for it.
     const Name wildname(Name("*").concatenate(
-               qname_.split(qname_.getLabelCount() -
-                            fresult1.closest_labels)));
-    const ZoneFinder::FindNSEC3Result fresult2 =
-        finder.findNSEC3(wildname, false);
-    if (fresult2.matched) {
-        isc_throw(BadNSEC3, "Matching NSEC3 found for nonexistent domain "
-                  << wildname);
-    }
-    response_.addRRset(Message::SECTION_AUTHORITY,
-                       boost::const_pointer_cast<AbstractRRset>(
-                           fresult2.closest_proof),
-                       dnssec_);
+               qname_->split(qname_->getLabelCount() - closest_labels)));
+    addNSEC3ForName(finder, wildname, false);
 }
 
 void
 Query::addWildcardProof(ZoneFinder& finder,
-                        const ZoneFinder::FindResult& db_result)
+                        const ZoneFinder::Context& db_context)
 {
-    if (db_result.isNSECSigned()) {
+    if (db_context.isNSECSigned()) {
         // Case for RFC4035 Section 3.1.3.3.
         //
         // The query name shouldn't exist in the zone if there were no wildcard
         // substitution.  Confirm that by specifying NO_WILDCARD.  It should
         // result in NXDOMAIN and an NSEC RR that proves it should be returned.
-        const ZoneFinder::FindResult fresult =
-            finder.find(qname_, RRType::NSEC(),
+        ConstZoneFinderContextPtr fcontext =
+            finder.find(*qname_, RRType::NSEC(),
                         dnssec_opt_ | ZoneFinder::NO_WILDCARD);
-        if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
-            fresult.rrset->getRdataCount() == 0) {
+        if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+            fcontext->rrset->getRdataCount() == 0) {
             isc_throw(BadNSEC,
                       "Unexpected NSEC result for wildcard proof");
         }
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               fresult.rrset),
-                           dnssec_);
-    } else if (db_result.isNSEC3Signed()) {
-        // Case for RFC5155 Section 7.2.6.
+        authorities_.push_back(fcontext->rrset);
+    } else if (db_context.isNSEC3Signed()) {
+        // Case for RFC 5155 Section 7.2.6.
         //
         // Note that the closest encloser must be the immediate ancestor
-        // of the matching wildcard, so NSEC3 for its next closer is what
-        // we are expected to provided per the RFC (if this assumption isn't
-        // met the zone is broken anyway).
-        const ZoneFinder::FindNSEC3Result NSEC3Result(
-            finder.findNSEC3(qname_, true));
-        // Note that at this point next_proof must not be NULL unless it's
-        // a run time collision (or zone/findNSEC3() is broken).  The
-        // unexpected case will be caught in addRRset() and result in SERVFAIL.
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               NSEC3Result.next_proof), dnssec_);
+        // of the matching wildcard, so NSEC3 for its next closer (and only
+        // that NSEC3) is what we are expected to provided per the RFC (if
+        // this assumption isn't met the zone is broken anyway).
+        addClosestEncloserProof(finder, *qname_, false, false);
     }
 }
 
@@ -249,142 +254,84 @@ Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
         isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
     }
     
-    const ZoneFinder::FindResult fresult =
-        finder.find(qname_, RRType::NSEC(),
+    ConstZoneFinderContextPtr fcontext =
+        finder.find(*qname_, RRType::NSEC(),
                     dnssec_opt_ | ZoneFinder::NO_WILDCARD);
-    if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
-        fresult.rrset->getRdataCount() == 0) {
+    if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
+        fcontext->rrset->getRdataCount() == 0) {
         isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
     }
-   
-    if (nsec->getName() != fresult.rrset->getName()) {
-        // one NSEC RR proves wildcard_nxrrset that no matched QNAME.
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
-                           dnssec_);
-    }
+
+    authorities_.push_back(fcontext->rrset);
 }
 
 void
 Query::addDS(ZoneFinder& finder, const Name& dname) {
-    ZoneFinder::FindResult ds_result =
+    ConstZoneFinderContextPtr ds_context =
         finder.find(dname, RRType::DS(), dnssec_opt_);
-    if (ds_result.code == ZoneFinder::SUCCESS) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               ds_result.rrset),
-                           dnssec_);
-    } else if (ds_result.code == ZoneFinder::NXRRSET &&
-               ds_result.isNSECSigned()) {
-        addNXRRsetProof(finder, ds_result);
-    } else if (ds_result.code == ZoneFinder::NXRRSET &&
-               ds_result.isNSEC3Signed()) {
-        // Add no DS proof with NSEC3 as specified in RFC5155 Section 7.2.7.
-        // Depending on whether the zone is optout or not, findNSEC3() may
-        // return non-NULL or NULL next_proof (respectively).  The Opt-Out flag
-        // must be set or cleared accordingly, but we don't check that
-        // in this level (as long as the zone signed validly and findNSEC3()
-        // is valid, the condition should be met; otherwise we'd let the
-        // validator detect the error).
-        const ZoneFinder::FindNSEC3Result nsec3_result =
-            finder.findNSEC3(dname, true);
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               nsec3_result.closest_proof), dnssec_);
-        if (nsec3_result.next_proof) {
-            response_.addRRset(Message::SECTION_AUTHORITY,
-                               boost::const_pointer_cast<AbstractRRset>(
-                                   nsec3_result.next_proof), dnssec_);
-        }
-    } else {
-        // Any other case should be an error
+    if (ds_context->code == ZoneFinder::SUCCESS) {
+        authorities_.push_back(ds_context->rrset);
+    } else if (ds_context->code == ZoneFinder::NXRRSET &&
+               ds_context->isNSECSigned()) {
+        addNXRRsetProof(finder, *ds_context);
+    } else if (ds_context->code == ZoneFinder::NXRRSET &&
+               ds_context->isNSEC3Signed()) {
+        // Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
+        addClosestEncloserProof(finder, dname, true);
+    } else if (ds_context->code != ZoneFinder::NXRRSET) {
+        // We know this domain should exist, so the result must be NXRRSET.
+        // If not, the zone is broken, so we'll return SERVFAIL by triggering
+        // an exception.
         isc_throw(BadDS, "Unexpected result for DS lookup for delegation");
     }
 }
 
 void
 Query::addNXRRsetProof(ZoneFinder& finder,
-                       const ZoneFinder::FindResult& db_result)
+                       const ZoneFinder::Context& db_context)
 {
-    if (db_result.isNSECSigned() && db_result.rrset) {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               db_result.rrset),
-                           dnssec_);
-        if (db_result.isWildcard()) {
-            addWildcardNXRRSETProof(finder, db_result.rrset);
+    if (db_context.isNSECSigned() && db_context.rrset) {
+        authorities_.push_back(db_context.rrset);
+        if (db_context.isWildcard()) {
+            addWildcardNXRRSETProof(finder, db_context.rrset);
         }
-    } else if (db_result.isNSEC3Signed() && !db_result.isWildcard()) {
-        // Handling depends on whether query type is DS or not
-        // (see RFC5155, 7.2.3 and 7.2.4):  If qtype == DS, do
-        // recursive search (and add next_proof, if necessary),
-        // otherwise, do non-recursive search
-        const bool qtype_ds = (qtype_ == RRType::DS());
-        ZoneFinder::FindNSEC3Result result(finder.findNSEC3(qname_, qtype_ds));
-        if (result.matched) {
-            response_.addRRset(Message::SECTION_AUTHORITY,
-                               boost::const_pointer_cast<AbstractRRset>(
-                                   result.closest_proof), dnssec_);
-            // For qtype == DS, next_proof could be set
-            // (We could check for opt-out here, but that's really the
-            // responsibility of the datasource)
-            if (qtype_ds && result.next_proof != ConstRRsetPtr()) {
-                response_.addRRset(Message::SECTION_AUTHORITY,
-                                   boost::const_pointer_cast<AbstractRRset>(
-                                       result.next_proof), dnssec_);
-            }
+    } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
+        if (*qtype_ == RRType::DS()) {
+            // RFC 5155, Section 7.2.4.  Add either NSEC3 for the qname or
+            // closest (provable) encloser proof in case of optout.
+            addClosestEncloserProof(finder, *qname_, true);
         } else {
-            isc_throw(BadNSEC3, "No matching NSEC3 found for existing domain "
-                      << qname_);
+            // RFC 5155, Section 7.2.3.  Just add NSEC3 for the qname.
+            addNSEC3ForName(finder, *qname_, true);
         }
-    } else if (db_result.isNSEC3Signed() && db_result.isWildcard()) {
-        // Case for RFC5155 Section 7.2.5
-        const ZoneFinder::FindNSEC3Result result(finder.findNSEC3(qname_,
-                                                                  true));
-        // We know there's no exact match for the qname, so findNSEC3() should
-        // return both closest and next proofs.  If the latter is NULL, it
-        // means a run time collision (or the zone is broken in other way).
-        // In that case addRRset() will throw, and it will be converted to
-        // SERVFAIL.
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               result.closest_proof), dnssec_);
-        response_.addRRset(Message::SECTION_AUTHORITY,
-                           boost::const_pointer_cast<AbstractRRset>(
-                               result.next_proof), dnssec_);
-
-        // Construct the matched wildcard name and add NSEC3 for it.
+    } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
+        // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
+        // qname, construct the matched wildcard name and add NSEC3 for it.
+        const uint8_t closest_labels =
+            addClosestEncloserProof(finder, *qname_, false);
         const Name wname = Name("*").concatenate(
-            qname_.split(qname_.getLabelCount() - result.closest_labels));
-        const ZoneFinder::FindNSEC3Result wresult(finder.findNSEC3(wname,
-                                                                   false));
-        if (wresult.matched) {
-            response_.addRRset(Message::SECTION_AUTHORITY,
-                               boost::const_pointer_cast<AbstractRRset>(
-                                   wresult.closest_proof), dnssec_);
-        } else {
-            isc_throw(BadNSEC3, "No matching NSEC3 found for existing domain "
-                      << wname);
-        }
+            qname_->split(qname_->getLabelCount() - closest_labels));
+        addNSEC3ForName(finder, wname, true);
     }
 }
 
 void
-Query::addAuthAdditional(ZoneFinder& finder) {
+Query::addAuthAdditional(ZoneFinder& finder,
+                         vector<ConstRRsetPtr>& additionals)
+{
+    const Name& origin = finder.getOrigin();
+
     // Fill in authority and addtional sections.
-    ZoneFinder::FindResult ns_result =
-        finder.find(finder.getOrigin(), RRType::NS(), dnssec_opt_);
+    ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
+                                                       dnssec_opt_);
 
     // zone origin name should have NS records
-    if (ns_result.code != ZoneFinder::SUCCESS) {
+    if (ns_context->code != ZoneFinder::SUCCESS) {
         isc_throw(NoApexNS, "There's no apex NS records in zone " <<
-                finder.getOrigin().toText());
-    } else {
-        response_.addRRset(Message::SECTION_AUTHORITY,
-            boost::const_pointer_cast<AbstractRRset>(ns_result.rrset), dnssec_);
-        // Handle additional for authority section
-        addAdditional(finder, *ns_result.rrset);
+                  finder.getOrigin().toText());
     }
+    authorities_.push_back(ns_context->rrset);
+    ns_context->getAdditional(A_AND_AAAA(), additionals);
 }
 
 namespace {
@@ -404,10 +351,20 @@ findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
 }
 
 void
-Query::process() {
+Query::process(datasrc::DataSourceClient& datasrc_client,
+               const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+               isc::dns::Message& response, bool dnssec)
+{
+    // Set up the cleaner object so internal pointers and vectors are
+    // always reset after scope leaves this method
+    QueryCleaner cleaner(*this);
+
+    // Set up query parameters for the rest of the (internal) methods
+    initialize(datasrc_client, qname, qtype, response, dnssec);
+
     // Found a zone which is the nearest ancestor to QNAME
-    const DataSourceClient::FindResult result = findZone(datasrc_client_,
-                                                         qname_, qtype_);
+    const DataSourceClient::FindResult result = findZone(*datasrc_client_,
+                                                         *qname_, *qtype_);
 
     // If we have no matching authoritative zone for the query name, return
     // REFUSED.  In short, this is to be compatible with BIND 9, but the
@@ -419,51 +376,48 @@ Query::process() {
         // If we tried to find a "parent zone" for a DS query and failed,
         // we may still have authority at the child side.  If we do, the query
         // has to be handled there.
-        if (qtype_ == RRType::DS() && qname_.getLabelCount() > 1 &&
+        if (*qtype_ == RRType::DS() && qname_->getLabelCount() > 1 &&
             processDSAtChild()) {
             return;
         }
-        response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-        response_.setRcode(Rcode::REFUSED());
+        response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
+        response_->setRcode(Rcode::REFUSED());
         return;
     }
     ZoneFinder& zfinder = *result.zone_finder;
 
     // We have authority for a zone that contain the query name (possibly
     // indirectly via delegation).  Look into the zone.
-    response_.setHeaderFlag(Message::HEADERFLAG_AA);
-    response_.setRcode(Rcode::NOERROR());
-    std::vector<ConstRRsetPtr> target;
-    boost::function0<ZoneFinder::FindResult> find;
-    const bool qtype_is_any = (qtype_ == RRType::ANY());
+    response_->setHeaderFlag(Message::HEADERFLAG_AA);
+    response_->setRcode(Rcode::NOERROR());
+    boost::function0<ZoneFinderContextPtr> find;
+    const bool qtype_is_any = (*qtype_ == RRType::ANY());
     if (qtype_is_any) {
-        find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
-                           boost::ref(target), dnssec_opt_);
+        find = boost::bind(&ZoneFinder::findAll, &zfinder, *qname_,
+                           boost::ref(answers_), dnssec_opt_);
     } else {
-        find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
+        find = boost::bind(&ZoneFinder::find, &zfinder, *qname_, *qtype_,
                            dnssec_opt_);
     }
-    ZoneFinder::FindResult db_result(find());
-    switch (db_result.code) {
+    ZoneFinderContextPtr db_context(find());
+    switch (db_context->code) {
         case ZoneFinder::DNAME: {
             // First, put the dname into the answer
-            response_.addRRset(Message::SECTION_ANSWER,
-                boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
-                dnssec_);
+            answers_.push_back(db_context->rrset);
             /*
              * Empty DNAME should never get in, as it is impossible to
              * create one in master file.
              *
              * FIXME: Other way to prevent this should be done
              */
-            assert(db_result.rrset->getRdataCount() > 0);
+            assert(db_context->rrset->getRdataCount() > 0);
             // Get the data of DNAME
             const rdata::generic::DNAME& dname(
                 dynamic_cast<const rdata::generic::DNAME&>(
-                db_result.rrset->getRdataIterator()->getCurrent()));
+                db_context->rrset->getRdataIterator()->getCurrent()));
             // The yet unmatched prefix dname
-            const Name prefix(qname_.split(0, qname_.getLabelCount() -
-                db_result.rrset->getName().getLabelCount()));
+            const Name prefix(qname_->split(0, qname_->getLabelCount() -
+                db_context->rrset->getName().getLabelCount()));
             // If we put it together, will it be too long?
             // (The prefix contains trailing ., which will be removed
             if (prefix.getLength() - Name::ROOT_NAME().getLength() +
@@ -472,20 +426,20 @@ Query::process() {
                  * In case the synthesized name is too long, section 4.1
                  * of RFC 2672 mandates we return YXDOMAIN.
                  */
-                response_.setRcode(Rcode::YXDOMAIN());
-                return;
+                response_->setRcode(Rcode::YXDOMAIN());
+                break;
             }
             // The new CNAME we are creating (it will be unsigned even
             // with DNSSEC, the DNAME is signed and it can be validated
             // by that)
-            RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
-                RRType::CNAME(), db_result.rrset->getTTL()));
+            RRsetPtr cname(new RRset(*qname_, db_context->rrset->getClass(),
+                RRType::CNAME(), db_context->rrset->getTTL()));
             // Construct the new target by replacing the end
-            cname->addRdata(rdata::generic::CNAME(qname_.split(0,
-                qname_.getLabelCount() -
-                db_result.rrset->getName().getLabelCount()).
+            cname->addRdata(rdata::generic::CNAME(qname_->split(0,
+                qname_->getLabelCount() -
+                db_context->rrset->getName().getLabelCount()).
                 concatenate(dname.getDname())));
-            response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
+            answers_.push_back(cname);
             break;
         }
         case ZoneFinder::CNAME:
@@ -498,48 +452,40 @@ Query::process() {
              *
              * So, just put it there.
              */
-            response_.addRRset(Message::SECTION_ANSWER,
-                boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
-                dnssec_);
+            answers_.push_back(db_context->rrset);
 
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
-            if (dnssec_ && db_result.isWildcard()) {
-                addWildcardProof(*result.zone_finder,db_result);
+            if (dnssec_ && db_context->isWildcard()) {
+                addWildcardProof(*result.zone_finder, *db_context);
             }
             break;
         case ZoneFinder::SUCCESS:
-            if (qtype_is_any) {
-                // If quety type is ANY, insert all RRs under the domain
-                // into answer section.
-                BOOST_FOREACH(ConstRRsetPtr rrset, target) {
-                    response_.addRRset(Message::SECTION_ANSWER,
-                        boost::const_pointer_cast<AbstractRRset>(rrset), dnssec_);
-                    // Handle additional for answer section
-                    addAdditional(*result.zone_finder, *rrset.get());
-                }
-            } else {
-                response_.addRRset(Message::SECTION_ANSWER,
-                    boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
-                    dnssec_);
-                // Handle additional for answer section
-                addAdditional(*result.zone_finder, *db_result.rrset);
+            // If query type is ANY, the rrs have already been added
+            if (!qtype_is_any) {
+                answers_.push_back(db_context->rrset);
             }
+
+            // Retrieve additional records for the answer
+            db_context->getAdditional(A_AND_AAAA(), additionals_);
+
             // If apex NS records haven't been provided in the answer
             // section, insert apex NS records into the authority section
             // and AAAA/A RRS of each of the NS RDATA into the additional
             // section.
-            if (qname_ != result.zone_finder->getOrigin() ||
-                db_result.code != ZoneFinder::SUCCESS ||
-                (qtype_ != RRType::NS() && !qtype_is_any))
+            // Checking the findZone() is a lightweight check to see if
+            // qname is the zone origin.
+            if (result.code != result::SUCCESS ||
+                db_context->code != ZoneFinder::SUCCESS ||
+                (*qtype_ != RRType::NS() && !qtype_is_any))
             {
-                addAuthAdditional(*result.zone_finder);
+                addAuthAdditional(*result.zone_finder, additionals_);
             }
 
             // If the answer is a result of wildcard substitution,
             // add a proof that there's no closer name.
-            if (dnssec_ && db_result.isWildcard()) {
-                addWildcardProof(*result.zone_finder,db_result);
+            if (dnssec_ && db_context->isWildcard()) {
+                addWildcardProof(*result.zone_finder, *db_context);
             }
             break;
         case ZoneFinder::DELEGATION:
@@ -547,28 +493,28 @@ Query::process() {
             // if we are an authority of the child, too.  If so, we need to
             // complete the process in the child as specified in Section
             // 2.2.1.2. of RFC3658.
-            if (qtype_ == RRType::DS() && processDSAtChild()) {
+            if (*qtype_ == RRType::DS() && processDSAtChild()) {
                 return;
             }
 
-            response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
-            response_.addRRset(Message::SECTION_AUTHORITY,
-                boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
-                dnssec_);
+            response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
+            authorities_.push_back(db_context->rrset);
+            // Retrieve additional records for the name servers
+            db_context->getAdditional(A_AND_AAAA(), additionals_);
+
             // If DNSSEC is requested, see whether there is a DS
             // record for this delegation.
             if (dnssec_) {
-                addDS(*result.zone_finder, db_result.rrset->getName());
+                addDS(*result.zone_finder, db_context->rrset->getName());
             }
-            addAdditional(*result.zone_finder, *db_result.rrset);
             break;
         case ZoneFinder::NXDOMAIN:
-            response_.setRcode(Rcode::NXDOMAIN());
+            response_->setRcode(Rcode::NXDOMAIN());
             addSOA(*result.zone_finder);
             if (dnssec_) {
-                if (db_result.isNSECSigned() && db_result.rrset) {
-                    addNXDOMAINProofByNSEC(zfinder, db_result.rrset);
-                } else if (db_result.isNSEC3Signed()) {
+                if (db_context->isNSECSigned() && db_context->rrset) {
+                    addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
+                } else if (db_context->isNSEC3Signed()) {
                     addNXDOMAINProofByNSEC3(zfinder);
                 }
             }
@@ -576,7 +522,7 @@ Query::process() {
         case ZoneFinder::NXRRSET:
             addSOA(*result.zone_finder);
             if (dnssec_) {
-                addNXRRsetProof(zfinder, db_result);
+                addNXRRsetProof(zfinder, *db_context);
             }
             break;
         default:
@@ -586,12 +532,41 @@ Query::process() {
             isc_throw(isc::NotImplemented, "Unknown result code");
             break;
     }
+
+    response_creator_.create(*response_, answers_, authorities_, additionals_,
+                             dnssec_);
+}
+
+void
+Query::initialize(datasrc::DataSourceClient& datasrc_client,
+                  const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+                  isc::dns::Message& response, bool dnssec)
+{
+    datasrc_client_ = &datasrc_client;
+    qname_ = &qname;
+    qtype_ = &qtype;
+    response_ = &response;
+    dnssec_ = dnssec;
+    dnssec_opt_ = (dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC :
+                   isc::datasrc::ZoneFinder::FIND_DEFAULT);
+}
+
+void
+Query::reset() {
+    datasrc_client_ = NULL;
+    qname_ = NULL;
+    qtype_ = NULL;
+    response_ = NULL;
+    answers_.clear();
+    authorities_.clear();
+    additionals_.clear();
+    response_creator_.clear();
 }
 
 bool
 Query::processDSAtChild() {
     const DataSourceClient::FindResult zresult =
-        datasrc_client_.findZone(qname_);
+        datasrc_client_->findZone(*qname_);
 
     if (zresult.code != result::SUCCESS) {
         return (false);
@@ -606,17 +581,19 @@ Query::processDSAtChild() {
     // The important point in this case is to return SOA so that the resolver
     // that happens to contact us can hunt for the appropriate parent zone
     // by seeing the SOA.
-    response_.setHeaderFlag(Message::HEADERFLAG_AA);
-    response_.setRcode(Rcode::NOERROR());
+    response_->setHeaderFlag(Message::HEADERFLAG_AA);
+    response_->setRcode(Rcode::NOERROR());
     addSOA(*zresult.zone_finder);
-    const ZoneFinder::FindResult ds_result =
-        zresult.zone_finder->find(qname_, RRType::DS(), dnssec_opt_);
-    if (ds_result.code == ZoneFinder::NXRRSET) {
+    ConstZoneFinderContextPtr ds_context =
+        zresult.zone_finder->find(*qname_, RRType::DS(), dnssec_opt_);
+    if (ds_context->code == ZoneFinder::NXRRSET) {
         if (dnssec_) {
-            addNXRRsetProof(*zresult.zone_finder, ds_result);
+            addNXRRsetProof(*zresult.zone_finder, *ds_context);
         }
     }
 
+    response_creator_.create(*response_, answers_, authorities_, additionals_,
+                             dnssec_);
     return (true);
 }
 
diff --git a/src/bin/auth/query.h b/src/bin/auth/query.h
index b6e6f05..dce19f7 100644
--- a/src/bin/auth/query.h
+++ b/src/bin/auth/query.h
@@ -15,8 +15,14 @@
  */
 
 #include <exceptions/exceptions.h>
+#include <dns/rrset.h>
 #include <datasrc/zone.h>
 
+#include <boost/noncopyable.hpp>
+
+#include <functional>
+#include <vector>
+
 namespace isc {
 namespace dns {
 class Message;
@@ -61,8 +67,14 @@ namespace auth {
 /// likely to misuse one of the classes instead of the other
 /// accidentally, and since it's considered a temporary development state,
 /// we keep this name at the moment.
-class Query {
+class Query : boost::noncopyable {
 private:
+    /// \brief Initial reserved size for the vectors in Query
+    ///
+    /// The value is larger than we expect the vectors to even become, and
+    /// has been chosen arbitrarily. The reason to set them quite high is
+    /// to prevent reallocation on addition.
+    static const size_t RESERVE_RRSETS = 64;
 
     /// \brief Adds a SOA.
     ///
@@ -96,7 +108,7 @@ private:
     ///               data
     /// \param db_result The ZoneFinder::FindResult returned by find()
     void addNXRRsetProof(isc::datasrc::ZoneFinder& finder,
-        const isc::datasrc::ZoneFinder::FindResult& db_result);
+                         const isc::datasrc::ZoneFinder::Context& db_context);
 
     /// Add NSEC RRs that prove an NXDOMAIN result.
     ///
@@ -115,7 +127,7 @@ private:
     /// of RFC5155.
     void addWildcardProof(
         isc::datasrc::ZoneFinder& finder,
-        const isc::datasrc::ZoneFinder::FindResult& dbResult);
+        const isc::datasrc::ZoneFinder::Context& db_context);
 
     /// \brief Adds one NSEC RR proved no matched QNAME,one NSEC RR proved no
     /// matched <QNAME,QTYPE> through wildcard extension.
@@ -129,44 +141,6 @@ private:
     void addWildcardNXRRSETProof(isc::datasrc::ZoneFinder& finder,
                                  isc::dns::ConstRRsetPtr nsec);
 
-    /// \brief Look up additional data (i.e., address records for the names
-    /// included in NS or MX records) and add them to the additional section.
-    ///
-    /// Note: Any additional data which has already been provided in the
-    /// answer section (i.e., if the original query happend to be for the
-    /// address of the DNS server), it should be omitted from the additional.
-    ///
-    /// This method may throw a exception because its underlying methods may
-    /// throw exceptions.
-    ///
-    /// \param zone The ZoneFinder through which the additional data for the
-    /// query is to be found.
-    /// \param rrset The RRset (i.e., NS or MX rrset) which require additional
-    /// processing.
-    void addAdditional(isc::datasrc::ZoneFinder& zone,
-                       const isc::dns::AbstractRRset& rrset);
-
-    /// \brief Find address records for a specified name.
-    ///
-    /// Search the specified zone for AAAA/A RRs of each of the NS/MX RDATA
-    /// (domain name), and insert the found ones into the additional section
-    /// if address records are available. By default the search will stop
-    /// once it encounters a zone cut.
-    ///
-    /// Note: we need to perform the search in the "GLUE OK" mode for NS RDATA,
-    /// which means that we should include A/AAAA RRs under a zone cut.
-    /// The glue records must exactly match the name in the NS RDATA, without
-    /// CNAME or wildcard processing.
-    ///
-    /// \param zone The \c ZoneFinder through which the address records is to
-    /// be found.
-    /// \param qname The name in rrset RDATA.
-    /// \param options The search options.
-    void addAdditionalAddrs(isc::datasrc::ZoneFinder& zone,
-                            const isc::dns::Name& qname,
-                            const isc::datasrc::ZoneFinder::FindOptions options
-                            = isc::datasrc::ZoneFinder::FIND_DEFAULT);
-
     /// \brief Look up a zone's NS RRset and their address records for an
     /// authoritative answer, and add them to the additional section.
     ///
@@ -185,7 +159,8 @@ private:
     ///
     /// \param finder The \c ZoneFinder through which the NS and additional
     /// data for the query are to be found.
-    void addAuthAdditional(isc::datasrc::ZoneFinder& finder);
+    void addAuthAdditional(isc::datasrc::ZoneFinder& finder,
+                           std::vector<isc::dns::ConstRRsetPtr>& additionals);
 
     /// \brief Process a DS query possible at the child side of zone cut.
     ///
@@ -204,10 +179,66 @@ private:
     /// within this method.
     bool processDSAtChild();
 
-public:
-    /// Constructor from query parameters.
+    /// \brief Add NSEC3 to the response for a closest encloser proof for a
+    /// given name.
+    ///
+    /// This method calls \c findNSEC3() of the given zone finder for the
+    /// given name in the recursive mode, and adds the returned NSEC3(s) to
+    /// the authority section of the response message associated with the
+    /// \c Query object.
+    ///
+    /// It returns the number of labels of the closest encloser (returned via
+    /// the \c findNSEC3() call) in case the caller needs to use that value
+    /// for subsequent processing, i.e, constructing the best possible wildcard
+    /// name that (would) match the query name.
+    ///
+    /// Unless \c exact_ok is true, \c name is expected to be non existent,
+    /// in which case findNSEC3() in the recursive mode must return both
+    /// closest and next proofs.  If the latter is NULL, it means a run time
+    /// collision (or the zone is broken in other way), and this method throws
+    /// a BadNSEC3 exception.
+    ///
+    /// If \c exact_ok is true, this method takes into account the case
+    /// where the name exists and may or may not be at a zone cut to an
+    /// optout zone.  In this case, depending on whether the zone is optout
+    /// or not, findNSEC3() may return non-NULL or NULL next_proof
+    /// (respectively).  This method adds the next proof if and only if
+    /// findNSEC3() returns non NULL value for it.  The Opt-Out flag
+    /// must be set or cleared accordingly, but this method doesn't check that
+    /// in this level (as long as the zone is signed validly and findNSEC3()
+    /// for the data source is implemented as documented, the condition
+    /// should be met; otherwise we'd let the validator detect the error).
+    ///
+    /// By default this method always adds the closest proof.
+    /// If \c add_closest is false, it only adds the next proof to the message.
+    /// This correspond to the case of "wildcard answer responses" as described
+    /// in Section 7.2.6 of RFC5155.
+    uint8_t addClosestEncloserProof(isc::datasrc::ZoneFinder& finder,
+                                    const isc::dns::Name& name, bool exact_ok,
+                                    bool add_closest = true);
+
+    /// \brief Add matching or covering NSEC3 to the response for a give name.
+    ///
+    /// This method calls \c findNSEC3() of the given zone finder for the
+    /// given name in the non recursive mode, and adds the returned NSEC3 to
+    /// the authority section of the response message associated with the
+    /// \c Query object.
+    ///
+    /// Depending on the caller's context, the returned NSEC3 is one and
+    /// only one of matching or covering NSEC3.  If \c match is true the
+    /// returned NSEC3 must be a matching one; otherwise it must be a covering
+    /// one.  If this assumption isn't met this method throws a BadNSEC3
+    /// exception (if it must be a matching NSEC3 but is not, it means a broken
+    /// zone, maybe with incorrect optout NSEC3s; if it must be a covering
+    /// NSEC3 but is not, it means a run time collision; or the \c findNSEC3()
+    /// implementation is broken for both cases.)
+    void addNSEC3ForName(isc::datasrc::ZoneFinder& finder,
+                         const isc::dns::Name& name, bool match);
+
+    /// Set up the Query object for a new query lookup
     ///
-    /// This constructor never throws an exception.
+    /// This is the first step of the process() method, and initializes
+    /// the member data
     ///
     /// \param datasrc_client The datasource wherein the answer to the query is
     /// to be found.
@@ -216,14 +247,49 @@ public:
     /// \param response The response message to store the answer to the query.
     /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
     ///     possible.
-    Query(const isc::datasrc::DataSourceClient& datasrc_client,
-          const isc::dns::Name& qname, const isc::dns::RRType& qtype,
-          isc::dns::Message& response, bool dnssec = false) :
-        datasrc_client_(datasrc_client), qname_(qname), qtype_(qtype),
-        response_(response), dnssec_(dnssec),
-        dnssec_opt_(dnssec ?  isc::datasrc::ZoneFinder::FIND_DNSSEC :
-                    isc::datasrc::ZoneFinder::FIND_DEFAULT)
-    {}
+    void initialize(datasrc::DataSourceClient& datasrc_client,
+                    const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+                    isc::dns::Message& response, bool dnssec = false);
+
+    /// \brief Resets any partly built response data, and internal pointers
+    ///
+    /// Called by the QueryCleaner object upon its destruction
+    void reset();
+
+    /// \brief Internal class used for cleanup of Query members
+    ///
+    /// The process() call creates an object of this class, which
+    /// upon its destruction, calls Query::reset(), so that outside
+    /// of single calls to process(), the query state is always clean.
+    class QueryCleaner {
+    public:
+        QueryCleaner(isc::auth::Query& query) : query_(query) {}
+        ~QueryCleaner() { query_.reset(); }
+    private:
+        isc::auth::Query& query_;
+    };
+
+protected:
+    // Following methods declared protected so they can be accessed
+    // by unit tests.
+
+    void createResponse();
+
+public:
+    /// Default constructor.
+    ///
+    /// Query parameters will be set by the call to process()
+    ///
+    Query() :
+        datasrc_client_(NULL), qname_(NULL), qtype_(NULL),
+        dnssec_(false), dnssec_opt_(isc::datasrc::ZoneFinder::FIND_DEFAULT),
+        response_(NULL)
+    {
+        answers_.reserve(RESERVE_RRSETS);
+        authorities_.reserve(RESERVE_RRSETS);
+        additionals_.reserve(RESERVE_RRSETS);
+    }
+
 
     /// Process the query.
     ///
@@ -251,7 +317,17 @@ public:
     /// This might throw BadZone or any of its specific subclasses, but that
     /// shouldn't happen in real-life (as BadZone means wrong data, it should
     /// have been rejected upon loading).
-    void process();
+    ///
+    /// \param datasrc_client The datasource wherein the answer to the query is
+    /// to be found.
+    /// \param qname The query name
+    /// \param qtype The RR type of the query
+    /// \param response The response message to store the answer to the query.
+    /// \param dnssec If the answer should include signatures and NSEC/NSEC3 if
+    ///     possible.
+    void process(datasrc::DataSourceClient& datasrc_client,
+                 const isc::dns::Name& qname, const isc::dns::RRType& qtype,
+                 isc::dns::Message& response, bool dnssec = false);
 
     /// \short Bad zone data encountered.
     ///
@@ -319,13 +395,105 @@ public:
         {}
     };
 
+    /// \brief Response Creator Class
+    ///
+    /// This is a helper class of Query, and is expected to be used during the
+    /// construction of the response message. This class performs the
+    /// duplicate RRset detection check.  It keeps a list of RRsets added
+    /// to the message and does not add an RRset if it is the same as one
+    /// already added.
+    ///
+    /// This class is essentially private to Query, but is visible to public
+    /// for testing purposes.  It's not expected to be used from a normal
+    /// application.
+    class ResponseCreator {
+    public:
+        /// \brief Constructor
+        ///
+        /// Reserves space for the list of RRsets.  Although the
+        /// ResponseCreator will be used to create a message from the
+        /// contents of the Query object's answers_, authorities_ and
+        /// additionals_ elements, and each of these are sized to
+        /// RESERVE_RRSETS, it is _extremely_ unlikely that all three will be
+        /// filled to capacity.  So we reserve more elements than in each of
+        /// these components, but not three times the amount.
+        ///
+        /// As with the answers_, authorities_ and additionals_ elements, the
+        /// reservation is made in the constructor to avoid dynamic allocation
+        /// of memory.  The ResponseCreator is a member variable of the Query
+        /// object so is constructed once and lasts as long as that object.
+        /// Internal state is cleared through the clear() method.
+        ResponseCreator() {
+            added_.reserve(2 * RESERVE_RRSETS);
+        }
+
+        /// \brief Reset internal state
+        void clear() {
+            added_.clear();
+        }
+
+        /// \brief Complete the response message with filling in the
+        /// response sections.
+        ///
+        /// This is the final step of the Query::process() method, and within
+        /// that method, it should be called before it returns (if any
+        /// response data is to be added)
+        ///
+        /// This will take a message to build and each RRsets for the answer,
+        /// authority, and additional sections, and add them to their
+        /// corresponding sections in the given message.  The RRsets are
+        /// filtered such that a particular RRset appears only once in the
+        /// message.
+        ///
+        /// If \c dnssec is true, it tells the message to include any RRSIGs
+        /// attached to the RRsets.
+        void create(
+            isc::dns::Message& message,
+            const std::vector<isc::dns::ConstRRsetPtr>& answers_,
+            const std::vector<isc::dns::ConstRRsetPtr>& authorities_,
+            const std::vector<isc::dns::ConstRRsetPtr>& additionals_,
+            const bool dnssec);
+
+    private:
+        // \brief RRset comparison functor.
+        struct IsSameKind : public std::binary_function<
+                            const isc::dns::AbstractRRset*,
+                            const isc::dns::AbstractRRset*,
+                            bool> {
+            bool operator()(const isc::dns::AbstractRRset* r1,
+                            const isc::dns::AbstractRRset* r2) const {
+                return (r1->isSameKind(*r2));
+            }
+        };
+
+        /// Insertion operation
+        ///
+        /// \param message Message to which the RRset is to be added
+        /// \param section Section of the message in which the RRset is put
+        /// \param rrset Pointer to RRset to be added to the message
+        /// \param dnssec Whether RRSIG records should be added as well
+        void addRRset(isc::dns::Message& message,
+                      const isc::dns::Message::Section section,
+                      const isc::dns::ConstRRsetPtr& rrset, const bool dnssec);
+
+
+    private:
+        /// List of RRsets already added to the message
+        std::vector<const isc::dns::AbstractRRset*> added_;
+    };
+
 private:
-    const isc::datasrc::DataSourceClient& datasrc_client_;
-    const isc::dns::Name& qname_;
-    const isc::dns::RRType& qtype_;
-    isc::dns::Message& response_;
-    const bool dnssec_;
-    const isc::datasrc::ZoneFinder::FindOptions dnssec_opt_;
+    const isc::datasrc::DataSourceClient* datasrc_client_;
+    const isc::dns::Name* qname_;
+    const isc::dns::RRType* qtype_;
+    bool dnssec_;
+    isc::datasrc::ZoneFinder::FindOptions dnssec_opt_;
+    ResponseCreator response_creator_;
+
+    isc::dns::Message* response_;
+    std::vector<isc::dns::ConstRRsetPtr> answers_;
+    std::vector<isc::dns::ConstRRsetPtr> authorities_;
+    std::vector<isc::dns::ConstRRsetPtr> additionals_;
 };
 
 }
diff --git a/src/bin/auth/statistics.cc b/src/bin/auth/statistics.cc
index bd16537..427f59e 100644
--- a/src/bin/auth/statistics.cc
+++ b/src/bin/auth/statistics.cc
@@ -112,9 +112,13 @@ AuthCountersImpl::submitStatistics() const {
         return (false);
     }
     std::stringstream statistics_string;
+    // add pid in order for stats to identify which auth sends
+    // statistics in the situation that multiple auth instances are
+    // working
     statistics_string << "{\"command\": [\"set\","
                       <<   "{ \"owner\": \"Auth\","
-                      <<   "  \"data\":"
+                      <<   "  \"pid\":" << getpid()
+                      <<   ", \"data\":"
                       <<     "{ \"queries.udp\": "
                       <<     server_counter_.get(AuthCounters::SERVER_UDP_QUERY)
                       <<     ", \"queries.tcp\": "
diff --git a/src/bin/auth/tests/.gitignore b/src/bin/auth/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/auth/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index d24ba89..f9fac2f 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -3,6 +3,7 @@ AM_CPPFLAGS += -I$(top_builddir)/src/bin # for generated spec_config.h header
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
 AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
 AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DAUTH_OBJ_DIR=\"$(abs_top_builddir)/src/bin/auth\"
 AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(abs_top_srcdir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
@@ -11,14 +12,18 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 if USE_STATIC_LINK
 AM_LDFLAGS = -static
+# Some test cases cannot work with static link.  To selectively disable such
+# tests we signal it via a definition.
+AM_CPPFLAGS += -DUSE_STATIC_LINK=1
 endif
 
 CLEANFILES = *.gcno *.gcda
 
+# Do not define global tests, use check-local so
+# environment can be set (needed for dynamic loading)
 TESTS =
 if HAVE_GTEST
 
-TESTS += run_unittests
 run_unittests_SOURCES = $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
 run_unittests_SOURCES += ../auth_srv.h ../auth_srv.cc
@@ -28,8 +33,10 @@ run_unittests_SOURCES += ../auth_config.h ../auth_config.cc
 run_unittests_SOURCES += ../command.h ../command.cc
 run_unittests_SOURCES += ../common.h ../common.cc
 run_unittests_SOURCES += ../statistics.h ../statistics.cc
+run_unittests_SOURCES += datasrc_util.h datasrc_util.cc
 run_unittests_SOURCES += auth_srv_unittest.cc
 run_unittests_SOURCES += config_unittest.cc
+run_unittests_SOURCES += config_syntax_unittest.cc
 run_unittests_SOURCES += command_unittest.cc
 run_unittests_SOURCES += common_unittest.cc
 run_unittests_SOURCES += query_unittest.cc
@@ -64,6 +71,10 @@ run_unittests_LDADD += $(top_builddir)/src/lib/server_common/libserver_common.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD += $(top_builddir)/src/lib/statistics/libstatistics.la
-endif
 
-noinst_PROGRAMS = $(TESTS)
+check-local:
+	B10_FROM_BUILD=${abs_top_builddir} ./run_unittests
+
+noinst_PROGRAMS = run_unittests
+
+endif
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index c3905d3..a8815da 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -41,6 +41,7 @@
 #include <dns/tests/unittest_util.h>
 #include <testutils/dnsmessage_test.h>
 #include <testutils/srv_test.h>
+#include <testutils/mockups.h>
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 
@@ -75,7 +76,7 @@ const char* const CONFIG_INMEMORY_EXAMPLE =
 class AuthSrvTest : public SrvTestBase {
 protected:
     AuthSrvTest() :
-        dnss_(ios_, NULL, NULL, NULL),
+        dnss_(),
         server(true, xfrout),
         rrclass(RRClass::IN()),
         // The empty string is expected value of the parameter of
@@ -87,8 +88,12 @@ protected:
         server.setXfrinSession(&notify_session);
         server.setStatisticsSession(&statistics_session);
     }
+
     virtual void processMessage() {
-        server.processMessage(*io_message, parse_message, response_obuffer,
+        // If processMessage has been called before, parse_message needs
+        // to be reset. If it hasn't, there's no harm in doing so
+        parse_message->clear(Message::PARSE);
+        server.processMessage(*io_message, *parse_message, *response_obuffer,
                               &dnsserv);
     }
 
@@ -120,8 +125,18 @@ protected:
             }
         }
     }
-    IOService ios_;
-    DNSService dnss_;
+
+    // Convenience method for tests that expect to return SERVFAIL
+    // It calls processMessage, checks if there is an answer, and
+    // check the header for default SERVFAIL data
+    void processAndCheckSERVFAIL() {
+        processMessage();
+        EXPECT_TRUE(dnsserv.hasAnswer());
+        headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
+                    opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+    }
+
+    MockDNSService dnss_;
     MockSession statistics_session;
     MockXfroutClient xfrout;
     AuthSrv server;
@@ -154,8 +169,7 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
     rrset_version_ns->addRdata(generic::NS(version_name));
     message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);
 
-    OutputBuffer obuffer(0);
-    MessageRenderer renderer(obuffer);
+    MessageRenderer renderer;
     message.toWire(renderer);
 
     data.clear();
@@ -174,7 +188,7 @@ TEST_F(AuthSrvTest, builtInQuery) {
                                        default_qid, Name("version.bind"),
                                        RRClass::CH(), RRType::TXT());
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     createBuiltinVersionResponse(default_qid, response_data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
@@ -286,7 +300,8 @@ TEST_F(AuthSrvTest, AXFRSuccess) {
     createRequestPacket(request_message, IPPROTO_TCP);
     // On success, the AXFR query has been passed to a separate process,
     // so we shouldn't have to respond.
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     EXPECT_TRUE(xfrout.isConnected());
     checkAllRcodeCountersZero();
@@ -307,7 +322,7 @@ TEST_F(AuthSrvTest, TSIGSigned) {
     boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     keyring->add(key);
     server.setTSIGKeyRing(&keyring);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
 
     // What did we get?
@@ -342,7 +357,7 @@ TEST_F(AuthSrvTest, TSIGSignedBadKey) {
     // Process the message, but use a different key there
     boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     server.setTSIGKeyRing(&keyring);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
 
     EXPECT_TRUE(dnsserv.hasAnswer());
@@ -377,7 +392,7 @@ TEST_F(AuthSrvTest, TSIGBadSig) {
     boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
     server.setTSIGKeyRing(&keyring);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
 
     EXPECT_TRUE(dnsserv.hasAnswer());
@@ -415,7 +430,7 @@ TEST_F(AuthSrvTest, TSIGCheckFirst) {
     boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
     keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
     server.setTSIGKeyRing(&keyring);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
 
     EXPECT_TRUE(dnsserv.hasAnswer());
@@ -446,7 +461,8 @@ TEST_F(AuthSrvTest, AXFRConnectFail) {
                                        Name("example.com"), RRClass::IN(),
                                        RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -460,7 +476,8 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
                                        Name("example.com"), RRClass::IN(),
                                        RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(xfrout.isConnected());
 
     xfrout.disableSend();
@@ -470,7 +487,8 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
                                        Name("example.com"), RRClass::IN(),
                                        RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -480,17 +498,17 @@ TEST_F(AuthSrvTest, AXFRSendFail) {
 }
 
 TEST_F(AuthSrvTest, AXFRDisconnectFail) {
-    // In our usage disconnect() shouldn't fail.  So we'll see the exception
-    // should it be thrown.
+    // In our usage disconnect() shouldn't fail. But even if it does,
+    // it should not disrupt service (so processMessage should have caught it)
     xfrout.disableSend();
     xfrout.disableDisconnect();
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                                        Name("example.com"), RRClass::IN(),
                                        RRType::AXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    EXPECT_THROW(server.processMessage(*io_message, parse_message,
-                                       response_obuffer, &dnsserv),
-                 XfroutError);
+    EXPECT_NO_THROW(server.processMessage(*io_message, *parse_message,
+                                          *response_obuffer, &dnsserv));
+    // Since the disconnect failed, we should still be 'connected'
     EXPECT_TRUE(xfrout.isConnected());
     // XXX: we need to re-enable disconnect.  otherwise an exception would be
     // thrown via the destructor of the server.
@@ -504,7 +522,8 @@ TEST_F(AuthSrvTest, IXFRConnectFail) {
                                        Name("example.com"), RRClass::IN(),
                                        RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -518,7 +537,8 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
                                        Name("example.com"), RRClass::IN(),
                                        RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(xfrout.isConnected());
 
     xfrout.disableSend();
@@ -528,7 +548,8 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
                                        Name("example.com"), RRClass::IN(),
                                        RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -538,17 +559,16 @@ TEST_F(AuthSrvTest, IXFRSendFail) {
 }
 
 TEST_F(AuthSrvTest, IXFRDisconnectFail) {
-    // In our usage disconnect() shouldn't fail.  So we'll see the exception
-    // should it be thrown.
+    // In our usage disconnect() shouldn't fail, but even if it does,
+    // procesMessage() should catch it.
     xfrout.disableSend();
     xfrout.disableDisconnect();
     UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                                        Name("example.com"), RRClass::IN(),
                                        RRType::IXFR());
     createRequestPacket(request_message, IPPROTO_TCP);
-    EXPECT_THROW(server.processMessage(*io_message, parse_message,
-                                       response_obuffer, &dnsserv),
-                 XfroutError);
+    EXPECT_NO_THROW(server.processMessage(*io_message, *parse_message,
+                                          *response_obuffer, &dnsserv));
     EXPECT_TRUE(xfrout.isConnected());
     // XXX: we need to re-enable disconnect.  otherwise an exception would be
     // thrown via the destructor of the server.
@@ -561,7 +581,8 @@ TEST_F(AuthSrvTest, notify) {
                                        RRClass::IN(), RRType::SOA());
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
 
     // An internal command message should have been created and sent to an
@@ -596,7 +617,8 @@ TEST_F(AuthSrvTest, notifyForCHClass) {
                                        RRClass::CH(), RRType::SOA());
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
 
     // Other conditions should be the same, so simply confirm the RR class is
@@ -614,7 +636,8 @@ TEST_F(AuthSrvTest, notifyEmptyQuestion) {
     request_message.setQid(default_qid);
     request_message.toWire(request_renderer);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
@@ -629,7 +652,8 @@ TEST_F(AuthSrvTest, notifyMultiQuestions) {
                                          RRType::SOA()));
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
@@ -641,7 +665,8 @@ TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
                                        RRClass::IN(), RRType::NS());
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -653,7 +678,8 @@ TEST_F(AuthSrvTest, notifyWithoutAA) {
                                        default_qid, Name("example.com"),
                                        RRClass::IN(), RRType::SOA());
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
@@ -666,7 +692,8 @@ TEST_F(AuthSrvTest, notifyWithErrorRcode) {
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     request_message.setRcode(Rcode::SERVFAIL());
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                 Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
@@ -683,7 +710,8 @@ TEST_F(AuthSrvTest, notifyWithoutSession) {
 
     // we simply ignore the notify and let it be resent if an internal error
     // happens.
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
@@ -696,7 +724,8 @@ TEST_F(AuthSrvTest, notifySendFail) {
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
 
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
@@ -708,7 +737,8 @@ TEST_F(AuthSrvTest, notifyReceiveFail) {
                                        RRClass::IN(), RRType::SOA());
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
@@ -720,7 +750,8 @@ TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
                                        RRClass::IN(), RRType::SOA());
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
@@ -733,7 +764,8 @@ TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
                                        RRClass::IN(), RRType::SOA());
     request_message.setHeaderFlag(Message::HEADERFLAG_AA);
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
@@ -748,7 +780,8 @@ updateConfig(AuthSrv* server, const char* const config_data,
 
     ConstElementPtr result = config_answer->get("result");
     EXPECT_EQ(Element::list, result->getType());
-    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
+    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue()) <<
+        "Bad result from updateConfig: " << result->str();
 }
 
 // Install a Sqlite3 data source with testing data.
@@ -759,7 +792,8 @@ TEST_F(AuthSrvTest, updateConfig) {
     // response should have the AA flag on, and have an RR in each answer
     // and authority section.
     createDataFromFile("examplequery_fromWire.wire");
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
@@ -773,7 +807,8 @@ TEST_F(AuthSrvTest, datasourceFail) {
     // in a SERVFAIL response, and the answer and authority sections should
     // be empty.
     createDataFromFile("badExampleQuery_fromWire.wire");
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                 opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
@@ -788,7 +823,8 @@ TEST_F(AuthSrvTest, updateConfigFail) {
 
     // The original data source should still exist.
     createDataFromFile("examplequery_fromWire.wire");
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                 QR_FLAG | AA_FLAG, 1, 1, 1, 0);
@@ -808,7 +844,7 @@ TEST_F(AuthSrvTest, updateWithInMemoryClient) {
 
     // The memory data source is empty, should return REFUSED rcode.
     createDataFromFile("examplequery_fromWire.wire");
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
@@ -825,7 +861,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientNoDNSSEC) {
     EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
 
     createDataFromFile("nsec3query_nodnssec_fromWire.wire");
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
 
     EXPECT_TRUE(dnsserv.hasAnswer());
@@ -842,7 +878,7 @@ TEST_F(AuthSrvTest, queryWithInMemoryClientDNSSEC) {
     EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
 
     createDataFromFile("nsec3query_fromWire.wire");
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
 
     EXPECT_TRUE(dnsserv.hasAnswer());
@@ -860,7 +896,7 @@ TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
                                        default_qid, Name("version.bind"),
                                        RRClass::CH(), RRType::TXT());
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     EXPECT_TRUE(dnsserv.hasAnswer());
     headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
@@ -886,7 +922,7 @@ TEST_F(AuthSrvTest, queryCounterUDPNormal) {
                                        default_qid, Name("example.com"),
                                        RRClass::IN(), RRType::NS());
     createRequestPacket(request_message, IPPROTO_UDP);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     // After processing UDP query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
@@ -905,7 +941,7 @@ TEST_F(AuthSrvTest, queryCounterTCPNormal) {
                                        default_qid, Name("example.com"),
                                        RRClass::IN(), RRType::NS());
     createRequestPacket(request_message, IPPROTO_TCP);
-    server.processMessage(*io_message, parse_message, response_obuffer,
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
                           &dnsserv);
     // After processing TCP query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -924,7 +960,8 @@ TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
     createRequestPacket(request_message, IPPROTO_TCP);
     // On success, the AXFR query has been passed to a separate process,
     // so auth itself shouldn't respond.
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     // After processing TCP AXFR query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -941,7 +978,8 @@ TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
     createRequestPacket(request_message, IPPROTO_TCP);
     // On success, the IXFR query has been passed to a separate process,
     // so auth itself shouldn't respond.
-    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
     EXPECT_FALSE(dnsserv.hasAnswer());
     // After processing TCP IXFR query, the counter should be 1.
     EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
@@ -962,7 +1000,8 @@ TEST_F(AuthSrvTest, queryCounterOpcodes) {
         // we intentionally use different values for each code
         for (int j = 0; j <= i; ++j) {
             parse_message->clear(Message::PARSE);
-            server.processMessage(*io_message, parse_message, response_obuffer,
+            server.processMessage(*io_message, *parse_message,
+                                  *response_obuffer,
                                   &dnsserv);
         }
 
@@ -988,11 +1027,10 @@ getDummyUnknownSocket() {
     return (socket);
 }
 
-// Submit unexpected type of query and check it throws isc::Unexpected
+// Submit unexpected type of query and check it is ignored
 TEST_F(AuthSrvTest, queryCounterUnexpected) {
     // This code isn't exception safe, but we'd rather keep the code
-    // simpler and more readable as this is only for tests and if it throws
-    // the program would immediately terminate anyway.
+    // simpler and more readable as this is only for tests
 
     // Create UDP query packet.
     UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
@@ -1008,9 +1046,7 @@ TEST_F(AuthSrvTest, queryCounterUnexpected) {
                                request_renderer.getLength(),
                                getDummyUnknownSocket(), *endpoint);
 
-    EXPECT_THROW(server.processMessage(*io_message, parse_message,
-                                       response_obuffer, &dnsserv),
-                 isc::Unexpected);
+    EXPECT_FALSE(dnsserv.hasAnswer());
 }
 
 TEST_F(AuthSrvTest, stop) {
@@ -1039,4 +1075,314 @@ TEST_F(AuthSrvTest, listenAddresses) {
                                 "Released tokens");
 }
 
+TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer1) {
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("example.com"),
+                                       RRClass::IN(), RRType::NS());
+
+    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+    EXPECT_NE(request_message.getRcode(), parse_message->getRcode());
+}
+
+TEST_F(AuthSrvTest, processNormalQuery_reuseRenderer2) {
+    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
+                                       default_qid, Name("example.com"),
+                                       RRClass::IN(), RRType::SOA());
+
+    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
+    createRequestPacket(request_message, IPPROTO_UDP);
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+    ConstQuestionPtr question = *parse_message->beginQuestion();
+    EXPECT_STRNE(question->getType().toText().c_str(),
+                 RRType::NS().toText().c_str());
+}
+//
+// Tests for catching exceptions in various stages of the query processing
+//
+// These tests work by defining two proxy classes, that act as an in-memory
+// client by default, but can throw exceptions at various points.
+//
+namespace {
+
+/// A the possible methods to throw in, either in FakeInMemoryClient or
+/// FakeZoneFinder
+enum ThrowWhen {
+    THROW_NEVER,
+    THROW_AT_FIND_ZONE,
+    THROW_AT_GET_ORIGIN,
+    THROW_AT_GET_CLASS,
+    THROW_AT_FIND,
+    THROW_AT_FIND_ALL,
+    THROW_AT_FIND_NSEC3
+};
+
+/// convenience function to check whether and what to throw
+void
+checkThrow(ThrowWhen method, ThrowWhen throw_at, bool isc_exception) {
+    if (method == throw_at) {
+        if (isc_exception) {
+            isc_throw(isc::Exception, "foo");
+        } else {
+            throw std::exception();
+        }
+    }
+}
+
+/// \brief proxy class for the ZoneFinder returned by the InMemoryClient
+///        proxied by FakeInMemoryClient
+///
+/// See the documentation for FakeInMemoryClient for more information,
+/// all methods simply check whether they should throw, and if not, call
+/// their proxied equivalent.
+class FakeZoneFinder : public isc::datasrc::ZoneFinder {
+public:
+    FakeZoneFinder(isc::datasrc::ZoneFinderPtr zone_finder,
+                   ThrowWhen throw_when, bool isc_exception,
+                   ConstRRsetPtr fake_rrset) :
+        real_zone_finder_(zone_finder),
+        throw_when_(throw_when),
+        isc_exception_(isc_exception),
+        fake_rrset_(fake_rrset)
+    {}
+
+    virtual isc::dns::Name
+    getOrigin() const {
+        checkThrow(THROW_AT_GET_ORIGIN, throw_when_, isc_exception_);
+        return (real_zone_finder_->getOrigin());
+    }
+
+    virtual isc::dns::RRClass
+    getClass() const {
+        checkThrow(THROW_AT_GET_CLASS, throw_when_, isc_exception_);
+        return (real_zone_finder_->getClass());
+    }
+
+    virtual isc::datasrc::ZoneFinderContextPtr
+    find(const isc::dns::Name& name,
+         const isc::dns::RRType& type,
+         isc::datasrc::ZoneFinder::FindOptions options)
+    {
+        using namespace isc::datasrc;
+        checkThrow(THROW_AT_FIND, throw_when_, isc_exception_);
+        // If faked RRset was specified on construction and it matches the
+        // query, return it instead of searching the real data source.
+        if (fake_rrset_ && fake_rrset_->getName() == name &&
+            fake_rrset_->getType() == type)
+        {
+            return (ZoneFinderContextPtr(new ZoneFinder::Context(
+                                             *this, options,
+                                             ResultContext(SUCCESS,
+                                                           fake_rrset_))));
+        }
+        return (real_zone_finder_->find(name, type, options));
+    }
+
+    virtual isc::datasrc::ZoneFinderContextPtr
+    findAll(const isc::dns::Name& name,
+            std::vector<isc::dns::ConstRRsetPtr> &target,
+            const FindOptions options = FIND_DEFAULT)
+    {
+        checkThrow(THROW_AT_FIND_ALL, throw_when_, isc_exception_);
+        return (real_zone_finder_->findAll(name, target, options));
+    }
+
+    virtual FindNSEC3Result
+    findNSEC3(const isc::dns::Name& name, bool recursive) {
+        checkThrow(THROW_AT_FIND_NSEC3, throw_when_, isc_exception_);
+        return (real_zone_finder_->findNSEC3(name, recursive));
+    }
+
+    virtual isc::dns::Name
+    findPreviousName(const isc::dns::Name& query) const {
+        return (real_zone_finder_->findPreviousName(query));
+    }
+
+private:
+    isc::datasrc::ZoneFinderPtr real_zone_finder_;
+    ThrowWhen throw_when_;
+    bool isc_exception_;
+    ConstRRsetPtr fake_rrset_;
+};
+
+/// \brief Proxy InMemoryClient that can throw exceptions at specified times
+///
+/// It is based on the memory client since that one is easy to override
+/// (with setInMemoryClient) with the current design of AuthSrv.
+class FakeInMemoryClient : public isc::datasrc::InMemoryClient {
+public:
+    /// \brief Create a proxy memory client
+    ///
+    /// \param real_client The real in-memory client to proxy
+    /// \param throw_when if set to any value other than never, that is
+    ///        the method that will throw an exception (either in this
+    ///        class or the related FakeZoneFinder)
+    /// \param isc_exception if true, throw isc::Exception, otherwise,
+    ///                      throw std::exception
+    /// \param fake_rrset If non NULL, it will be used as an answer to
+    /// find() for that name and type.
+    FakeInMemoryClient(AuthSrv::InMemoryClientPtr real_client,
+                       ThrowWhen throw_when, bool isc_exception,
+                       ConstRRsetPtr fake_rrset = ConstRRsetPtr()) :
+        real_client_(real_client),
+        throw_when_(throw_when),
+        isc_exception_(isc_exception),
+        fake_rrset_(fake_rrset)
+    {}
+
+    /// \brief proxy call for findZone
+    ///
+    /// if this instance was constructed with throw_when set to find_zone,
+    /// this method will throw. Otherwise, it will return a FakeZoneFinder
+    /// instance which will throw at the method specified at the
+    /// construction of this instance.
+    virtual FindResult
+    findZone(const isc::dns::Name& name) const {
+        checkThrow(THROW_AT_FIND_ZONE, throw_when_, isc_exception_);
+        const FindResult result = real_client_->findZone(name);
+        return (FindResult(result.code, isc::datasrc::ZoneFinderPtr(
+                                        new FakeZoneFinder(result.zone_finder,
+                                                           throw_when_,
+                                                           isc_exception_,
+                                                           fake_rrset_))));
+    }
+
+private:
+    AuthSrv::InMemoryClientPtr real_client_;
+    ThrowWhen throw_when_;
+    bool isc_exception_;
+    ConstRRsetPtr fake_rrset_;
+};
+
+} // end anonymous namespace for throwing proxy classes
+
+// Test for the tests
+//
+// Set the proxies to never throw, this should have the same result as
+// queryWithInMemoryClientNoDNSSEC, and serves to test the two proxy classes
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxy) {
+    // Set real inmem client to proxy
+    updateConfig(&server, CONFIG_INMEMORY_EXAMPLE, true);
+
+    AuthSrv::InMemoryClientPtr fake_client(
+        new FakeInMemoryClient(server.getInMemoryClient(rrclass),
+                               THROW_NEVER, false));
+    ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
+    server.setInMemoryClient(rrclass, fake_client);
+
+    createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+}
+
+// Convenience function for the rest of the tests, set up a proxy
+// to throw in the given method
+// If isc_exception is true, it will throw isc::Exception, otherwise
+// it will throw std::exception
+// If non null rrset is given, it will be passed to the proxy so it can
+// return some faked response.
+void
+setupThrow(AuthSrv* server, const char *config, ThrowWhen throw_when,
+           bool isc_exception, ConstRRsetPtr rrset = ConstRRsetPtr())
+{
+    // Set real inmem client to proxy
+    updateConfig(server, config, true);
+
+    // Set it to throw on findZone(), this should result in
+    // SERVFAIL on any exception
+    AuthSrv::InMemoryClientPtr fake_client(
+        new FakeInMemoryClient(
+            server->getInMemoryClient(isc::dns::RRClass::IN()),
+            throw_when, isc_exception, rrset));
+
+    ASSERT_NE(AuthSrv::InMemoryClientPtr(),
+              server->getInMemoryClient(isc::dns::RRClass::IN()));
+    server->setInMemoryClient(isc::dns::RRClass::IN(), fake_client);
+}
+
+TEST_F(AuthSrvTest, queryWithThrowingProxyServfails) {
+    // Test the common cases, all of which should simply return SERVFAIL
+    // Use THROW_NEVER as end marker
+    ThrowWhen throws[] = { THROW_AT_FIND_ZONE,
+                           THROW_AT_GET_ORIGIN,
+                           THROW_AT_FIND,
+                           THROW_AT_FIND_NSEC3,
+                           THROW_NEVER };
+    UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
+                                             default_qid, Name("foo.example."),
+                                             RRClass::IN(), RRType::TXT());
+    for (ThrowWhen* when(throws); *when != THROW_NEVER; ++when) {
+        createRequestPacket(request_message, IPPROTO_UDP);
+        setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, true);
+        processAndCheckSERVFAIL();
+        // To be sure, check same for non-isc-exceptions
+        createRequestPacket(request_message, IPPROTO_UDP);
+        setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, *when, false);
+        processAndCheckSERVFAIL();
+    }
+}
+
+// Throw isc::Exception in getClass(). (Currently?) getClass is not called
+// in the processMessage path, so this should result in a normal answer
+TEST_F(AuthSrvTest, queryWithInMemoryClientProxyGetClass) {
+    createDataFromFile("nsec3query_nodnssec_fromWire.wire");
+    setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_AT_GET_CLASS, true);
+
+    // getClass is not called so it should just answer
+    server.processMessage(*io_message, *parse_message, *response_obuffer,
+                          &dnsserv);
+
+    EXPECT_TRUE(dnsserv.hasAnswer());
+    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
+                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 2, 1);
+}
+
+TEST_F(AuthSrvTest, queryWithThrowingInToWire) {
+    // Set up a faked data source.  It will return an empty RRset for the
+    // query.
+    ConstRRsetPtr empty_rrset(new RRset(Name("foo.example"),
+                                        RRClass::IN(), RRType::TXT(),
+                                        RRTTL(0)));
+    setupThrow(&server, CONFIG_INMEMORY_EXAMPLE, THROW_NEVER, true,
+               empty_rrset);
+
+    // Repeat the query processing two times.  Due to the faked RRset,
+    // toWire() should throw, and it should result in SERVFAIL.
+    OutputBufferPtr orig_buffer;
+    for (int i = 0; i < 2; ++i) {
+        UnitTestUtil::createDNSSECRequestMessage(request_message, opcode,
+                                                 default_qid,
+                                                 Name("foo.example."),
+                                                 RRClass::IN(), RRType::TXT());
+        createRequestPacket(request_message, IPPROTO_UDP);
+        server.processMessage(*io_message, *parse_message, *response_obuffer,
+                              &dnsserv);
+        headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
+                    opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+
+        // Make a backup of the original buffer for latest tests and replace
+        // it with a new one
+        if (!orig_buffer) {
+            orig_buffer = response_obuffer;
+            response_obuffer.reset(new OutputBuffer(0));
+        }
+        request_message.clear(Message::RENDER);
+        parse_message->clear(Message::PARSE);
+    }
+
+    // Now check if the original buffer is intact
+    parse_message->clear(Message::PARSE);
+    InputBuffer ibuffer(orig_buffer->getData(), orig_buffer->getLength());
+    parse_message->fromWire(ibuffer);
+    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
+                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
+}
+
 }
diff --git a/src/bin/auth/tests/command_unittest.cc b/src/bin/auth/tests/command_unittest.cc
index 087ce81..bcaf4b1 100644
--- a/src/bin/auth/tests/command_unittest.cc
+++ b/src/bin/auth/tests/command_unittest.cc
@@ -14,6 +14,8 @@
 
 #include <config.h>
 
+#include "datasrc_util.h"
+
 #include <auth/auth_srv.h>
 #include <auth/auth_config.h>
 #include <auth/command.h>
@@ -49,8 +51,11 @@ using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::datasrc;
 using namespace isc::config;
+using namespace isc::testutils;
+using namespace isc::auth::unittest;
 
 namespace {
+
 class AuthCommandTest : public ::testing::Test {
 protected:
     AuthCommandTest() :
@@ -176,23 +181,23 @@ zoneChecks(AuthSrv& server) {
     EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::A()).code);
+              find(Name("ns.test1.example"), RRType::A())->code);
     EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::AAAA()).code);
+              find(Name("ns.test1.example"), RRType::AAAA())->code);
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::A()).code);
+              find(Name("ns.test2.example"), RRType::A())->code);
     EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::AAAA()).code);
+              find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
 
 void
 configureZones(AuthSrv& server) {
-    ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR "/test1.zone.in "
+    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test1.zone.in "
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR "/test2.zone.in "
+    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR "/test2.zone.in "
                         TEST_DATA_BUILDDIR "/test2.zone.copied"));
     configureAuthServer(server, Element::fromJSON(
                             "{\"datasources\": "
@@ -213,28 +218,28 @@ newZoneChecks(AuthSrv& server) {
     EXPECT_TRUE(server.getInMemoryClient(RRClass::IN()));
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::A()).code);
+              find(Name("ns.test1.example"), RRType::A())->code);
     // now test1.example should have ns/AAAA
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test1.example")).zone_finder->
-              find(Name("ns.test1.example"), RRType::AAAA()).code);
+              find(Name("ns.test1.example"), RRType::AAAA())->code);
 
     // test2.example shouldn't change
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::A()).code);
+              find(Name("ns.test2.example"), RRType::A())->code);
     EXPECT_EQ(ZoneFinder::NXRRSET, server.getInMemoryClient(RRClass::IN())->
               findZone(Name("ns.test2.example")).zone_finder->
-              find(Name("ns.test2.example"), RRType::AAAA()).code);
+              find(Name("ns.test2.example"), RRType::AAAA())->code);
 }
 
 TEST_F(AuthCommandTest, loadZone) {
     configureZones(server_);
 
-    ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
+    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
                         "/test1-new.zone.in "
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
-    ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
+    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
                         "/test2-new.zone.in "
                         TEST_DATA_BUILDDIR "/test2.zone.copied"));
 
@@ -245,10 +250,133 @@ TEST_F(AuthCommandTest, loadZone) {
     newZoneChecks(server_);
 }
 
+// This test uses dynamic load of a data source module, and won't work when
+// statically linked.
+TEST_F(AuthCommandTest,
+#ifdef USE_STATIC_LINK
+       DISABLED_loadZoneSQLite3
+#else
+       loadZoneSQLite3
+#endif
+    )
+{
+    const char* const SPEC_FILE = AUTH_OBJ_DIR "/auth.spec";
+
+    // Prepare the database first
+    const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
+    const string bad_db = TEST_DATA_BUILDDIR "/does-not-exist.sqlite3";
+    stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
+    createSQLite3DB(RRClass::IN(), Name("example.org"), test_db.c_str(), ss);
+
+    // Then store a config of the zone to the auth server
+    // This omits many config options of the auth server, but these are
+    // not read now.
+    isc::testutils::MockSession session;
+    // The session should not take care of anything or start anything, we
+    // need it only to hold the config we're going to put into it.
+    ModuleCCSession module_session(SPEC_FILE, session, NULL, NULL, false,
+                                  false);
+    // This describes the data source in the configuration
+    const ElementPtr
+        map(Element::fromJSON("{\"datasources\": ["
+                              "  {"
+                              "    \"type\": \"memory\","
+                              "    \"zones\": ["
+                              "      {"
+                              "        \"origin\": \"example.org\","
+                              "        \"file\": \"" + test_db + "\","
+                              "        \"filetype\": \"sqlite3\""
+                              "      }"
+                              "    ]"
+                              "  }"
+                              "]}"));
+    module_session.setLocalConfig(map);
+    server_.setConfigSession(&module_session);
+
+    // The loadzone command needs the zone to be already loaded, because
+    // it is used for reloading only
+    AuthSrv::InMemoryClientPtr dsrc(new InMemoryClient());
+    dsrc->addZone(ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
+                                                       Name("example.org"))));
+    server_.setInMemoryClient(RRClass::IN(), dsrc);
+
+    // Now send the command to reload it
+    result_ = execAuthServerCommand(server_, "loadzone",
+        Element::fromJSON("{\"origin\": \"example.org\"}"));
+    checkAnswer(0);
+
+    // Get the zone and look if there are data in it (the original one was
+    // empty)
+    ASSERT_TRUE(server_.getInMemoryClient(RRClass::IN()));
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+              findZone(Name("example.org")).zone_finder->
+              find(Name("example.org"), RRType::SOA())->code);
+
+    // Some error cases. First, the zone has no configuration.
+    dsrc->addZone(ZoneFinderPtr(new InMemoryZoneFinder(RRClass::IN(),
+                                                       Name("example.com"))));
+    result_ = execAuthServerCommand(server_, "loadzone",
+        Element::fromJSON("{\"origin\": \"example.com\"}"));
+    checkAnswer(1);
+
+    // The previous zone is not hurt in any way
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+              findZone(Name("example.org")).zone_finder->
+              find(Name("example.org"), RRType::SOA())->code);
+
+    module_session.setLocalConfig(Element::fromJSON("{\"datasources\": []}"));
+    result_ = execAuthServerCommand(server_, "loadzone",
+        Element::fromJSON("{\"origin\": \"example.org\"}"));
+    checkAnswer(1);
+
+    // The previous zone is not hurt in any way
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+              findZone(Name("example.org")).zone_finder->
+              find(Name("example.org"), RRType::SOA())->code);
+    // Configure an unreadable zone. Should fail, but leave the original zone
+    // data there
+    const ElementPtr
+        mapBad(Element::fromJSON("{\"datasources\": ["
+                                 "  {"
+                                 "    \"type\": \"memory\","
+                                 "    \"zones\": ["
+                                 "      {"
+                                 "        \"origin\": \"example.org\","
+                                 "        \"file\": \"" + bad_db + "\","
+                                 "        \"filetype\": \"sqlite3\""
+                                 "      }"
+                                 "    ]"
+                                 "  }"
+                                 "]}"));
+    module_session.setLocalConfig(mapBad);
+    result_ = execAuthServerCommand(server_, "loadzone",
+        Element::fromJSON("{\"origin\": \"example.com\"}"));
+    checkAnswer(1);
+    // The previous zone is not hurt in any way
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+              findZone(Name("example.org")).zone_finder->
+              find(Name("example.org"), RRType::SOA())->code);
+
+    // Broken configuration (not valid against the spec)
+    const ElementPtr
+        broken(Element::fromJSON("{\"datasources\": ["
+                                 "  {"
+                                 "    \"type\": \"memory\","
+                                 "    \"zones\": [[]]"
+                                 "  }"
+                                 "]}"));
+    module_session.setLocalConfig(broken);
+    checkAnswer(1);
+    // The previous zone is not hurt in any way
+    EXPECT_EQ(ZoneFinder::SUCCESS, server_.getInMemoryClient(RRClass::IN())->
+              findZone(Name("example.org")).zone_finder->
+              find(Name("example.org"), RRType::SOA())->code);
+}
+
 TEST_F(AuthCommandTest, loadBrokenZone) {
     configureZones(server_);
 
-    ASSERT_EQ(0, system(INSTALL_PROG " " TEST_DATA_DIR
+    ASSERT_EQ(0, system(INSTALL_PROG " -c " TEST_DATA_DIR
                         "/test1-broken.zone.in "
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
     result_ = execAuthServerCommand(server_, "loadzone",
@@ -262,7 +390,7 @@ TEST_F(AuthCommandTest, loadUnreadableZone) {
     configureZones(server_);
 
     // install the zone file as unreadable
-    ASSERT_EQ(0, system(INSTALL_PROG " -m 000 " TEST_DATA_DIR
+    ASSERT_EQ(0, system(INSTALL_PROG " -c -m 000 " TEST_DATA_DIR
                         "/test1.zone.in "
                         TEST_DATA_BUILDDIR "/test1.zone.copied"));
     result_ = execAuthServerCommand(server_, "loadzone",
diff --git a/src/bin/auth/tests/common_unittest.cc b/src/bin/auth/tests/common_unittest.cc
index 9b18142..184988d 100644
--- a/src/bin/auth/tests/common_unittest.cc
+++ b/src/bin/auth/tests/common_unittest.cc
@@ -36,7 +36,7 @@ public:
         BOOST_FOREACH(const Environ &env, restoreEnviron) {
             if (env.second == NULL) {
                 EXPECT_EQ(0, unsetenv(env.first.c_str())) <<
-                    "Couldn't restore environment, results of other tests"
+                    "Couldn't restore environment, results of other tests "
                     "are uncertain";
             } else {
                 EXPECT_EQ(0, setenv(env.first.c_str(), env.second->c_str(),
diff --git a/src/bin/auth/tests/config_syntax_unittest.cc b/src/bin/auth/tests/config_syntax_unittest.cc
new file mode 100644
index 0000000..8caedfd
--- /dev/null
+++ b/src/bin/auth/tests/config_syntax_unittest.cc
@@ -0,0 +1,71 @@
+// 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 <cc/data.h>
+#include <config/module_spec.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::config;
+
+namespace {
+
+const char* const SPEC_FILE = AUTH_OBJ_DIR "/auth.spec";
+
+class AuthConfigSyntaxTest : public ::testing::Test {
+protected:
+    AuthConfigSyntaxTest() : mspec_(moduleSpecFromFile(SPEC_FILE))
+    {}
+    ModuleSpec mspec_;
+};
+
+TEST_F(AuthConfigSyntaxTest, inmemoryDefaultFileType) {
+    // filetype is optional
+    EXPECT_TRUE(
+        mspec_.validateConfig(
+            Element::fromJSON(
+                "{\"listen_on\": [], \"datasources\": "
+                "  [{\"type\": \"memory\", \"class\": \"IN\", "
+                "    \"zones\": [{\"origin\": \"example.com\","
+                "                 \"file\": \""
+                TEST_DATA_DIR "/example.zone\"}]}]}"), true));
+}
+
+TEST_F(AuthConfigSyntaxTest, inmemorySQLite3Backend) {
+    // Specifying non-default in-memory filetype
+    EXPECT_TRUE(
+        mspec_.validateConfig(
+            Element::fromJSON(
+                "{\"datasources\": "
+                "  [{\"type\": \"memory\","
+                "    \"zones\": [{\"origin\": \"example.com\","
+                "                 \"file\": \""
+                TEST_DATA_DIR "/example.zone\","
+                "                 \"filetype\": \"sqlite3\"}]}]}"), false));
+}
+
+TEST_F(AuthConfigSyntaxTest, badInmemoryFileType) {
+    // filetype must be a string
+    EXPECT_FALSE(
+        mspec_.validateConfig(
+            Element::fromJSON(
+                "{\"datasources\": "
+                "  [{\"type\": \"memory\","
+                "    \"zones\": [{\"origin\": \"example.com\","
+                "                 \"file\": \""
+                TEST_DATA_DIR "/example.zone\","
+                "                 \"filetype\": 42}]}]}"), false));
+}
+}
diff --git a/src/bin/auth/tests/config_unittest.cc b/src/bin/auth/tests/config_unittest.cc
index 973ab31..d471a53 100644
--- a/src/bin/auth/tests/config_unittest.cc
+++ b/src/bin/auth/tests/config_unittest.cc
@@ -21,6 +21,7 @@
 
 #include <cc/data.h>
 
+#include <datasrc/data_source.h>
 #include <datasrc/memory_datasrc.h>
 
 #include <xfr/xfrout_client.h>
@@ -29,21 +30,27 @@
 #include <auth/auth_config.h>
 #include <auth/common.h>
 
+#include "datasrc_util.h"
+
 #include <testutils/mockups.h>
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 
+#include <sstream>
+
+using namespace std;
 using namespace isc::dns;
 using namespace isc::data;
 using namespace isc::datasrc;
 using namespace isc::asiodns;
-using namespace isc::asiolink;
+using namespace isc::auth::unittest;
+using namespace isc::testutils;
 
 namespace {
 class AuthConfigTest : public ::testing::Test {
 protected:
     AuthConfigTest() :
-        dnss_(ios_, NULL, NULL, NULL),
+        dnss_(),
         rrclass(RRClass::IN()),
         server(true, xfrout),
         // The empty string is expected value of the parameter of
@@ -53,8 +60,7 @@ protected:
     {
         server.setDNSService(dnss_);
     }
-    IOService ios_;
-    DNSService dnss_;
+    MockDNSService dnss_;
     const RRClass rrclass;
     MockXfroutClient xfrout;
     AuthSrv server;
@@ -146,6 +152,14 @@ TEST_F(AuthConfigTest, invalidListenAddressConfig) {
 // Try setting addresses trough config
 TEST_F(AuthConfigTest, listenAddressConfig) {
     isc::testutils::portconfig::listenAddressConfig(server);
+
+    // listenAddressConfig should have attempted to create 4 DNS server
+    // objects: two IP addresses, TCP and UDP for each.  For UDP, the "SYNC_OK"
+    // option should have been specified.
+    EXPECT_EQ(2, dnss_.getTCPFdParams().size());
+    EXPECT_EQ(2, dnss_.getUDPFdParams().size());
+    EXPECT_EQ(DNSService::SERVER_SYNC_OK, dnss_.getUDPFdParams().at(0).options);
+    EXPECT_EQ(DNSService::SERVER_SYNC_OK, dnss_.getUDPFdParams().at(1).options);
 }
 
 class MemoryDatasrcConfigTest : public AuthConfigTest {
@@ -191,7 +205,56 @@ TEST_F(MemoryDatasrcConfigTest, addOneZone) {
     // Check it actually loaded something
     EXPECT_EQ(ZoneFinder::SUCCESS, server.getInMemoryClient(rrclass)->findZone(
         Name("ns.example.com.")).zone_finder->find(Name("ns.example.com."),
-        RRType::A()).code);
+        RRType::A())->code);
+}
+
+// This test uses dynamic load of a data source module, and won't work when
+// statically linked.
+TEST_F(MemoryDatasrcConfigTest,
+#ifdef USE_STATIC_LINK
+       DISABLED_addOneWithFiletypeSQLite3
+#else
+       addOneWithFiletypeSQLite3
+#endif
+    )
+{
+    const string test_db = TEST_DATA_BUILDDIR "/auth_test.sqlite3.copied";
+    stringstream ss("example.org. 3600 IN SOA . . 0 0 0 0 0\n");
+    createSQLite3DB(rrclass, Name("example.org"), test_db.c_str(), ss);
+
+    // In-memory with an SQLite3 data source as the backend.
+    parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.org\","
+                      "               \"file\": \""
+                      + test_db +  "\","
+                      "               \"filetype\": \"sqlite3\"}]}]"));
+    parser->commit();
+    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
+
+    // Failure case: the specified zone doesn't exist in the DB file.
+    delete parser;
+    parser = createAuthConfigParser(server, "datasources");
+    EXPECT_THROW(parser->build(
+                     Element::fromJSON(
+                         "[{\"type\": \"memory\","
+                         "  \"zones\": [{\"origin\": \"example.com\","
+                         "               \"file\": \""
+                         + test_db +  "\","
+                         "               \"filetype\": \"sqlite3\"}]}]")),
+                 DataSourceError);
+}
+
+TEST_F(MemoryDatasrcConfigTest, addOneWithFiletypeText) {
+    // Explicitly specifying "text" is okay.
+    parser->build(Element::fromJSON(
+                      "[{\"type\": \"memory\","
+                      "  \"zones\": [{\"origin\": \"example.com\","
+                      "               \"file\": \""
+                      TEST_DATA_DIR "/example.zone\","
+                      "               \"filetype\": \"text\"}]}]"));
+    parser->commit();
+    EXPECT_EQ(1, server.getInMemoryClient(rrclass)->getZoneCount());
 }
 
 TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
@@ -292,7 +355,7 @@ TEST_F(MemoryDatasrcConfigTest, remove) {
     EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
 }
 
-TEST_F(MemoryDatasrcConfigTest, adDuplicateZones) {
+TEST_F(MemoryDatasrcConfigTest, addDuplicateZones) {
     EXPECT_THROW(parser->build(
                      Element::fromJSON(
                          "[{\"type\": \"memory\","
@@ -306,6 +369,13 @@ TEST_F(MemoryDatasrcConfigTest, adDuplicateZones) {
 }
 
 TEST_F(MemoryDatasrcConfigTest, addBadZone) {
+    // origin and file are missing
+    EXPECT_THROW(parser->build(
+                     Element::fromJSON(
+                         "[{\"type\": \"memory\","
+                         "  \"zones\": [{}]}]")),
+                 AuthConfigError);
+
     // origin is missing
     EXPECT_THROW(parser->build(
                      Element::fromJSON(
@@ -313,6 +383,13 @@ TEST_F(MemoryDatasrcConfigTest, addBadZone) {
                          "  \"zones\": [{\"file\": \"example.zone\"}]}]")),
                  AuthConfigError);
 
+    // file is missing
+    EXPECT_THROW(parser->build(
+                     Element::fromJSON(
+                         "[{\"type\": \"memory\","
+                         "  \"zones\": [{\"origin\": \"example.com\"}]}]")),
+                 AuthConfigError);
+
     // missing zone file
     EXPECT_THROW(parser->build(
                      Element::fromJSON(
@@ -325,7 +402,7 @@ TEST_F(MemoryDatasrcConfigTest, addBadZone) {
                       "[{\"type\": \"memory\","
                       "  \"zones\": [{\"origin\": \"example..com\","
                       "               \"file\": \"example.zone\"}]}]")),
-                 EmptyLabel);
+                 AuthConfigError);
 
     // bogus RR class name
     EXPECT_THROW(parser->build(
diff --git a/src/bin/auth/tests/datasrc_util.cc b/src/bin/auth/tests/datasrc_util.cc
new file mode 100644
index 0000000..664ae8c
--- /dev/null
+++ b/src/bin/auth/tests/datasrc_util.cc
@@ -0,0 +1,77 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <cc/data.h>
+
+#include <datasrc/client.h>
+#include <datasrc/zone.h>
+#include <datasrc/factory.h>
+
+#include "datasrc_util.h"
+
+#include <boost/bind.hpp>
+
+#include <istream>
+
+#include <cstdlib>
+
+using namespace std;
+
+using namespace isc::dns;
+using namespace isc::data;
+using namespace isc::datasrc;
+
+namespace isc {
+namespace auth {
+namespace unittest {
+
+namespace {
+void
+addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
+    updater->addRRset(*rrset);
+}
+}
+
+void
+createSQLite3DB(RRClass zclass, const Name& zname,
+                const char* const db_file, istream& rr_stream)
+{
+    // We always begin with an empty template SQLite3 DB file and install
+    // the zone data from the zone file.
+    const char* const install_cmd_prefix = INSTALL_PROG " -c " TEST_DATA_DIR
+        "/rwtest.sqlite3 ";
+    const string install_cmd = string(install_cmd_prefix) + db_file;
+    if (system(install_cmd.c_str()) != 0) {
+        isc_throw(isc::Unexpected,
+                  "Error setting up; command failed: " << install_cmd);
+    }
+
+    DataSourceClientContainer container("sqlite3",
+                                        Element::fromJSON(
+                                            "{\"database_file\": \"" +
+                                            string(db_file) + "\"}"));
+    ZoneUpdaterPtr updater = container.getInstance().getUpdater(zname, true);
+    masterLoad(rr_stream, zname, zclass, boost::bind(addRRset, updater, _1));
+    updater->commit();
+}
+
+} // end of unittest
+} // end of auth
+} // end of isc
diff --git a/src/bin/auth/tests/datasrc_util.h b/src/bin/auth/tests/datasrc_util.h
new file mode 100644
index 0000000..07ebc0c
--- /dev/null
+++ b/src/bin/auth/tests/datasrc_util.h
@@ -0,0 +1,58 @@
+// 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 __AUTH_DATA_SOURCE_UTIL_H
+#define __AUTH_DATA_SOURCE_UTIL_H 1
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <istream>
+
+namespace isc {
+namespace auth {
+namespace unittest {
+
+/// \brief Create an SQLite3 database file for a given zone from a stream.
+///
+/// This function creates an SQLite3 DB file for the specified zone
+/// with specified content.  The zone will be created in the given
+/// SQLite3 database file.  The database file does not have to exist;
+/// this function will automatically create a new file for the test
+/// based on a template that only contains the necessary schema. If
+/// the given file already exists this function overrides the content
+/// (so basically the file must be an ephemeral one only for that test
+/// case).
+///
+/// The input stream must produce strings as the corresponding
+/// \c dns::masterLoad() function expects.
+///
+/// \param zclass The RR class of the zone
+/// \param zname The origin name of the zone
+/// \param db_file The SQLite3 data base file in which the zone data should be
+/// installed.
+/// \param rr_stream An input stream that produces zone data.
+void
+createSQLite3DB(dns::RRClass zclass, const dns::Name& zname,
+                const char* const db_file, std::istream& rr_stream);
+
+} // end of unittest
+} // end of auth
+} // end of isc
+
+#endif  // __AUTH_DATA_SOURCE_UTIL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 624db2f..63429ae 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -12,12 +12,13 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <map>
 #include <sstream>
 #include <vector>
-#include <map>
 
 #include <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
+#include <boost/static_assert.hpp>
 
 #include <exceptions/exceptions.h>
 
@@ -238,6 +239,10 @@ const char* const unsigned_delegation_optout_nsec_txt =
 const char* const bad_delegation_txt =
     "bad-delegation.example.com. 3600 IN NS ns.example.net.\n";
 
+// Delegation from an unsigned parent.  There's no DS, and there's no NSEC
+// or NSEC3 that proves it.
+const char* const nosec_delegation_txt =
+    "nosec-delegation.example.com. 3600 IN NS ns.nosec.example.net.\n";
 
 // A helper function that generates a textual representation of RRSIG RDATA
 // for the given covered type.  The resulting RRSIG may not necessarily make
@@ -313,7 +318,7 @@ public:
             unsigned_delegation_txt << unsigned_delegation_nsec_txt <<
             unsigned_delegation_optout_txt <<
             unsigned_delegation_optout_nsec_txt <<
-            bad_delegation_txt;
+            bad_delegation_txt << nosec_delegation_txt;
 
         masterLoad(zone_stream, origin_, rrclass_,
                    boost::bind(&MockZoneFinder::loadRRset, this, _1));
@@ -370,12 +375,14 @@ public:
     }
     virtual isc::dns::Name getOrigin() const { return (origin_); }
     virtual isc::dns::RRClass getClass() const { return (rrclass_); }
-    virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type,
-                            const FindOptions options = FIND_DEFAULT);
-    virtual FindResult findAll(const isc::dns::Name& name,
-                               std::vector<ConstRRsetPtr>& target,
-                               const FindOptions options = FIND_DEFAULT);
+    virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+                                      const isc::dns::RRType& type,
+                                      const FindOptions options =
+                                      FIND_DEFAULT);
+    virtual ZoneFinderContextPtr findAll(const isc::dns::Name& name,
+                                         std::vector<ConstRRsetPtr>& target,
+                                         const FindOptions options =
+                                         FIND_DEFAULT);
 
     virtual ZoneFinder::FindNSEC3Result
     findNSEC3(const Name& name, bool recursive);
@@ -397,8 +404,10 @@ public:
                        ConstRRsetPtr rrset)
     {
         nsec_name_ = nsec_name;
-        nsec_result_.reset(new ZoneFinder::FindResult(code, rrset,
-                                                      RESULT_NSEC_SIGNED));
+        nsec_context_.reset(new Context(*this,
+                                        FIND_DEFAULT, // a fake value
+                                        ResultContext(code, rrset,
+                                                      RESULT_NSEC_SIGNED)));
     }
 
     // Once called, the findNSEC3 will return the provided result for the next
@@ -435,6 +444,18 @@ public:
     ConstRRsetPtr dname_rrset_; // could be used as an arbitrary bogus RRset
     ConstRRsetPtr empty_nsec_rrset_;
 
+protected:
+    // A convenient shortcut.  Will also be used by further derived mocks.
+    ZoneFinderContextPtr createContext(FindOptions options,
+                                       Result code,
+                                       isc::dns::ConstRRsetPtr rrset,
+                                       FindResultFlags flags = RESULT_DEFAULT)
+    {
+        return (ZoneFinderContextPtr(
+                    new Context(*this, options,
+                                ResultContext(code, rrset, flags))));
+    }
+
 private:
     typedef map<RRType, ConstRRsetPtr> RRsetStore;
     typedef map<Name, RRsetStore> Domains;
@@ -505,7 +526,7 @@ private:
     bool use_nsec3_;
     // The following two will be used for faked NSEC cases
     Name nsec_name_;
-    boost::scoped_ptr<ZoneFinder::FindResult> nsec_result_;
+    ZoneFinderContextPtr nsec_context_;
     // The following two are for faking bad NSEC3 responses
     // Enabled when not NULL
     const FindNSEC3Result* nsec3_fake_;
@@ -533,12 +554,12 @@ substituteWild(const AbstractRRset& wild_rrset, const Name& real_name) {
     return (rrset);
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                         const FindOptions options)
 {
-    ZoneFinder::FindResult result(find(name, RRType::ANY(), options));
-    if (result.code == NXRRSET) {
+    ZoneFinderContextPtr result(find(name, RRType::ANY(), options));
+    if (result->code == NXRRSET) {
         const Domains::const_iterator found_domain = domains_.find(name);
         if (!found_domain->second.empty()) {
             for (RRsetStore::const_iterator found_rrset =
@@ -547,7 +568,10 @@ MockZoneFinder::findAll(const Name& name, std::vector<ConstRRsetPtr>& target,
                 // Insert RRs under the domain name into target
                 target.push_back(found_rrset->second);
             }
-            return (FindResult(SUCCESS, RRsetPtr()));
+            return (ZoneFinderContextPtr(
+                        new Context(*this, options,
+                                    ResultContext(SUCCESS, RRsetPtr()),
+                                    target)));
         }
     }
 
@@ -610,16 +634,16 @@ MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
     isc_throw(isc::Unexpected, "findNSEC3() isn't expected to fail");
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 MockZoneFinder::find(const Name& name, const RRType& type,
                      const FindOptions options)
 {
     // Emulating a broken zone: mandatory apex RRs are missing if specifically
     // configured so (which are rare cases).
     if (name == origin_ && type == RRType::SOA() && !has_SOA_) {
-        return (FindResult(NXDOMAIN, RRsetPtr()));
+        return (createContext(options, NXDOMAIN, RRsetPtr()));
     } else if (name == origin_ && type == RRType::NS() && !has_apex_NS_) {
-        return (FindResult(NXDOMAIN, RRsetPtr()));
+        return (createContext(options, NXDOMAIN, RRsetPtr()));
     }
 
     // Special case for names on or under a zone cut and under DNAME
@@ -634,11 +658,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         // handled just like an in-zone case below.  Others result in
         // DELEGATION.
         if (type != RRType::DS() || it->first != name) {
-            return (FindResult(DELEGATION, delegation_ns));
+            return (createContext(options, DELEGATION, delegation_ns));
         }
     } else if (name.compare(dname_name_).getRelation() ==
                NameComparisonResult::SUBDOMAIN) {
-        return (FindResult(DNAME, dname_rrset_));
+        return (createContext(options, DNAME, dname_rrset_));
     }
 
     // normal cases.  names are searched for only per exact-match basis
@@ -668,33 +692,36 @@ MockZoneFinder::find(const Name& name, const RRType& type,
                 }
                 rrset = noconst;
             }
-            return (FindResult(SUCCESS, rrset));
+            return (createContext(options, SUCCESS, rrset));
         }
 
         // Otherwise, if this domain name has CNAME, return it.
         found_rrset = found_domain->second.find(RRType::CNAME());
         if (found_rrset != found_domain->second.end()) {
-            return (FindResult(CNAME, found_rrset->second));
+            return (createContext(options, CNAME, found_rrset->second));
         }
 
         // Otherwise it's NXRRSET case...
         // ...but a special pathological case first:
         if (found_domain->first == bad_signed_delegation_name_ &&
             type == RRType::DS()) {
-            return (FindResult(NXDOMAIN, RRsetPtr()));
+            return (createContext(options, NXDOMAIN, RRsetPtr()));
         }
         // normal cases follow.
         if ((options & FIND_DNSSEC) != 0) {
             if (use_nsec3_) {
-                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+                return (createContext(options, NXRRSET, RRsetPtr(),
+                                      RESULT_NSEC3_SIGNED));
             }
             found_rrset = found_domain->second.find(RRType::NSEC());
             if (found_rrset != found_domain->second.end()) {
-                return (FindResult(NXRRSET, found_rrset->second,
-                                   RESULT_NSEC_SIGNED));
+                return (createContext(options, NXRRSET, found_rrset->second,
+                                      RESULT_NSEC_SIGNED));
             }
         }
-        return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC_SIGNED));
+        // If no NSEC is found or DNSSEC isn't specified, behave as if the
+        // zone is unsigned.
+        return (createContext(options, NXRRSET, RRsetPtr()));
     }
 
     // query name isn't found in our domains.
@@ -714,16 +741,17 @@ MockZoneFinder::find(const Name& name, const RRType& type,
         --domain;               // reset domain to the "previous name"
         if ((options & FIND_DNSSEC) != 0) {
             if (use_nsec3_) {
-                return (FindResult(NXRRSET, RRsetPtr(), RESULT_NSEC3_SIGNED));
+                return (createContext(options, NXRRSET, RRsetPtr(),
+                                      RESULT_NSEC3_SIGNED));
             }
             RRsetStore::const_iterator found_rrset =
                 (*domain).second.find(RRType::NSEC());
             if (found_rrset != (*domain).second.end()) {
-                return (FindResult(NXRRSET, found_rrset->second,
-                                   RESULT_NSEC_SIGNED));
+                return (createContext(options, NXRRSET, found_rrset->second,
+                                      RESULT_NSEC_SIGNED));
             }
         }
-        return (FindResult(NXRRSET, RRsetPtr()));
+        return (createContext(options, NXRRSET, RRsetPtr()));
     }
 
     // Another possibility is wildcard.  For simplicity we only check
@@ -746,39 +774,37 @@ MockZoneFinder::find(const Name& name, const RRType& type,
                         domain->second.find(type);
                     // Matched the QTYPE
                     if(found_rrset != domain->second.end()) {
-                        return (FindResult(SUCCESS,
-                                           substituteWild(
-                                               *found_rrset->second, name),
-                                           RESULT_WILDCARD |
-                                           (use_nsec3_ ?
-                                            RESULT_NSEC3_SIGNED :
-                                            RESULT_NSEC_SIGNED)));
+                        return (createContext(options,SUCCESS, substituteWild(
+                                                  *found_rrset->second, name),
+                                              RESULT_WILDCARD |
+                                              (use_nsec3_ ?
+                                               RESULT_NSEC3_SIGNED :
+                                               RESULT_NSEC_SIGNED)));
                     } else {
                         // No matched QTYPE, this case is for NXRRSET with
                         // WILDCARD
                         if (use_nsec3_) {
-                            return (FindResult(NXRRSET, RRsetPtr(),
-                                               RESULT_WILDCARD |
-                                               RESULT_NSEC3_SIGNED));
+                            return (createContext(options, NXRRSET, RRsetPtr(),
+                                                  RESULT_WILDCARD |
+                                                  RESULT_NSEC3_SIGNED));
                         }
                         const Name new_name =
                             Name("*").concatenate(wild_suffix);
                         found_rrset = domain->second.find(RRType::NSEC());
                         assert(found_rrset != domain->second.end());
-                        return (FindResult(NXRRSET,
-                                           substituteWild(
-                                               *found_rrset->second,
-                                               new_name),
-                                           RESULT_WILDCARD |
-                                           RESULT_NSEC_SIGNED));
+                        return (createContext(options, NXRRSET, substituteWild(
+                                                  *found_rrset->second,
+                                                  new_name),
+                                              RESULT_WILDCARD |
+                                              RESULT_NSEC_SIGNED));
                     }
                 } else {
                     // This is empty non terminal name case on wildcard.
                     const Name empty_name = Name("*").concatenate(wild_suffix);
                     if (use_nsec3_) {
-                        return (FindResult(NXRRSET, RRsetPtr(),
-                                           RESULT_WILDCARD |
-                                           RESULT_NSEC3_SIGNED));
+                        return (createContext(options, NXRRSET, RRsetPtr(),
+                                              RESULT_WILDCARD |
+                                              RESULT_NSEC3_SIGNED));
                     }
                     for (Domains::reverse_iterator it = domains_.rbegin();
                          it != domains_.rend();
@@ -787,13 +813,15 @@ MockZoneFinder::find(const Name& name, const RRType& type,
                         if ((*it).first < empty_name &&
                             (nsec_it = (*it).second.find(RRType::NSEC()))
                             != (*it).second.end()) {
-                            return (FindResult(NXRRSET, (*nsec_it).second,
-                                               RESULT_WILDCARD |
-                                               RESULT_NSEC_SIGNED));
+                            return (createContext(options, NXRRSET,
+                                                  (*nsec_it).second,
+                                                  RESULT_WILDCARD |
+                                                  RESULT_NSEC_SIGNED));
                         }
                     }
                 }
-                return (FindResult(NXRRSET, RRsetPtr(), RESULT_WILDCARD));
+                return (createContext(options, NXRRSET, RRsetPtr(),
+                                      RESULT_WILDCARD));
              }
         }
         const Name cnamewild_suffix("cnamewild.example.com");
@@ -804,11 +832,11 @@ MockZoneFinder::find(const Name& name, const RRType& type,
             RRsetStore::const_iterator found_rrset =
                 domain->second.find(RRType::CNAME());
             assert(found_rrset != domain->second.end());
-            return (FindResult(CNAME,
-                               substituteWild(*found_rrset->second, name),
-                               RESULT_WILDCARD |
-                               (use_nsec3_ ? RESULT_NSEC3_SIGNED :
-                                RESULT_NSEC_SIGNED)));
+            return (createContext(options, CNAME,
+                                  substituteWild(*found_rrset->second, name),
+                                  RESULT_WILDCARD |
+                                  (use_nsec3_ ? RESULT_NSEC3_SIGNED :
+                                   RESULT_NSEC_SIGNED)));
         }
     }
 
@@ -821,12 +849,13 @@ MockZoneFinder::find(const Name& name, const RRType& type,
     // than the origin)
     if ((options & FIND_DNSSEC) != 0) {
         if (use_nsec3_) {
-            return (FindResult(NXDOMAIN, RRsetPtr(), RESULT_NSEC3_SIGNED));
+            return (createContext(options, NXDOMAIN, RRsetPtr(),
+                                  RESULT_NSEC3_SIGNED));
         }
 
         // Emulate a broken DataSourceClient for some special names.
-        if (nsec_result_ && nsec_name_ == name) {
-            return (*nsec_result_);
+        if (nsec_context_ && nsec_name_ == name) {
+            return (nsec_context_);
         }
 
         // Normal case
@@ -839,12 +868,12 @@ MockZoneFinder::find(const Name& name, const RRType& type,
             if ((*it).first < name &&
                 (nsec_it = (*it).second.find(RRType::NSEC()))
                 != (*it).second.end()) {
-                return (FindResult(NXDOMAIN, (*nsec_it).second,
-                                   RESULT_NSEC_SIGNED));
+                return (createContext(options, NXDOMAIN, (*nsec_it).second,
+                                      RESULT_NSEC_SIGNED));
             }
         }
     }
-    return (FindResult(NXDOMAIN, RRsetPtr()));
+    return (createContext(options,NXDOMAIN, RRsetPtr()));
 }
 
 class QueryTest : public ::testing::Test {
@@ -880,6 +909,7 @@ protected:
     const qid_t qid;
     const uint16_t query_code;
     const string ns_addrs_and_sig_txt; // convenient shortcut
+    Query query;
 };
 
 // A wrapper to check resulting response message commonly used in
@@ -923,15 +953,31 @@ TEST_F(QueryTest, noZone) {
     // There's no zone in the memory datasource.  So the response should have
     // REFUSED.
     InMemoryClient empty_memory_client;
-    Query nozone_query(empty_memory_client, qname, qtype, response);
-    EXPECT_NO_THROW(nozone_query.process());
+    EXPECT_NO_THROW(query.process(empty_memory_client, qname, qtype,
+                                  response));
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
 TEST_F(QueryTest, exactMatch) {
-    Query query(memory_client, qname, qtype, response);
-    EXPECT_NO_THROW(query.process());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    // find match rrset
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+                  www_a_txt, zone_ns_txt, ns_addrs_txt);
+}
+
+TEST_F(QueryTest, exactMatchMultipleQueries) {
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
+    // find match rrset
+    responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
+                  www_a_txt, zone_ns_txt, ns_addrs_txt);
+
+    // clean up response for second query
+    response.clear(isc::dns::Message::RENDER);
+    response.setRcode(Rcode::NOERROR());
+    response.setOpcode(Opcode::QUERY());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
     // find match rrset
+    SCOPED_TRACE("Second query");
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
 }
@@ -940,8 +986,7 @@ TEST_F(QueryTest, exactMatchIgnoreSIG) {
     // Check that we do not include the RRSIG when not requested even when
     // we receive it from the data source.
     mock_finder->setIncludeRRSIGAnyway(true);
-    Query query(memory_client, qname, qtype, response);
-    EXPECT_NO_THROW(query.process());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -949,8 +994,8 @@ TEST_F(QueryTest, exactMatchIgnoreSIG) {
 
 TEST_F(QueryTest, dnssecPositive) {
     // Just like exactMatch, but the signatures should be included as well
-    Query query(memory_client, qname, qtype, response, true);
-    EXPECT_NO_THROW(query.process());
+    EXPECT_NO_THROW(query.process(memory_client, qname, qtype, response,
+                                  true));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
                   (www_a_txt + std::string("www.example.com. 3600 IN RRSIG "
@@ -968,8 +1013,9 @@ TEST_F(QueryTest, dnssecPositive) {
 TEST_F(QueryTest, exactAddrMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
-                          response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("noglue.example.com"),
+                                  qtype, response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 2,
                   "noglue.example.com. 3600 IN A 192.0.2.53\n", zone_ns_txt,
@@ -980,8 +1026,8 @@ TEST_F(QueryTest, exactAddrMatch) {
 TEST_F(QueryTest, apexNSMatch) {
     // find match rrset, omit authority data which has already been provided
     // in the answer section from the authority section.
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"), RRType::NS(),
-                          response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::NS(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
                   zone_ns_txt, NULL, ns_addrs_txt);
@@ -991,8 +1037,8 @@ TEST_F(QueryTest, apexNSMatch) {
 TEST_F(QueryTest, exactAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(Query(memory_client, Name("noglue.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("noglue.example.com"),
+                                  RRType::ANY(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 3, 2,
                   (string("noglue.example.com. 3600 IN A 192.0.2.53\n") +
@@ -1005,8 +1051,8 @@ TEST_F(QueryTest, exactAnyMatch) {
 TEST_F(QueryTest, apexAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 5, 0, 3,
                   (string(soa_txt) + string(zone_ns_txt) +
                    string(nsec_apex_txt)).c_str(),
@@ -1014,23 +1060,23 @@ TEST_F(QueryTest, apexAnyMatch) {
 }
 
 TEST_F(QueryTest, mxANYMatch) {
-    EXPECT_NO_THROW(Query(memory_client, Name("mx.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("mx.example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 4, 3, 4,
                   (string(mx_txt) + string(nsec_mx_txt)).c_str(), zone_ns_txt,
                   (string(ns_addrs_txt) + string(www_a_txt)).c_str());
 }
 
 TEST_F(QueryTest, glueANYMatch) {
-    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("delegation.example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                   NULL, delegation_txt, ns_addrs_txt);
 }
 
 TEST_F(QueryTest, nodomainANY) {
-    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"),
-                          RRType::ANY(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                                  RRType::ANY(), response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
@@ -1042,23 +1088,35 @@ TEST_F(QueryTest, noApexNS) {
     // Disable apex NS record
     mock_finder->setApexNSFlag(false);
 
-    EXPECT_THROW(Query(memory_client, Name("noglue.example.com"), qtype,
-                       response).process(), Query::NoApexNS);
+    EXPECT_THROW(query.process(memory_client, Name("noglue.example.com"), qtype,
+                               response), Query::NoApexNS);
     // We don't look into the response, as it threw
 }
 
 TEST_F(QueryTest, delegation) {
-    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
-                          qtype, response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("delegation.example.com"),
+                                  qtype, response));
 
     responseCheck(response, Rcode::NOERROR(), 0, 0, 4, 3,
                   NULL, delegation_txt, ns_addrs_txt);
 }
 
+TEST_F(QueryTest, delegationWithDNSSEC) {
+    // Similar to the previous one, but with requesting DNSSEC.
+    // In this case the parent zone would behave as unsigned, so the result
+    // should be just like non DNSSEC delegation.
+    query.process(memory_client, Name("www.nosec-delegation.example.com"),
+                  qtype, response, true);
+
+    responseCheck(response, Rcode::NOERROR(), 0, 0, 1, 0,
+                  NULL, nosec_delegation_txt, NULL);
+}
+
 TEST_F(QueryTest, secureDelegation) {
-    EXPECT_NO_THROW(Query(memory_client,
-                          Name("foo.signed-delegation.example.com"),
-                          qtype, response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("foo.signed-delegation.example.com"),
+                                  qtype, response, true));
 
     // Should now contain RRSIG and DS record as well.
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
@@ -1071,9 +1129,9 @@ TEST_F(QueryTest, secureDelegation) {
 }
 
 TEST_F(QueryTest, secureUnsignedDelegation) {
-    EXPECT_NO_THROW(Query(memory_client,
-                          Name("foo.unsigned-delegation.example.com"),
-                          qtype, response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("foo.unsigned-delegation.example.com"),
+                                  qtype, response, true));
 
     // Should now contain RRSIG and NSEC record as well.
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
@@ -1092,8 +1150,9 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3) {
     mock_finder->setNSEC3Flag(true);
     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
 
-    Query(memory_client, Name("foo.unsigned-delegation.example.com"),
-          qtype, response, true).process();
+    query.process(memory_client,
+                  Name("foo.unsigned-delegation.example.com"),
+                  qtype, response, true);
 
     // The response should contain the NS and matching NSEC3 with its RRSIG
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
@@ -1110,14 +1169,14 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
     // Similar to the previous case, but the delegation is an optout.
     mock_finder->setNSEC3Flag(true);
 
-    Query(memory_client, Name("foo.unsigned-delegation.example.com"),
-          qtype, response, true).process();
+    query.process(memory_client,
+                  Name("foo.unsigned-delegation.example.com"),
+                  qtype, response, true);
 
     // The response should contain the NS and the closest provable encloser
     // proof (and their RRSIGs).  The closest encloser is the apex (origin),
     // and with our faked hash the covering NSEC3 for the next closer
     // (= child zone name) is that for www.example.com.
-    cout << response << endl;
     responseCheck(response, Rcode::NOERROR(), 0, 0, 5, 0,
                   NULL,
                   (string(unsigned_delegation_txt) +
@@ -1135,19 +1194,22 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
 TEST_F(QueryTest, badSecureDelegation) {
     // Test whether exception is raised if DS query at delegation results in
     // something different than SUCCESS or NXRRSET
-    EXPECT_THROW(Query(memory_client, Name("bad-delegation.example.com"),
-                       qtype, response, true).process(), Query::BadDS);
+    EXPECT_THROW(query.process(memory_client,
+                               Name("bad-delegation.example.com"),
+                               qtype, response, true), Query::BadDS);
 
     // But only if DNSSEC is requested (it shouldn't even try to look for
     // the DS otherwise)
-    EXPECT_NO_THROW(Query(memory_client, Name("bad-delegation.example.com"),
-                          qtype, response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("bad-delegation.example.com"),
+                                  qtype, response));
 }
 
 
 TEST_F(QueryTest, nxdomain) {
-    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                          response).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("nxdomain.example.com"), qtype,
+                                  response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
@@ -1156,8 +1218,9 @@ TEST_F(QueryTest, nxdomainWithNSEC) {
     // NXDOMAIN with DNSSEC proof.  We should have SOA, NSEC that proves
     // NXDOMAIN and NSEC that proves nonexistence of matching wildcard,
     // as well as their RRSIGs.
-    EXPECT_NO_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                          response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("nxdomain.example.com"), qtype,
+                                  response, true));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1176,8 +1239,8 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
     // is derived from the next domain of the NSEC that proves NXDOMAIN, and
     // the NSEC to provide the non existence of wildcard is different from
     // the first NSEC.
-    Query(memory_client, Name("(.no.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("(.no.example.com"), qtype, response,
+                  true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1194,8 +1257,8 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
 TEST_F(QueryTest, nxdomainWithNSECDuplicate) {
     // See comments about nz_txt.  In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence of wildcard.
-    Query(memory_client, Name("nx.no.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("nx.no.example.com"), qtype, response,
+                  true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 4, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1211,8 +1274,8 @@ TEST_F(QueryTest, nxdomainBadNSEC1) {
     mock_finder->setNSECResult(Name("badnsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("badnsec.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("badnsec.example.com"),
+                               qtype, response, true),
                  std::bad_cast);
 }
 
@@ -1221,8 +1284,8 @@ TEST_F(QueryTest, nxdomainBadNSEC2) {
     mock_finder->setNSECResult(Name("emptynsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("emptynsec.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("emptynsec.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
@@ -1231,8 +1294,8 @@ TEST_F(QueryTest, nxdomainBadNSEC3) {
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
@@ -1240,8 +1303,8 @@ TEST_F(QueryTest, nxdomainBadNSEC4) {
     // "no-wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
@@ -1251,8 +1314,8 @@ TEST_F(QueryTest, nxdomainBadNSEC5) {
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
     // This is a bit odd, but we'll simply include the returned RRset.
-    Query(memory_client, Name("nxdomain.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("nxdomain.example.com"), qtype,
+                  response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
                          string("example.com. 3600 IN RRSIG ") +
@@ -1271,14 +1334,14 @@ TEST_F(QueryTest, nxdomainBadNSEC6) {
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response, true),
                  Query::BadNSEC);
 }
 
 TEST_F(QueryTest, nxrrset) {
-    EXPECT_NO_THROW(Query(memory_client, Name("www.example.com"),
-                          RRType::TXT(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("www.example.com"),
+                                  RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
@@ -1287,8 +1350,8 @@ TEST_F(QueryTest, nxrrset) {
 TEST_F(QueryTest, nxrrsetWithNSEC) {
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC that proves the
     // NXRRSET and their RRSIGs.
-    Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("www.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1308,8 +1371,8 @@ TEST_F(QueryTest, emptyNameWithNSEC) {
     // exact match), so we only need one NSEC.
     // From the point of the Query::process(), this is actually no different
     // from the other NXRRSET case, but we check that explicitly just in case.
-    Query(memory_client, Name("no.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("no.example.com"), RRType::A(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1324,8 +1387,8 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
     // NXRRSET with DNSSEC proof requested, but there's no NSEC at that node.
     // This is an unexpected event (if the zone is supposed to be properly
     // signed with NSECs), but we accept and ignore the oddity.
-    Query(memory_client, Name("nonsec.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("nonsec.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1336,8 +1399,8 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
 TEST_F(QueryTest, wildcardNSEC) {
     // The qname matches *.wild.example.com.  The response should contain
     // an NSEC that proves the non existence of a closer name.
-    Query(memory_client, Name("www.wild.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("www.wild.example.com"), RRType::A(),
+                  response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
                   (string(wild_txt).replace(0, 1, "www") +
                    string("www.wild.example.com. 3600 IN RRSIG ") +
@@ -1356,8 +1419,8 @@ TEST_F(QueryTest, wildcardNSEC) {
 TEST_F(QueryTest, CNAMEwildNSEC) {
     // Similar to the previous case, but the matching wildcard record is
     // CNAME.
-    Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
-          response, true).process();
+    query.process(memory_client, Name("www.cnamewild.example.com"),
+                  RRType::A(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(cnamewild_txt).replace(0, 1, "www") +
                    string("www.cnamewild.example.com. 3600 IN RRSIG ") +
@@ -1379,8 +1442,8 @@ TEST_F(QueryTest, wildcardNSEC3) {
     // of identifying the next closer name.
     mock_finder->addRecord(nsec3_atwild_txt);
 
-    Query(memory_client, Name("x.y.wild.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("x.y.wild.example.com"), RRType::A(),
+                  response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 6, 6,
                   (string(wild_txt).replace(0, 1, "x.y") +
                    string("x.y.wild.example.com. 3600 IN RRSIG ") +
@@ -1404,8 +1467,8 @@ TEST_F(QueryTest, CNAMEwildNSEC3) {
     mock_finder->setNSEC3Flag(true);
     mock_finder->addRecord(nsec3_atcnamewild_txt);
 
-    Query(memory_client, Name("www.cnamewild.example.com"), RRType::A(),
-          response, true).process();
+    query.process(memory_client, Name("www.cnamewild.example.com"),
+                  RRType::A(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(cnamewild_txt).replace(0, 1, "www") +
                    string("www.cnamewild.example.com. 3600 IN RRSIG ") +
@@ -1427,9 +1490,9 @@ TEST_F(QueryTest, badWildcardNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
-                 isc::InvalidParameter);
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
+                 Query::BadNSEC3);
 }
 
 TEST_F(QueryTest, badWildcardProof1) {
@@ -1438,8 +1501,8 @@ TEST_F(QueryTest, badWildcardProof1) {
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
@@ -1447,8 +1510,8 @@ TEST_F(QueryTest, badWildcardProof2) {
     // "wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
@@ -1457,8 +1520,8 @@ TEST_F(QueryTest, badWildcardProof3) {
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(Query(memory_client, Name("www.wild.example.com"),
-                       RRType::A(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www.wild.example.com"),
+                               RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
@@ -1466,8 +1529,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
     // NXRRSET on WILDCARD with DNSSEC proof.  We should have SOA, NSEC that
     // proves the NXRRSET and their RRSIGs. In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence RRSETs of wildcard.
-    Query(memory_client, Name("www.wild.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("www.wild.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1483,8 +1546,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
     // proves the NXRRSET and their RRSIGs. In this case we need two NSEC RRs,
     // one proves NXDOMAIN and the other proves non existence RRSETs of
     // wildcard.
-    Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(),
-          response, true).process();
+    query.process(memory_client, Name("www1.uwild.example.com"),
+                  RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1506,8 +1569,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
     mock_finder->addRecord(nsec3_uwild_txt);
     mock_finder->setNSEC3Flag(true);
 
-    Query(memory_client, Name("www1.uwild.example.com"), RRType::TXT(),
-          response, true).process();
+    query.process(memory_client, Name("www1.uwild.example.com"),
+                  RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
@@ -1540,10 +1603,9 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Collision) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    // Message::addRRset() will detect it and throw InvalidParameter.
-    EXPECT_THROW(Query(memory_client, Name("www1.uwild.example.com"),
-                       RRType::TXT(), response, true).process(),
-                 isc::InvalidParameter);
+    EXPECT_THROW(query.process(memory_client, Name("www1.uwild.example.com"),
+                               RRType::TXT(), response, true),
+                 Query::BadNSEC3);
 }
 
 TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
@@ -1558,8 +1620,8 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
     mock_finder->addRecord(nsec3_wild_txt);
     mock_finder->addRecord(nsec3_uwild_txt);
 
-    EXPECT_THROW(Query(memory_client, Name("www1.uwild.example.com"),
-                       RRType::TXT(), response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("www1.uwild.example.com"),
+                               RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
@@ -1567,8 +1629,8 @@ TEST_F(QueryTest, wildcardEmptyWithNSEC) {
     // Empty WILDCARD with DNSSEC proof.  We should have SOA, NSEC that proves
     // the NXDOMAIN and their RRSIGs. In this case we need two NSEC RRs,
     // one proves NXDOMAIN and the other proves non existence wildcard.
-    Query(memory_client, Name("a.t.example.com"), RRType::A(), response,
-          true).process();
+    query.process(memory_client, Name("a.t.example.com"), RRType::A(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -1591,19 +1653,19 @@ TEST_F(QueryTest, noSOA) {
     mock_finder->setSOAFlag(false);
 
     // The NX Domain
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"),
-                       qtype, response).process(), Query::NoSOA);
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               qtype, response), Query::NoSOA);
     // Of course, we don't look into the response, as it throwed
 
     // NXRRSET
-    EXPECT_THROW(Query(memory_client, Name("nxrrset.example.com"),
-                       qtype, response).process(), Query::NoSOA);
+    EXPECT_THROW(query.process(memory_client, Name("nxrrset.example.com"),
+                               qtype, response), Query::NoSOA);
 }
 
 TEST_F(QueryTest, noMatchZone) {
     // there's a zone in the memory datasource but it doesn't match the qname.
     // should result in REFUSED.
-    Query(memory_client, Name("example.org"), qtype, response).process();
+    query.process(memory_client, Name("example.org"), qtype, response);
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
@@ -1614,8 +1676,8 @@ TEST_F(QueryTest, noMatchZone) {
  * A record, other to unknown out of zone one.
  */
 TEST_F(QueryTest, MX) {
-    Query(memory_client, Name("mx.example.com"), RRType::MX(),
-          response).process();
+    query.process(memory_client, Name("mx.example.com"), RRType::MX(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
                   mx_txt, NULL,
@@ -1628,8 +1690,8 @@ TEST_F(QueryTest, MX) {
  * This should not trigger the additional processing for the exchange.
  */
 TEST_F(QueryTest, MXAlias) {
-    Query(memory_client, Name("cnamemx.example.com"), RRType::MX(),
-          response).process();
+    query.process(memory_client, Name("cnamemx.example.com"), RRType::MX(),
+                  response);
 
     // there shouldn't be no additional RRs for the exchanges (we have 3
     // RRs for the NS).  The normal MX case is tested separately so we don't
@@ -1648,8 +1710,8 @@ TEST_F(QueryTest, MXAlias) {
  * returned.
  */
 TEST_F(QueryTest, CNAME) {
-    Query(memory_client, Name("cname.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_txt, NULL, NULL);
@@ -1658,8 +1720,8 @@ TEST_F(QueryTest, CNAME) {
 TEST_F(QueryTest, explicitCNAME) {
     // same owner name as the CNAME test but explicitly query for CNAME RR.
     // expect the same response as we don't provide a full chain yet.
-    Query(memory_client, Name("cname.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::CNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_txt, zone_ns_txt, ns_addrs_txt);
@@ -1670,8 +1732,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
     // note: with chaining, what should be expected is not trivial:
     // BIND 9 returns the CNAME in answer and SOA in authority, no additional.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional.
-    Query(memory_client, Name("cname.example.com"), RRType::TXT(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::TXT(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_txt, NULL, NULL);
@@ -1679,8 +1741,8 @@ TEST_F(QueryTest, CNAME_NX_RRSET) {
 
 TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
     // same owner name as the NXRRSET test but explicitly query for CNAME RR.
-    Query(memory_client, Name("cname.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cname.example.com"), RRType::CNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_txt, zone_ns_txt, ns_addrs_txt);
@@ -1693,8 +1755,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
     // RCODE being NXDOMAIN.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional,
     // RCODE being NOERROR.
-    Query(memory_client, Name("cnamenxdom.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("cnamenxdom.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_nxdom_txt, NULL, NULL);
@@ -1702,8 +1764,8 @@ TEST_F(QueryTest, CNAME_NX_DOMAIN) {
 
 TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
     // same owner name as the NXDOMAIN test but explicitly query for CNAME RR.
-    Query(memory_client, Name("cnamenxdom.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cnamenxdom.example.com"),
+                  RRType::CNAME(), response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_nxdom_txt, zone_ns_txt, ns_addrs_txt);
@@ -1718,8 +1780,8 @@ TEST_F(QueryTest, CNAME_OUT) {
      * Then the same test should be done with .org included there and
      * see what it does (depends on what we want to do)
      */
-    Query(memory_client, Name("cnameout.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("cnameout.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_out_txt, NULL, NULL);
@@ -1727,8 +1789,8 @@ TEST_F(QueryTest, CNAME_OUT) {
 
 TEST_F(QueryTest, explicitCNAME_OUT) {
     // same owner name as the OUT test but explicitly query for CNAME RR.
-    Query(memory_client, Name("cnameout.example.com"), RRType::CNAME(),
-        response).process();
+    query.process(memory_client, Name("cnameout.example.com"), RRType::CNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_out_txt, zone_ns_txt, ns_addrs_txt);
@@ -1743,8 +1805,8 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
  * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
  */
 TEST_F(QueryTest, DNAME) {
-    Query(memory_client, Name("www.dname.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("www.dname.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
         (string(dname_txt) + synthetized_cname_txt).c_str(),
@@ -1759,8 +1821,8 @@ TEST_F(QueryTest, DNAME) {
  * DNAME.
  */
 TEST_F(QueryTest, DNAME_ANY) {
-    Query(memory_client, Name("www.dname.example.com"), RRType::ANY(),
-        response).process();
+    query.process(memory_client, Name("www.dname.example.com"), RRType::ANY(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
         (string(dname_txt) + synthetized_cname_txt).c_str(), NULL, NULL);
@@ -1768,8 +1830,8 @@ TEST_F(QueryTest, DNAME_ANY) {
 
 // Test when we ask for DNAME explicitly, it does no synthetizing.
 TEST_F(QueryTest, explicitDNAME) {
-    Query(memory_client, Name("dname.example.com"), RRType::DNAME(),
-        response).process();
+    query.process(memory_client, Name("dname.example.com"), RRType::DNAME(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         dname_txt, zone_ns_txt, ns_addrs_txt);
@@ -1780,8 +1842,8 @@ TEST_F(QueryTest, explicitDNAME) {
  * the CNAME, it should return the RRset.
  */
 TEST_F(QueryTest, DNAME_A) {
-    Query(memory_client, Name("dname.example.com"), RRType::A(),
-        response).process();
+    query.process(memory_client, Name("dname.example.com"), RRType::A(),
+                  response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         dname_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -1792,8 +1854,8 @@ TEST_F(QueryTest, DNAME_A) {
  * It should not synthetize the CNAME.
  */
 TEST_F(QueryTest, DNAME_NX_RRSET) {
-    EXPECT_NO_THROW(Query(memory_client, Name("dname.example.com"),
-        RRType::TXT(), response).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("dname.example.com"),
+                    RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
         NULL, soa_txt, NULL, mock_finder->getOrigin());
@@ -1812,8 +1874,8 @@ TEST_F(QueryTest, LongDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(Query(memory_client, longname, RRType::A(),
-        response).process());
+    EXPECT_NO_THROW(query.process(memory_client, longname, RRType::A(),
+                    response));
 
     responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
         dname_txt, NULL, NULL);
@@ -1831,8 +1893,8 @@ TEST_F(QueryTest, MaxLenDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(Query(memory_client, longname, RRType::A(),
-        response).process());
+    EXPECT_NO_THROW(query.process(memory_client, longname, RRType::A(),
+                    response));
 
     // Check the answer is OK
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
@@ -1963,23 +2025,23 @@ public:
         MockZoneFinder(), origin_(origin), have_ds_(have_ds)
     {}
     virtual isc::dns::Name getOrigin() const { return (origin_); }
-    virtual FindResult find(const isc::dns::Name&,
-                            const isc::dns::RRType& type,
-                            const FindOptions)
+    virtual ZoneFinderContextPtr find(const isc::dns::Name&,
+                                      const isc::dns::RRType& type,
+                                      const FindOptions options)
     {
         if (type == RRType::SOA()) {
             RRsetPtr soa = textToRRset(origin_.toText() + " 3600 IN SOA . . "
                                        "0 0 0 0 0\n", origin_);
             soa->addRRsig(RdataPtr(new generic::RRSIG(
                                        getCommonRRSIGText("SOA"))));
-            return (FindResult(SUCCESS, soa));
+            return (createContext(options, SUCCESS, soa));
         }
         if (type == RRType::NS()) {
             RRsetPtr ns = textToRRset(origin_.toText() + " 3600 IN NS " +
                                       Name("ns").concatenate(origin_).toText());
             ns->addRRsig(RdataPtr(new generic::RRSIG(
                                       getCommonRRSIGText("NS"))));
-            return (FindResult(SUCCESS, ns));
+            return (createContext(options, SUCCESS, ns));
         }
         if (type == RRType::DS()) {
             if (have_ds_) {
@@ -1989,7 +2051,7 @@ public:
                                           "3CD34AC1AFE51DE");
                 ds->addRRsig(RdataPtr(new generic::RRSIG(
                                           getCommonRRSIGText("DS"))));
-                return (FindResult(SUCCESS, ds));
+                return (createContext(options, SUCCESS, ds));
             } else {
                 RRsetPtr nsec = textToRRset(origin_.toText() +
                                             " 3600 IN NSEC " +
@@ -1997,12 +2059,13 @@ public:
                                             " SOA NSEC RRSIG");
                 nsec->addRRsig(RdataPtr(new generic::RRSIG(
                                             getCommonRRSIGText("NSEC"))));
-                return (FindResult(NXRRSET, nsec, RESULT_NSEC_SIGNED));
+                return (createContext(options, NXRRSET, nsec,
+                                      RESULT_NSEC_SIGNED));
             }
         }
 
         // Returning NXDOMAIN is not correct, but doesn't matter for our tests.
-        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+        return (createContext(options, NXDOMAIN, ConstRRsetPtr()));
     }
 private:
     const Name origin_;
@@ -2016,8 +2079,9 @@ TEST_F(QueryTest, dsAboveDelegation) {
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("delegation.example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
                   (string(delegation_ds_txt) + "\n" +
@@ -2039,9 +2103,9 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(Query(memory_client,
-                          Name("unsigned-delegation.example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client,
+                                  Name("unsigned-delegation.example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) +
@@ -2057,8 +2121,8 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 // when it happens to be sent to the child zone, as described in RFC 4035,
 // section 3.1.4.1. The example is inspired by the B.8. example from the RFC.
 TEST_F(QueryTest, dsBelowDelegation) {
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2074,8 +2138,8 @@ TEST_F(QueryTest, dsBelowDelegation) {
 // In our implementation NSEC/NSEC3 isn't attached in this case.
 TEST_F(QueryTest, dsBelowDelegationWithDS) {
     mock_finder->addRecord(zone_ds_txt); // add the DS to the child's apex
-    EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
-                          RRType::DS(), response, true).process());
+    EXPECT_NO_THROW(query.process(memory_client, Name("example.com"),
+                                  RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2087,16 +2151,16 @@ TEST_F(QueryTest, dsBelowDelegationWithDS) {
 // server.  It should just like the "noZone" test case, but DS query involves
 // special processing, so we test it explicitly.
 TEST_F(QueryTest, dsNoZone) {
-    Query(memory_client, Name("example"), RRType::DS(), response,
-          true).process();
+    query.process(memory_client, Name("example"), RRType::DS(), response,
+                  true);
     responseCheck(response, Rcode::REFUSED(), 0, 0, 0, 0, NULL, NULL, NULL);
 }
 
 // DS query for a "grandchild" zone.  This should result in normal
 // delegation (unless this server also has authority of the grandchild zone).
 TEST_F(QueryTest, dsAtGrandParent) {
-    Query(memory_client, Name("grand.delegation.example.com"), RRType::DS(),
-          response, true).process();
+    query.process(memory_client, Name("grand.delegation.example.com"),
+                  RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), 0, 0, 6, 6, NULL,
                   (string(delegation_txt) + string(delegation_ds_txt) +
                    "delegation.example.com. 3600 IN RRSIG " +
@@ -2114,7 +2178,7 @@ TEST_F(QueryTest, dsAtGrandParentAndChild) {
     const Name childname("grand.delegation.example.com");
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(childname)));
-    Query(memory_client, childname, RRType::DS(), response, true).process();
+    query.process(memory_client, childname, RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" +
                    childname.toText() + " 3600 IN RRSIG " +
@@ -2132,8 +2196,8 @@ TEST_F(QueryTest, dsAtRoot) {
     // Pretend to be a root server.
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME())));
-    Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
-          true).process();
+    query.process(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+                  true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(". 3600 IN SOA . . 0 0 0 0 0\n") +
                    ". 3600 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" +
@@ -2149,8 +2213,8 @@ TEST_F(QueryTest, dsAtRootWithDS) {
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME(),
                                                       true)));
-    Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
-          true).process();
+    query.process(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+                  true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD"
                           "3CD34AC1AFE51DE\n") +
@@ -2166,8 +2230,8 @@ TEST_F(QueryTest, nxrrsetWithNSEC3) {
 
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC3 that proves the
     // NXRRSET and their RRSIGs.
-    Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
-          true).process();
+    query.process(memory_client, Name("www.example.com"), RRType::TXT(),
+                  response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
@@ -2189,8 +2253,9 @@ TEST_F(QueryTest, nxrrsetMissingNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(Query(memory_client, Name("www.example.com"), RRType::TXT(),
-                       response, true).process(), Query::BadNSEC3);
+    EXPECT_THROW(query.process(memory_client, Name("www.example.com"),
+                               RRType::TXT(), response, true),
+                 Query::BadNSEC3);
 }
 
 TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
@@ -2199,8 +2264,8 @@ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
 
     // This delegation has no DS, but does have a matching NSEC3 record
     // (See RFC5155 section 7.2.4)
-    Query(memory_client, Name("unsigned-delegation.example.com."),
-          RRType::DS(), response, true).process();
+    query.process(memory_client, Name("unsigned-delegation.example.com."),
+                  RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
@@ -2221,8 +2286,8 @@ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
     // 'next closer' should have opt-out set, though that is not
     // actually checked)
     // (See RFC5155 section 7.2.4)
-    Query(memory_client, Name("unsigned-delegation-optout.example.com."),
-          RRType::DS(), response, true).process();
+    query.process(memory_client, Name("unsigned-delegation-optout.example.com."),
+                  RRType::DS(), response, true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("SOA") + "\n" +
@@ -2248,8 +2313,8 @@ TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
     // This will be the covering NSEC3 for the possible wildcard
     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
 
-    Query(memory_client, Name("nxdomain.example.com"), qtype,
-          response, true).process();
+    query.process(memory_client, Name("nxdomain.example.com"), qtype,
+                  response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
                   (string(soa_txt) +
@@ -2283,10 +2348,9 @@ TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    // Message::addRRset() will detect it and throw InvalidParameter.
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"),
-                       RRType::TXT(), response, true).process(),
-                 isc::InvalidParameter);
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"),
+                               RRType::TXT(), response, true),
+                 Query::BadNSEC3);
 }
 
 TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
@@ -2303,8 +2367,8 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3, &wname);
 
-    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"), qtype,
-                       response, true).process(),
+    EXPECT_THROW(query.process(memory_client, Name("nxdomain.example.com"), qtype,
+                               response, true),
                  Query::BadNSEC3);
 }
 
@@ -2313,11 +2377,152 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
 // clean them up.
 TEST_F(QueryTest, emptyNameWithNSEC3) {
     mock_finder->setNSEC3Flag(true);
-    ZoneFinder::FindResult result = mock_finder->find(
+    ZoneFinderContextPtr result = mock_finder->find(
         Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
-    EXPECT_FALSE(result.rrset);
-    EXPECT_TRUE(result.isNSEC3Signed());
-    EXPECT_FALSE(result.isWildcard());
+    EXPECT_EQ(ZoneFinder::NXRRSET, result->code);
+    EXPECT_FALSE(result->rrset);
+    EXPECT_TRUE(result->isNSEC3Signed());
+    EXPECT_FALSE(result->isWildcard());
+}
+
+// Vector of RRsets used for the test.   Having this external to functions and
+// classes used for the testing simplifies the code.
+std::vector<RRsetPtr> rrset_vector;
+
+// Callback function for masterLoad.
+void
+loadRRsetVectorCallback(RRsetPtr rrsetptr) {
+    rrset_vector.push_back(rrsetptr);
+}
+
+// Load a set of RRsets into a vector for use in the duplicate RRset test.
+// They don't make a lot of sense as a zone, they are just different.  The
+// variables used in the stringstream input have been chosen so that each
+// represents just one RRset.
+void
+loadRRsetVector() {
+    stringstream ss;
+
+    // Comments indicate offset in the rrset_vector (when loaded) and the
+    // number of RRs in that RRset.
+    ss << soa_txt               // 0(1)
+       << zone_ns_txt           // 1(3)
+       << delegation_txt        // 2(4)
+       << delegation_ds_txt     // 3(1)
+       << mx_txt                // 4(3)
+       << www_a_txt             // 5(1)
+       << cname_txt             // 6(1)
+       << cname_nxdom_txt       // 7(1)
+       << cname_out_txt;        // 8(1)
+    rrset_vector.clear();
+    masterLoad(ss, Name("example.com."), RRClass::IN(),
+               loadRRsetVectorCallback);
+}
+
+TEST_F(QueryTest, DuplicateNameRemoval) {
+
+    // Load some RRsets into the master vector.
+    loadRRsetVector();
+    EXPECT_EQ(9, rrset_vector.size());
+
+    // Create an answer, authority and additional vector with some overlapping
+    // entries.  The following indicate which elements from rrset_vector
+    // go into each section vector.  (The values have been separated to show
+    // the overlap.)
+    //
+    // answer     = 0 1 2 3
+    // authority  =     2 3 4 5 6 7...
+    //                     ...5 (duplicate in the same section)
+    // additional = 0     3       7 8
+    //
+    // If the duplicate removal works, we should end up with the following in
+    // the message created from the three vectors:
+    //
+    // answer     = 0 1 2 3
+    // authority  =         4 5 6 7
+    // additional =                 8
+    //
+    // The expected section into which each RRset is placed is indicated in the
+    // array below.
+    const Message::Section expected_section[] = {
+        Message::SECTION_ANSWER,
+        Message::SECTION_ANSWER,
+        Message::SECTION_ANSWER,
+        Message::SECTION_ANSWER,
+        Message::SECTION_AUTHORITY,
+        Message::SECTION_AUTHORITY,
+        Message::SECTION_AUTHORITY,
+        Message::SECTION_AUTHORITY,
+        Message::SECTION_ADDITIONAL
+    };
+    EXPECT_EQ(rrset_vector.size(),
+              (sizeof(expected_section) / sizeof(Message::Section)));
+
+    // Create the vectors of RRsets (with the RRsets in a semi-random order).
+    std::vector<ConstRRsetPtr> answer;
+    answer.insert(answer.end(), rrset_vector.begin() + 2,
+                  rrset_vector.begin() + 4);
+    answer.insert(answer.end(), rrset_vector.begin() + 0,
+                  rrset_vector.begin() + 2);
+
+    std::vector<ConstRRsetPtr> authority;
+    authority.insert(authority.end(), rrset_vector.begin() + 3,
+                     rrset_vector.begin() + 8);
+    authority.push_back(rrset_vector[2]);
+    authority.push_back(rrset_vector[5]);
+
+    std::vector<ConstRRsetPtr> additional;
+    additional.insert(additional.end(), rrset_vector.begin() + 7,
+                      rrset_vector.end());
+    additional.push_back(rrset_vector[3]);
+    additional.push_back(rrset_vector[0]);
+
+    // Create the message object into which the RRsets are put
+    Message message(Message::RENDER);
+    EXPECT_EQ(0, message.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(0, message.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(0, message.getRRCount(Message::SECTION_ADDITIONAL));
+
+    // ... and fill it.
+    Query::ResponseCreator().create(message, answer, authority, additional,
+                                    false);
+
+    // Check counts in each section.  Note that these are RR counts,
+    // not RRset counts.
+    EXPECT_EQ(9, message.getRRCount(Message::SECTION_ANSWER));
+    EXPECT_EQ(6, message.getRRCount(Message::SECTION_AUTHORITY));
+    EXPECT_EQ(1, message.getRRCount(Message::SECTION_ADDITIONAL));
+
+    // ... and check that the RRsets are in the correct section
+    BOOST_STATIC_ASSERT(Message::SECTION_QUESTION == 0);
+    BOOST_STATIC_ASSERT(Message::SECTION_ANSWER == 1);
+    BOOST_STATIC_ASSERT(Message::SECTION_AUTHORITY == 2);
+    BOOST_STATIC_ASSERT(Message::SECTION_ADDITIONAL == 3);
+    Message::Section sections[] = {
+        Message::SECTION_QUESTION,
+        Message::SECTION_ANSWER,
+        Message::SECTION_AUTHORITY,
+        Message::SECTION_ADDITIONAL
+    };
+    for (int section = 1; section <= 3; ++section) {
+        for (int vecindex = 0; vecindex < rrset_vector.size(); ++vecindex) {
+            // Prepare error message in case an assertion fails (as the default
+            // message will only refer to the loop indexes).
+            stringstream ss;
+            ss << "section " << section << ", name "
+               << rrset_vector[vecindex]->getName();
+            SCOPED_TRACE(ss.str());
+
+            // Check RRset is in the right section and not in the wrong
+            // section.
+            if (sections[section] == expected_section[vecindex]) {
+                EXPECT_TRUE(message.hasRRset(sections[section],
+                            rrset_vector[vecindex]));
+            } else {
+                EXPECT_FALSE(message.hasRRset(sections[section],
+                             rrset_vector[vecindex]));
+            }
+        }
+    }
 }
 }
diff --git a/src/bin/auth/tests/statistics_unittest.cc b/src/bin/auth/tests/statistics_unittest.cc
index 0595829..2c25591 100644
--- a/src/bin/auth/tests/statistics_unittest.cc
+++ b/src/bin/auth/tests/statistics_unittest.cc
@@ -281,6 +281,8 @@ TEST_F(AuthCountersTest, submitStatisticsWithoutValidator) {
                          ->get(0)->stringValue());
     EXPECT_EQ("Auth", statistics_session_.sent_msg->get("command")
                          ->get(1)->get("owner")->stringValue());
+    EXPECT_EQ(statistics_session_.sent_msg->get("command")
+              ->get(1)->get("pid")->intValue(), getpid());
     ConstElementPtr statistics_data = statistics_session_.sent_msg
                                           ->get("command")->get(1)
                                           ->get("data");
diff --git a/src/bin/auth/tests/testdata/example.sqlite3 b/src/bin/auth/tests/testdata/example.sqlite3
index e8e255b..0f6ee02 100644
Binary files a/src/bin/auth/tests/testdata/example.sqlite3 and b/src/bin/auth/tests/testdata/example.sqlite3 differ
diff --git a/src/bin/bind10/.gitignore b/src/bin/bind10/.gitignore
new file mode 100644
index 0000000..8dc8a04
--- /dev/null
+++ b/src/bin/bind10/.gitignore
@@ -0,0 +1,3 @@
+/bind10
+/bind10_src.py
+/run_bind10.sh
diff --git a/src/bin/bind10/bind10.8 b/src/bin/bind10/bind10.8
index c2e44e7..2dafaab 100644
--- a/src/bin/bind10/bind10.8
+++ b/src/bin/bind10/bind10.8
@@ -2,12 +2,12 @@
 .\"     Title: bind10
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: November 23, 2011
+.\"      Date: March 1, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "BIND10" "8" "November 23, 2011" "BIND10" "BIND10"
+.TH "BIND10" "8" "March 1, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 bind10 \- BIND 10 boss process
 .SH "SYNOPSIS"
 .HP \w'\fBbind10\fR\ 'u
-\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-w\ \fR\fB\fIwait_time\fR\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR] [\fB\-\-wait\ \fR\fB\fIwait_time\fR\fR]
+\fBbind10\fR [\fB\-c\ \fR\fB\fIconfig\-filename\fR\fR] [\fB\-i\fR] [\fB\-m\ \fR\fB\fIfile\fR\fR] [\fB\-n\fR] [\fB\-p\ \fR\fB\fIdata_path\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-w\ \fR\fB\fIwait_time\fR\fR] [\fB\-\-cmdctl\-port\fR\ \fIport\fR] [\fB\-\-config\-file\fR\ \fIconfig\-filename\fR] [\fB\-\-data\-path\fR\ \fIdirectory\fR] [\fB\-\-msgq\-socket\-file\ \fR\fB\fIfile\fR\fR] [\fB\-\-no\-cache\fR] [\fB\-\-no\-kill\fR] [\fB\-\-pid\-file\fR\ \fIfilename\fR] [\fB\-\-pretty\-name\ \fR\fB\fIname\fR\fR] [\fB\-\-user\ \fR\fB\fIuser\fR\fR] [\fB\-\-verbose\fR] [\fB\-\-wait\ \fR\fB\fIwait_time\fR\fR]
 .SH "DESCRIPTION"
 .PP
 The
@@ -34,9 +34,8 @@ The arguments are as follows:
 .PP
 \fB\-c\fR \fIconfig\-filename\fR, \fB\-\-config\-file\fR \fIconfig\-filename\fR
 .RS 4
-The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&.
-.sp
-Defaults to b10\-config\&.db\&.
+The configuration filename to use\&. Can be either absolute or relative to data path\&. In case it is absolute, value of data path is not considered\&. Defaults to
+b10\-config\&.db\&.
 .RE
 .PP
 \fB\-\-cmdctl\-port\fR \fIport\fR
@@ -50,7 +49,9 @@ for the default\&.)
 .PP
 \fB\-p\fR \fIdirectory\fR, \fB\-\-data\-path\fR \fIdirectory\fR
 .RS 4
-The path where BIND 10 programs look for various data files\&. Currently only b10\-cfgmgr uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
+The path where BIND 10 programs look for various data files\&. Currently only
+\fBb10-cfgmgr\fR(8)
+uses it to locate the configuration file, but the usage might be extended for other programs and other types of files\&.
 .RE
 .PP
 \fB\-m\fR \fIfile\fR, \fB\-\-msgq\-socket\-file\fR \fIfile\fR
@@ -68,12 +69,18 @@ Disables the hot\-spot caching used by the
 daemon\&.
 .RE
 .PP
+\fB\-i\fR, \fB\-\-no\-kill\fR
+.RS 4
+When this option is passed,
+\fBbind10\fR
+does not send SIGTERM and SIGKILL signals to modules during shutdown\&. (This option was introduced for use during testing\&.)
+.RE
+.PP
 \fB\-u\fR \fIuser\fR, \fB\-\-user\fR \fIname\fR
 .RS 4
 The username for
 \fBbind10\fR
 to run as\&.
-
 \fBbind10\fR
 must be initially ran as the root user to use this option\&. The default is to run as the current user\&.
 .RE
@@ -82,7 +89,7 @@ must be initially ran as the root user to use this option\&. The default is to r
 .RS 4
 If defined, the PID of the
 \fBbind10\fR
-is stored in this file\&. This is used for testing purposes\&.
+is stored in this file\&.
 .RE
 .PP
 \fB\-\-pretty\-name \fR\fB\fIname\fR\fR
@@ -103,7 +110,9 @@ and its child processes\&.
 .PP
 \fB\-w\fR \fIwait_time\fR, \fB\-\-wait\fR \fIwait_time\fR
 .RS 4
-Sets the amount of time that BIND 10 will wait for the configuration manager (a key component of BIND 10) to initialize itself before abandoning the start up and terminating with an error\&. The wait_time is specified in seconds and has a default value of 10\&.
+Sets the amount of time that BIND 10 will wait for the configuration manager (a key component of BIND 10) to initialize itself before abandoning the start up and terminating with an error\&. The
+\fIwait_time\fR
+is specified in seconds and has a default value of 10\&.
 .RE
 .SH "CONFIGURATION AND COMMANDS"
 .PP
@@ -145,18 +154,6 @@ to manage under
 .IP \(bu 2.3
 .\}
 
-\fI/Boss/components/setuid\fR
-.RE
-.sp
-.RS 4
-.ie n \{\
-\h'-04'\(bu\h'+03'\c
-.\}
-.el \{\
-.sp -1
-.IP \(bu 2.3
-.\}
-
 \fI/Boss/components/b10\-stats\fR
 .RE
 .sp
@@ -212,11 +209,11 @@ to manage under
 \fBb10\-sockcreator\fR,
 \fBb10\-cfgmgr\fR, and
 \fBb10\-msgq\fR
-is not configurable\&. It is hardcoded and
+is not configurable\&. They are hardcoded and
 \fBbind10\fR
 will not run without them\&.)
 .PP
-These named sets (listed above) contain the following settings:
+The named sets for components contain the following settings:
 .PP
 \fIaddress\fR
 .RS 4
@@ -258,7 +255,7 @@ will use the component name instead\&.
 .PP
 \fIspecial\fR
 .RS 4
-This defines if the component is started a special way\&.
+This defines if the component is started a special, hardcoded way\&.
 .RE
 .PP
 The
@@ -307,14 +304,22 @@ will exit\&.
 .PP
 The statistics data collected by the
 \fBb10\-stats\fR
-daemon include:
+daemon for
+\(lqBoss\(rq
+include:
 .PP
-bind10\&.boot_time
+boot_time
 .RS 4
 The date and time that the
 \fBbind10\fR
 process started\&. This is represented in ISO 8601 format\&.
 .RE
+.SH "FILES"
+.PP
+sockcreator\-XXXXXX/sockcreator
+\(em the Unix Domain socket located in a temporary file directory for
+\fBb10\-sockcreator\fR
+communication\&.
 .SH "SEE ALSO"
 .PP
 
@@ -339,5 +344,5 @@ The
 daemon was initially designed by Shane Kerr of ISC\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index 6705760..2501fee 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010-2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>November 23, 2011</date>
+    <date>March 1, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2011</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -45,6 +45,7 @@
     <cmdsynopsis>
       <command>bind10</command>
       <arg><option>-c <replaceable>config-filename</replaceable></option></arg>
+      <arg><option>-i</option></arg>
       <arg><option>-m <replaceable>file</replaceable></option></arg>
       <arg><option>-n</option></arg>
       <arg><option>-p <replaceable>data_path</replaceable></option></arg>
@@ -56,6 +57,7 @@
       <arg><option>--data-path</option> <replaceable>directory</replaceable></arg>
       <arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
       <arg><option>--no-cache</option></arg>
+      <arg><option>--no-kill</option></arg>
       <arg><option>--pid-file</option> <replaceable>filename</replaceable></arg>
       <arg><option>--pretty-name <replaceable>name</replaceable></option></arg>
       <arg><option>--user <replaceable>user</replaceable></option></arg>
@@ -97,8 +99,8 @@
         <listitem>
           <para>The configuration filename to use. Can be either absolute or
           relative to data path. In case it is absolute, value of data path is
-          not considered.</para>
-          <para>Defaults to b10-config.db.</para>
+          not considered.
+          Defaults to <filename>b10-config.db</filename>.</para>
         </listitem>
       </varlistentry>
 
@@ -123,9 +125,11 @@
         </term>
         <listitem>
           <para>The path where BIND 10 programs look for various data files.
-          Currently only b10-cfgmgr uses it to locate the configuration file,
-          but the usage might be extended for other programs and other types
-          of files.</para>
+	  Currently only
+	  <citerefentry><refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+	  uses it to locate the configuration file, but the usage
+	  might be extended for other programs and other types of
+	  files.</para>
         </listitem>
       </varlistentry>
 
@@ -154,10 +158,20 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-i</option>, <option>--no-kill</option></term>
+        <listitem>
+	  <para>When this option is passed, <command>bind10</command>
+	  does not send SIGTERM and SIGKILL signals to modules during
+	  shutdown. (This option was introduced for use during
+	  testing.)</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-u</option> <replaceable>user</replaceable>, <option>--user</option> <replaceable>name</replaceable></term>
+<!-- TODO: example more detail. -->
         <listitem>
           <para>The username for <command>bind10</command> to run as.
-<!-- TODO: example more detail. -->
             <command>bind10</command> must be initially ran as the
             root user to use this option.
             The default is to run as the current user.</para>
@@ -169,7 +183,6 @@
         <listitem>
           <para>If defined, the PID of the <command>bind10</command> is stored
              in this file.
-             This is used for testing purposes.
           </para>
          </listitem>
       </varlistentry>
@@ -201,11 +214,12 @@ The default is the basename of ARG 0.
       <varlistentry>
         <term><option>-w</option> <replaceable>wait_time</replaceable>, <option>--wait</option> <replaceable>wait_time</replaceable></term>
         <listitem>
-          <para>Sets the amount of time that BIND 10 will wait for
-          the configuration manager (a key component of BIND 10) to
-          initialize itself before abandoning the start up and
-          terminating with an error.  The wait_time is specified in
-          seconds and has a default value of 10.
+	  <para>Sets the amount of time that BIND 10 will wait for
+	  the configuration manager (a key component of BIND 10)
+	  to initialize itself before abandoning the start up and
+	  terminating with an error.  The
+	  <replaceable>wait_time</replaceable> is specified in
+	  seconds and has a default value of 10.
           </para>
         </listitem>
       </varlistentry>
@@ -230,48 +244,24 @@ TODO: configuration section
     <itemizedlist>
 
       <listitem>
-        <para> <varname>/Boss/components/b10-auth</varname> </para>
-      </listitem>
-
-      <listitem>
         <para> <varname>/Boss/components/b10-cmdctl</varname> </para>
       </listitem>
 
       <listitem>
-        <para> <varname>/Boss/components/setuid</varname> </para>
-      </listitem>
-
-      <listitem>
         <para> <varname>/Boss/components/b10-stats</varname> </para>
       </listitem>
 
-      <listitem>
-        <para> <varname>/Boss/components/b10-stats-httpd</varname> </para>
-      </listitem>
-
-      <listitem>
-        <para> <varname>/Boss/components/b10-xfrin</varname> </para>
-      </listitem>
-
-      <listitem>
-        <para> <varname>/Boss/components/b10-xfrout</varname> </para>
-      </listitem>
-
-      <listitem>
-        <para> <varname>/Boss/components/b10-zonemgr</varname> </para>
-      </listitem>
-
     </itemizedlist>
 
     <para>
       (Note that the startup of <command>b10-sockcreator</command>,
       <command>b10-cfgmgr</command>, and <command>b10-msgq</command>
-      is not configurable. It is hardcoded and <command>bind10</command>
+      is not configurable. They are hardcoded and <command>bind10</command>
       will not run without them.)
     </para>
 
     <para>
-      These named sets (listed above) contain the following settings:
+      The named sets for components contain the following settings:
     </para>
 
     <variablelist>
@@ -346,7 +336,7 @@ list
           <term> <varname>special</varname> </term>
         <listitem>
           <para>
-            This defines if the component is started a special
+            This defines if the component is started a special, hardcoded
             way.
 <!--
 TODO: document this ... but maybe some of these will be removed
@@ -357,7 +347,6 @@ cfgmgr
 cmdctl
 msgq
 resolver
-setuid
 sockcreator
 xfrin
 -->
@@ -374,6 +363,22 @@ xfrin
     </para>
 <!-- TODO: let's just let bind10 be known as bind10 and not Boss -->
 
+<!-- TODO -->
+<!--
+    <para>
+      <command>drop_socket</command>
+      This is an internal command and not exposed to the administrator.
+    </para>
+-->
+
+<!-- TODO -->
+<!--
+    <para>
+      <command>get_socket</command>
+      This is an internal command and not exposed to the administrator.
+    </para>
+-->
+
     <para>
       <command>getstats</command> tells <command>bind10</command>
       to send its statistics data to the <command>b10-stats</command>
@@ -420,13 +425,13 @@ xfrin
 
     <para>
       The statistics data collected by the <command>b10-stats</command>
-      daemon include:
+      daemon for <quote>Boss</quote> include:
     </para>
 
     <variablelist>
 
       <varlistentry>
-        <term>bind10.boot_time</term>
+        <term>boot_time</term>
         <listitem><para>
           The date and time that the <command>bind10</command>
           process started.
@@ -438,13 +443,16 @@ xfrin
 
   </refsect1>
 
-<!--
   <refsect1>
     <title>FILES</title>
-    <para><filename></filename>
+    <para><filename>sockcreator-XXXXXX/sockcreator</filename>
+    —
+    the Unix Domain socket located in a temporary file directory for
+    <command>b10-sockcreator</command>
+<!--    <citerefentry><refentrytitle>b10-sockcreator</refentrytitle><manvolnum>8</manvolnum></citerefentry> -->
+    communication.
     </para>
   </refsect1>
--->
 
   <refsect1>
     <title>SEE ALSO</title>
@@ -476,6 +484,9 @@ xfrin
       <citetitle>BIND 10 Guide</citetitle>.
     </para>
   </refsect1>
+<!-- <citerefentry>
+        <refentrytitle>b10-sockcreator</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>, -->
 
   <refsect1 id='history'><title>HISTORY</title>
     <para>The development of <command>bind10</command>
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index 79635fd..3dd938f 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -24,7 +24,7 @@ needs a dedicated message bus.
 An error was encountered when the boss module specified
 statistics data which is invalid for the boss specification file.
 
-% BIND10_COMPONENT_FAILED component %1 (pid %2) failed with %3 exit status
+% BIND10_COMPONENT_FAILED component %1 (pid %2) failed: %3
 The process terminated, but the bind10 boss didn't expect it to, which means
 it must have failed.
 
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index faf1582..37b845d 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -167,8 +167,9 @@ class BoB:
     """Boss of BIND class."""
     
     def __init__(self, msgq_socket_file=None, data_path=None,
-    config_filename=None, nocache=False, verbose=False, setuid=None,
-    username=None, cmdctl_port=None, wait_time=10):
+                 config_filename=None, clear_config=False, nocache=False,
+                 verbose=False, nokill=False, setuid=None, username=None,
+                 cmdctl_port=None, wait_time=10):
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
@@ -208,8 +209,10 @@ class BoB:
         self.uid = setuid
         self.username = username
         self.verbose = verbose
+        self.nokill = nokill
         self.data_path = data_path
         self.config_filename = config_filename
+        self.clear_config = clear_config
         self.cmdctl_port = cmdctl_port
         self.wait_time = wait_time
         self._component_configurator = isc.bind10.component.Configurator(self,
@@ -465,6 +468,8 @@ class BoB:
             args.append("--data-path=" + self.data_path)
         if self.config_filename is not None:
             args.append("--config-filename=" + self.config_filename)
+        if self.clear_config:
+            args.append("--clear-config")
         bind_cfgd = ProcessInfo("b10-cfgmgr", args,
                                 self.c_channel_env)
         bind_cfgd.spawn()
@@ -702,32 +707,36 @@ class BoB:
         # still not enough.
         time.sleep(1)
         self.reap_children()
-        # next try sending a SIGTERM
-        components_to_stop = list(self.components.values())
-        for component in components_to_stop:
-            logger.info(BIND10_SEND_SIGTERM, component.name(), component.pid())
-            try:
-                component.kill()
-            except OSError:
-                # ignore these (usually ESRCH because the child
-                # finally exited)
-                pass
-        # finally, send SIGKILL (unmaskable termination) until everybody dies
-        while self.components:
-            # XXX: some delay probably useful... how much is uncertain
-            time.sleep(0.1)  
-            self.reap_children()
+
+        # Send TERM and KILL signals to modules if we're not prevented
+        # from doing so
+        if not self.nokill:
+            # next try sending a SIGTERM
             components_to_stop = list(self.components.values())
             for component in components_to_stop:
-                logger.info(BIND10_SEND_SIGKILL, component.name(),
-                            component.pid())
+                logger.info(BIND10_SEND_SIGTERM, component.name(), component.pid())
                 try:
-                    component.kill(True)
+                    component.kill()
                 except OSError:
                     # ignore these (usually ESRCH because the child
                     # finally exited)
                     pass
-        logger.info(BIND10_SHUTDOWN_COMPLETE)
+            # finally, send SIGKILL (unmaskable termination) until everybody dies
+            while self.components:
+                # XXX: some delay probably useful... how much is uncertain
+                time.sleep(0.1)
+                self.reap_children()
+                components_to_stop = list(self.components.values())
+                for component in components_to_stop:
+                    logger.info(BIND10_SEND_SIGKILL, component.name(),
+                                component.pid())
+                    try:
+                        component.kill(True)
+                    except OSError:
+                        # ignore these (usually ESRCH because the child
+                        # finally exited)
+                        pass
+            logger.info(BIND10_SHUTDOWN_COMPLETE)
 
     def _get_process_exit_status(self):
         return os.waitpid(-1, os.WNOHANG)
@@ -892,7 +901,7 @@ class BoB:
         # the need to find the place ourself or bother users. Also, this
         # secures the socket on some platforms, as it creates a private
         # directory.
-        self._tmpdir = tempfile.mkdtemp()
+        self._tmpdir = tempfile.mkdtemp(prefix='sockcreator-')
         # Get the name
         self._socket_path = os.path.join(self._tmpdir, "sockcreator")
         # And bind the socket to the name
@@ -1043,6 +1052,8 @@ def parse_args(args=sys.argv[1:], Parser=OptionParser):
                       help="UNIX domain socket file the b10-msgq daemon will use")
     parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
                       default=False, help="disable hot-spot cache in authoritative DNS server")
+    parser.add_option("-i", "--no-kill", action="store_true", dest="nokill",
+                      default=False, help="do not send SIGTERM and SIGKILL signals to modules during shutdown")
     parser.add_option("-u", "--user", dest="user", type="string", default=None,
                       help="Change user after startup (must run as root)")
     parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
@@ -1053,6 +1064,10 @@ def parse_args(args=sys.argv[1:], Parser=OptionParser):
     parser.add_option("-c", "--config-file", action="store",
                       dest="config_file", default=None,
                       help="Configuration database filename")
+    parser.add_option("--clear-config", action="store_true",
+                      dest="clear_config", default=False,
+                      help="Create backup of the configuration file and " +
+                           "start with a clean configuration")
     parser.add_option("-p", "--data-path", dest="data_path",
                       help="Directory to search for configuration files",
                       default=None)
@@ -1165,9 +1180,10 @@ def main():
     try:
         # Go bob!
         boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
-                           options.config_file, options.nocache,
-                           options.verbose, setuid, username,
-                           options.cmdctl_port, options.wait_time)
+                           options.config_file, options.clear_config,
+                           options.nocache, options.verbose, options.nokill,
+                           setuid, username, options.cmdctl_port,
+                           options.wait_time)
         startup_result = boss_of_bind.startup()
         if startup_result:
             logger.fatal(BIND10_STARTUP_ERROR, startup_result)
diff --git a/src/bin/bind10/bob.spec b/src/bin/bind10/bob.spec
index 29b1f40..8b75640 100644
--- a/src/bin/bind10/bob.spec
+++ b/src/bin/bind10/bob.spec
@@ -8,15 +8,7 @@
         "item_type": "named_set",
         "item_optional": false,
         "item_default": {
-          "b10-auth": { "special": "auth", "kind": "needed" },
-          "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
-          "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
-          "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
           "b10-stats": { "address": "Stats", "kind": "dispensable" },
-          "b10-stats-httpd": {
-            "address": "StatsHttpd",
-            "kind": "dispensable"
-          },
           "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
         },
         "named_set_item_spec": {
diff --git a/src/bin/bind10/tests/.gitignore b/src/bin/bind10/tests/.gitignore
new file mode 100644
index 0000000..5e54716
--- /dev/null
+++ b/src/bin/bind10/tests/.gitignore
@@ -0,0 +1 @@
+/bind10_test.py
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index 882824d..84a9da9 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -1012,6 +1012,22 @@ class TestParseArgs(unittest.TestCase):
         options = parse_args(['--config-file=config-file'], TestOptParser)
         self.assertEqual('config-file', options.config_file)
 
+    def test_clear_config(self):
+        options = parse_args([], TestOptParser)
+        self.assertEqual(False, options.clear_config)
+        options = parse_args(['--clear-config'], TestOptParser)
+        self.assertEqual(True, options.clear_config)
+
+    def test_nokill(self):
+        options = parse_args([], TestOptParser)
+        self.assertEqual(False, options.nokill)
+        options = parse_args(['--no-kill'], TestOptParser)
+        self.assertEqual(True, options.nokill)
+        options = parse_args([], TestOptParser)
+        self.assertEqual(False, options.nokill)
+        options = parse_args(['-i'], TestOptParser)
+        self.assertEqual(True, options.nokill)
+
     def test_cmdctl_port(self):
         """
         Test it can parse the command control port.
@@ -1160,11 +1176,13 @@ class TestBossComponents(unittest.TestCase):
         # We check somewhere else that the shutdown is actually called
         # from there (the test_kills).
 
-    def test_kills(self):
+    def __real_test_kill(self, nokill = False):
         """
-        Test that the boss kills components which don't want to stop.
+        Helper function that does the actual kill functionality testing.
         """
         bob = MockBob()
+        bob.nokill = nokill
+
         killed = []
         class ImmortalComponent:
             """
@@ -1194,11 +1212,33 @@ class TestBossComponents(unittest.TestCase):
         bob.shutdown()
 
         self.assertTrue(bob.ccs.stopped)
-        self.assertEqual([False, True], killed)
+
+        # Here, killed is an array where False is added if SIGTERM
+        # should be sent, or True if SIGKILL should be sent, in order in
+        # which they're sent.
+        if nokill:
+            self.assertEqual([], killed)
+        else:
+            self.assertEqual([False, True], killed)
+
         self.assertTrue(self.__called)
 
         bob._component_configurator.shutdown = orig
 
+    def test_kills(self):
+        """
+        Test that the boss kills components which don't want to stop.
+        """
+        self.__real_test_kill()
+
+    def test_nokill(self):
+        """
+        Test that the boss *doesn't* kill components which don't want to
+        stop, when asked not to (by passing the --no-kill option which
+        sets bob.nokill to True).
+        """
+        self.__real_test_kill(True)
+
     def test_component_shutdown(self):
         """
         Test the component_shutdown sets all variables accordingly.
diff --git a/src/bin/bindctl/.gitignore b/src/bin/bindctl/.gitignore
new file mode 100644
index 0000000..71fba03
--- /dev/null
+++ b/src/bin/bindctl/.gitignore
@@ -0,0 +1,3 @@
+/bindctl
+/bindctl_main.py
+/run_bindctl.sh
diff --git a/src/bin/bindctl/Makefile.am b/src/bin/bindctl/Makefile.am
index 700f26e..52a5145 100644
--- a/src/bin/bindctl/Makefile.am
+++ b/src/bin/bindctl/Makefile.am
@@ -8,7 +8,7 @@ EXTRA_DIST = $(man_MANS) bindctl.xml
 noinst_SCRIPTS = run_bindctl.sh
 
 python_PYTHON = __init__.py bindcmd.py cmdparse.py exception.py moduleinfo.py \
-		mycollections.py
+		mycollections.py command_sets.py
 pythondir = $(pyexecdir)/bindctl
 
 bindctldir = $(pkgdatadir)
diff --git a/src/bin/bindctl/bindcmd.py b/src/bin/bindctl/bindcmd.py
index b67bc4b..f1a622e 100644
--- a/src/bin/bindctl/bindcmd.py
+++ b/src/bin/bindctl/bindcmd.py
@@ -23,6 +23,7 @@ from cmd import Cmd
 from bindctl.exception import *
 from bindctl.moduleinfo import *
 from bindctl.cmdparse import BindCmdParse
+from bindctl import command_sets
 from xml.dom import minidom
 import isc
 import isc.cc.data
@@ -37,6 +38,7 @@ from hashlib import sha1
 import csv
 import pwd
 import getpass
+import copy
 
 try:
     from collections import OrderedDict
@@ -319,6 +321,8 @@ class BindCmdInterpreter(Cmd):
                                   param_spec = arg)
                 if ("item_default" in arg):
                     param.default = arg["item_default"]
+                if ("item_description" in arg):
+                    param.desc = arg["item_description"]
                 cmd.add_param(param)
             module.add_command(cmd)
         self.add_module_info(module)
@@ -361,12 +365,12 @@ class BindCmdInterpreter(Cmd):
                 if type(name) == int:
                     # lump all extraneous arguments together as one big final one
                     # todo: check if last param type is a string?
-                    if (param_count > 2):
-                        while (param_count > len(command_info.params) - 1):
-                            params[param_count - 2] += params[param_count - 1]
-                            del(params[param_count - 1])
-                            param_count = len(params)
-                            cmd.params = params.copy()
+                    while (param_count > 2 and
+                           param_count > len(command_info.params) - 1):
+                        params[param_count - 2] += " " + params[param_count - 1]
+                        del(params[param_count - 1])
+                        param_count = len(params)
+                        cmd.params = params.copy()
 
                     # (-1, help is always in the all_params list)
                     if name >= len(all_params) - 1:
@@ -391,8 +395,9 @@ class BindCmdInterpreter(Cmd):
                 param_nr += 1
 
         # Convert parameter value according parameter spec file.
-        # Ignore check for commands belongs to module 'config'
-        if cmd.module != CONFIG_MODULE_NAME:
+        # Ignore check for commands belongs to module 'config' or 'execute
+        if cmd.module != CONFIG_MODULE_NAME and\
+           cmd.module != command_sets.EXECUTE_MODULE_NAME:
             for param_name in cmd.params:
                 param_spec = command_info.get_param_with_name(param_name).param_spec
                 try:
@@ -406,16 +411,9 @@ class BindCmdInterpreter(Cmd):
         if cmd.command == "help" or ("help" in cmd.params.keys()):
             self._handle_help(cmd)
         elif cmd.module == CONFIG_MODULE_NAME:
-            try:
-                self.apply_config_cmd(cmd)
-            except isc.cc.data.DataTypeError as dte:
-                print("Error: " + str(dte))
-            except isc.cc.data.DataNotFoundError as dnfe:
-                print("Error: " + str(dnfe))
-            except isc.cc.data.DataAlreadyPresentError as dape:
-                print("Error: " + str(dape))
-            except KeyError as ke:
-                print("Error: missing " + str(ke))
+            self.apply_config_cmd(cmd)
+        elif cmd.module == command_sets.EXECUTE_MODULE_NAME:
+            self.apply_execute_cmd(cmd)
         else:
             self.apply_cmd(cmd)
 
@@ -574,6 +572,14 @@ class BindCmdInterpreter(Cmd):
             self._print_correct_usage(err)
         except isc.cc.data.DataTypeError as err:
             print("Error! ", err)
+        except isc.cc.data.DataTypeError as dte:
+            print("Error: " + str(dte))
+        except isc.cc.data.DataNotFoundError as dnfe:
+            print("Error: " + str(dnfe))
+        except isc.cc.data.DataAlreadyPresentError as dape:
+            print("Error: " + str(dape))
+        except KeyError as ke:
+            print("Error: missing " + str(ke))
 
     def _print_correct_usage(self, ept):
         if isinstance(ept, CmdUnknownModuleSyntaxError):
@@ -726,6 +732,84 @@ class BindCmdInterpreter(Cmd):
 
         self.location = new_location
 
+    def apply_execute_cmd(self, command):
+        '''Handles the 'execute' command, which executes a number of
+           (preset) statements. The command set to execute is either
+           read from a file (e.g. 'execute file <file>'.) or one
+           of the sets as defined in command_sets.py'''
+        if command.command == 'file':
+            try:
+                with open(command.params['filename']) as command_file:
+                    commands = command_file.readlines()
+            except IOError as ioe:
+                print("Error: " + str(ioe))
+                return
+        elif command_sets.has_command_set(command.command):
+            commands = command_sets.get_commands(command.command)
+        else:
+            # Should not be reachable; parser should've caught this
+            raise Exception("Unknown execute command type " + command.command)
+
+        # We have our set of commands now, depending on whether 'show' was
+        # specified, show or execute them
+        if 'show' in command.params and command.params['show'] == 'show':
+            self.__show_execute_commands(commands)
+        else:
+            self.__apply_execute_commands(commands)
+
+    def __show_execute_commands(self, commands):
+        '''Prints the command list without executing them'''
+        for line in commands:
+            print(line.strip())
+
+    def __apply_execute_commands(self, commands):
+        '''Applies the configuration commands from the given iterator.
+           This is the method that catches, comments, echo statements, and
+           other directives. All commands not filtered by this method are
+           interpreted as if they are directly entered in an active session.
+           Lines starting with any of the following characters are not
+           passed directly:
+           # - These are comments
+           ! - These are directives
+               !echo: print the rest of the line
+               !verbose on/off: print the commands themselves too
+               Unknown directives are ignored (with a warning)
+           The execution is stopped if there are any errors.
+        '''
+        verbose = False
+        try:
+            for line in commands:
+                line = line.strip()
+                if verbose:
+                    print(line)
+                if line.startswith('#') or len(line) == 0:
+                    continue
+                elif line.startswith('!'):
+                    if re.match('^!echo ', line, re.I) and len(line) > 6:
+                        print(line[6:])
+                    elif re.match('^!verbose\s+on\s*$', line, re.I):
+                        verbose = True
+                    elif re.match('^!verbose\s+off$', line, re.I):
+                        verbose = False
+                    else:
+                        print("Warning: ignoring unknown directive: " + line)
+                else:
+                    cmd = BindCmdParse(line)
+                    self._validate_cmd(cmd)
+                    self._handle_cmd(cmd)
+        except (isc.config.ModuleCCSessionError,
+                IOError, http.client.HTTPException,
+                BindCtlException, isc.cc.data.DataTypeError,
+                isc.cc.data.DataNotFoundError,
+                isc.cc.data.DataAlreadyPresentError,
+                KeyError) as err:
+            print('Error: ', err)
+            print()
+            print('Depending on the contents of the script, and which')
+            print('commands it has called, there can be committed and')
+            print('local changes. It is advised to check your settings,')
+            print('and revert local changes with "config revert".')
+
     def apply_cmd(self, cmd):
         '''Handles a general module command'''
         url = '/' + cmd.module + '/' + cmd.command
diff --git a/src/bin/bindctl/bindctl_main.py.in b/src/bin/bindctl/bindctl_main.py.in
index 58c03eb..1685355 100755
--- a/src/bin/bindctl/bindctl_main.py.in
+++ b/src/bin/bindctl/bindctl_main.py.in
@@ -22,6 +22,7 @@ import sys; sys.path.append ('@@PYTHONPATH@@')
 
 from bindctl.moduleinfo import *
 from bindctl.bindcmd import *
+from bindctl import command_sets
 import pprint
 from optparse import OptionParser, OptionValueError
 import isc.util.process
@@ -146,5 +147,6 @@ if __name__ == '__main__':
     tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain,
                               csv_file_dir=options.csv_file_dir)
     prepare_config_commands(tool)
+    command_sets.prepare_execute_commands(tool)
     result = tool.run()
     sys.exit(result)
diff --git a/src/bin/bindctl/command_sets.py b/src/bin/bindctl/command_sets.py
new file mode 100644
index 0000000..9e2c2ef
--- /dev/null
+++ b/src/bin/bindctl/command_sets.py
@@ -0,0 +1,95 @@
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file provides a built-in set of 'execute' commands, for common
+# functions, such as adding an initial auth server.
+# By calling the function prepare_execute_commands, the
+# commands in the command_sets map are added to the virtual
+# component called 'execute'. This is done in bindctl_main.
+
+from bindctl.moduleinfo import *
+# The name of the 'virtual' command set execution module in bindctl
+EXECUTE_MODULE_NAME = 'execute'
+
+# This is a map of command names to lists
+# Each element in the set should itself be a dict containing:
+# 'description': A string with a description of the command set
+# 'commands': A list of bindctl commands
+command_sets = {
+    'init_authoritative_server': {
+        'description':
+            'Configure and run a basic Authoritative server, with default '+
+            'SQLite3 backend, and xfrin and xfrout functionality',
+        'commands':
+            [
+            '!echo adding Authoritative server component',
+            'config add /Boss/components b10-auth',
+            'config set /Boss/components/b10-auth/kind needed',
+            'config set /Boss/components/b10-auth/special auth',
+            '!echo adding Xfrin component',
+            'config add /Boss/components b10-xfrin',
+            'config set /Boss/components/b10-xfrin/address Xfrin',
+            'config set /Boss/components/b10-xfrin/kind dispensable',
+            '!echo adding Xfrout component',
+            'config add /Boss/components b10-xfrout',
+            'config set /Boss/components/b10-xfrout/address Xfrout',
+            'config set /Boss/components/b10-xfrout/kind dispensable',
+            '!echo adding Zone Manager component',
+            'config add /Boss/components b10-zonemgr',
+            'config set /Boss/components/b10-zonemgr/address Zonemgr',
+            'config set /Boss/components/b10-zonemgr/kind dispensable',
+            '!echo Components added. Please enter "config commit" to',
+            '!echo finalize initial setup and run the components.'
+            ]
+    }
+}
+
+def has_command_set(name):
+    return name in command_sets
+
+def get_commands(name):
+    return command_sets[name]['commands']
+
+def get_description(name):
+    return command_sets[name]['description']
+
+# For each
+def prepare_execute_commands(tool):
+    """This function is called by bindctl_main, and sets up the commands
+       defined here for use in bindctl."""
+    # common parameter
+    param_show = ParamInfo(name="show", type="string", optional=True,
+        desc="Show the list of commands without executing them")
+
+    # The command module
+    module = ModuleInfo(name=EXECUTE_MODULE_NAME,
+                        desc="Execute a given set of commands")
+
+    # Command to execute a file
+    cmd = CommandInfo(name="file", desc="Read commands from file")
+    param = ParamInfo(name="filename", type="string", optional=False,
+                      desc="File to read the set of commands from.")
+    cmd.add_param(param)
+    cmd.add_param(param_show)
+    module.add_command(cmd)
+
+    # and loop through all command sets defined above
+    for name in command_sets:
+        cmd = CommandInfo(name=name, desc=get_description(name))
+        cmd.add_param(param_show)
+        module.add_command(cmd)
+
+    tool.add_module_info(module)
+
diff --git a/src/bin/bindctl/moduleinfo.py b/src/bin/bindctl/moduleinfo.py
index 6e41dce..6c3a304 100644
--- a/src/bin/bindctl/moduleinfo.py
+++ b/src/bin/bindctl/moduleinfo.py
@@ -57,8 +57,12 @@ class ParamInfo:
     def __str__(self):        
         return str("\t%s <type: %s> \t(%s)" % (self.name, self.type, self.desc))
 
-    def get_name(self):
-        return "%s <type: %s>" % (self.name, self.type)
+    def get_basic_info(self):
+        if self.is_optional:
+            opt_str = "optional"
+        else:
+            opt_str = "mandatory"
+        return "%s (%s, %s)" % (self.name, self.type, opt_str)
 
     def get_desc(self):
         return self.desc
@@ -155,37 +159,24 @@ class CommandInfo:
         """Prints the help info for this command to stdout"""
         print("Command ", self)
         print("\t\thelp (Get help for command)")
-                
+
         params = self.params.copy()
         del params["help"]
 
         if len(params) == 0:
-            print("No parameters for the command")
+            print("This command has no parameters")
             return
-        
-        print("\nMandatory parameters:")
-        mandatory_infos = []
-        for info in params.values():            
-            if not info.is_optional:
-                print("    %s" % info.get_name())
-                print(textwrap.fill(info.get_desc(),
-                      initial_indent="        ",
-                      subsequent_indent="        ",
-                      width=70))
-                mandatory_infos.append(info)
 
-        optional_infos = [info for info in params.values() 
-                          if info not in mandatory_infos]
-        if len(optional_infos) > 0:
-            print("\nOptional parameters:")      
-            for info in optional_infos:
-                print("    %s" % info.get_name())
-                print(textwrap.fill(info.get_desc(),
+        print("Parameters:")
+        for info in params.values():
+            print("    %s" % info.get_basic_info())
+            description = info.get_desc()
+            if description != "":
+                print(textwrap.fill(description,
                       initial_indent="        ",
                       subsequent_indent="        ",
                       width=70))
 
-
 class ModuleInfo:
     """Define the information of one module, include module name, 
     module supporting commands.
diff --git a/src/bin/bindctl/tests/.gitignore b/src/bin/bindctl/tests/.gitignore
new file mode 100644
index 0000000..284bacc
--- /dev/null
+++ b/src/bin/bindctl/tests/.gitignore
@@ -0,0 +1 @@
+/bindctl_test
diff --git a/src/bin/bindctl/tests/bindctl_test.py b/src/bin/bindctl/tests/bindctl_test.py
index cef35dc..1ddb916 100644
--- a/src/bin/bindctl/tests/bindctl_test.py
+++ b/src/bin/bindctl/tests/bindctl_test.py
@@ -180,12 +180,10 @@ class TestCmdSyntax(unittest.TestCase):
         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):
         self.my_assert_raise(CmdUnknownModuleSyntaxError, "zoned d")
         self.my_assert_raise(CmdUnknownModuleSyntaxError, "dd dd  ")
 
-
     def testCmdUnknownCmdSyntaxError(self):
         self.my_assert_raise(CmdUnknownCmdSyntaxError, "zone dd")
 
@@ -198,6 +196,7 @@ class TestCmdSyntax(unittest.TestCase):
     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")
 
 class TestModuleInfo(unittest.TestCase):
 
@@ -366,10 +365,20 @@ class TestConfigCommands(unittest.TestCase):
         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)
+
+        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 = cmdparse.BindCmdParse("config unset identifier=\"foo/bar\"")
+        self.assertRaises(isc.cc.data.DataNotFoundError,
+                          self.tool.apply_config_cmd, cmd)
+
         # 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)
diff --git a/src/bin/cfgmgr/.gitignore b/src/bin/cfgmgr/.gitignore
new file mode 100644
index 0000000..aad54f4
--- /dev/null
+++ b/src/bin/cfgmgr/.gitignore
@@ -0,0 +1,2 @@
+/b10-cfgmgr
+/b10-cfgmgr.py
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 2ccc430..760b6d8 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -49,6 +49,10 @@ def parse_options(args=sys.argv[1:], Parser=OptionParser):
                       help="Configuration database filename " +
                       "(default=" + DEFAULT_CONFIG_FILE + ")",
                       default=DEFAULT_CONFIG_FILE)
+    parser.add_option("--clear-config", action="store_true",
+                      dest="clear_config", default=False,
+                      help="Back up the configuration file and start with " +
+                           "a clean one")
     (options, args) = parser.parse_args(args)
     if args:
         parser.error("No non-option arguments allowed")
@@ -85,7 +89,8 @@ def main():
     options = parse_options()
     global cm
     try:
-        cm = ConfigManager(options.data_path, options.config_file)
+        cm = ConfigManager(options.data_path, options.config_file,
+                           None, options.clear_config)
         signal.signal(signal.SIGINT, signal_handler)
         signal.signal(signal.SIGTERM, signal_handler)
         cm.read_config()
diff --git a/src/bin/cfgmgr/tests/.gitignore b/src/bin/cfgmgr/tests/.gitignore
new file mode 100644
index 0000000..dcdab2d
--- /dev/null
+++ b/src/bin/cfgmgr/tests/.gitignore
@@ -0,0 +1 @@
+/b10-cfgmgr_test.py
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index ea5fc8b..ca91c9c 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -24,12 +24,13 @@ import bind10_config
 from isc.testutils.parse_args import OptsError, TestOptParser
 
 class MyConfigManager:
-    def __init__(self, path, filename):
+    def __init__(self, path, filename, session=None, rename_config_file=False):
         self._path = path
         self.read_config_called = False
         self.notify_boss_called = False
         self.run_called = False
         self.write_config_called = False
+        self.rename_config_called = False
         self.running = True
         self.virtual_modules = []
 
@@ -45,6 +46,9 @@ class MyConfigManager:
     def write_config(self):
         self.write_config_called = True
 
+    def rename_config_file(self, ofile, nfile):
+        self.rename_config_called = True
+
     def set_virtual_module(self, spec, function):
         self.virtual_modules.append((spec, function))
 
@@ -90,6 +94,7 @@ class TestConfigManagerStartup(unittest.TestCase):
         self.assertTrue(self.loaded_plugins)
         # if there are no changes, config is not written
         self.assertFalse(b.cm.write_config_called)
+        self.assertFalse(b.cm.rename_config_called)
 
         self.assertTrue(b.cm.running)
         b.signal_handler(None, None)
@@ -187,6 +192,14 @@ class TestParseArgs(unittest.TestCase):
         self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
                           TestOptParser)
 
+    def test_clear_config(self):
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options([], TestOptParser)
+        self.assertFalse(parsed.clear_config)
+        parsed = b.parse_options(['--clear-config'], TestOptParser)
+        self.assertTrue(parsed.clear_config)
+        
+
 if __name__ == '__main__':
     unittest.main()
 
diff --git a/src/bin/cmdctl/.gitignore b/src/bin/cmdctl/.gitignore
new file mode 100644
index 0000000..a194135
--- /dev/null
+++ b/src/bin/cmdctl/.gitignore
@@ -0,0 +1,5 @@
+/b10-cmdctl
+/cmdctl.py
+/cmdctl.spec
+/cmdctl.spec.pre
+/run_b10-cmdctl.sh
diff --git a/src/bin/cmdctl/b10-cmdctl.8 b/src/bin/cmdctl/b10-cmdctl.8
index c8c938b..0b478fe 100644
--- a/src/bin/cmdctl/b10-cmdctl.8
+++ b/src/bin/cmdctl/b10-cmdctl.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-cmdctl
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 9, 2010
+.\"      Date: February 28, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-CMDCTL" "8" "March 9, 2010" "BIND10" "BIND10"
+.TH "B10\-CMDCTL" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -70,6 +70,33 @@ Enable verbose mode\&.
 .RS 4
 Display the version number and exit\&.
 .RE
+.SH "CONFIGURATION AND COMMANDS"
+.PP
+The configurable settings are:
+.PP
+
+\fIaccounts_file\fR
+defines the path to the user accounts database\&. The default is
+/usr/local/etc/bind10\-devel/cmdctl\-accounts\&.csv\&.
+.PP
+
+\fIcert_file\fR
+defines the path to the PEM certificate file\&. The default is
+/usr/local/etc/bind10\-devel/cmdctl\-certfile\&.pem\&.
+.PP
+
+\fIkey_file\fR
+defines the path to the PEM private key file\&. The default is
+/usr/local/etc/bind10\-devel/cmdctl\-keyfile\&.pem\&.
+.PP
+The configuration command is:
+.PP
+
+\fBshutdown\fR
+exits
+\fBb10\-cmdctl\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .SH "FILES"
 .PP
 /usr/local/etc/bind10\-devel/cmdctl\-accounts\&.csv
@@ -93,5 +120,5 @@ The
 daemon was initially designed and coded by Zhang Likun of CNNIC\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/cmdctl/b10-cmdctl.xml b/src/bin/cmdctl/b10-cmdctl.xml
index 06953a4..e01d5a2 100644
--- a/src/bin/cmdctl/b10-cmdctl.xml
+++ b/src/bin/cmdctl/b10-cmdctl.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 9, 2010</date>
+    <date>February 28, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -37,7 +37,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -138,6 +138,50 @@
   </refsect1>
 
   <refsect1>
+    <title>CONFIGURATION AND COMMANDS</title>
+    <para>
+      The configurable settings are:
+    </para>
+
+    <para>
+      <varname>accounts_file</varname> defines the path to the
+      user accounts database.
+      The default is
+      <filename>/usr/local/etc/bind10-devel/cmdctl-accounts.csv</filename>.
+    </para>
+
+    <para>
+      <varname>cert_file</varname> defines the path to the
+      PEM certificate file.
+      The default is
+      <filename>/usr/local/etc/bind10-devel/cmdctl-certfile.pem</filename>.
+    </para>
+
+    <para>
+      <varname>key_file</varname> defines the path to the PEM private key
+      file.
+      The default is
+      <filename>/usr/local/etc/bind10-devel/cmdctl-keyfile.pem</filename>.
+    </para>
+
+<!-- TODO: formating -->
+    <para>
+      The configuration command is:
+    </para>
+
+<!-- NOTE: print_settings is not documented since I think will be removed -->
+
+    <para>
+      <command>shutdown</command> exits <command>b10-cmdctl</command>.
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
+    </para>
+
+  </refsect1>
+
+  <refsect1>
     <title>FILES</title>
 <!-- TODO: replace /usr/local -->
 <!-- TODO: permissions -->
diff --git a/src/bin/cmdctl/tests/.gitignore b/src/bin/cmdctl/tests/.gitignore
new file mode 100644
index 0000000..ab9dfef
--- /dev/null
+++ b/src/bin/cmdctl/tests/.gitignore
@@ -0,0 +1 @@
+/cmdctl_test
diff --git a/src/bin/dbutil/.gitignore b/src/bin/dbutil/.gitignore
new file mode 100644
index 0000000..abb63d5
--- /dev/null
+++ b/src/bin/dbutil/.gitignore
@@ -0,0 +1,3 @@
+/b10-dbutil
+/dbutil.py
+/run_dbutil.sh
diff --git a/src/bin/dbutil/Makefile.am b/src/bin/dbutil/Makefile.am
new file mode 100644
index 0000000..e05055f
--- /dev/null
+++ b/src/bin/dbutil/Makefile.am
@@ -0,0 +1,39 @@
+SUBDIRS = . tests
+
+bin_SCRIPTS = b10-dbutil
+man_MANS = b10-dbutil.8
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/dbutil_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+EXTRA_DIST = $(man_MANS) b10-dbutil.xml dbutil_messages.mes
+
+noinst_SCRIPTS = run_dbutil.sh
+
+CLEANFILES = b10-dbutil b10-dbutil.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/dbutil_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/dbutil_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/dbutil_messages.pyo
+
+if ENABLE_MAN
+
+b10-dbutil.8: b10-dbutil.xml
+	xsltproc --novalid --xinclude --nonet -o $@ http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $(srcdir)/b10-dbutil.xml
+
+endif
+
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/dbutil_messages.py : dbutil_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/dbutil_messages.mes
+
+b10-dbutil: dbutil.py $(PYTHON_LOGMSGPKG_DIR)/work/dbutil_messages.py
+	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
+	       -e "s|@@SYSCONFDIR@@|@sysconfdir@|" \
+	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" dbutil.py >$@
+	chmod a+x $@
+
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/bin/dbutil/b10-dbutil.8 b/src/bin/dbutil/b10-dbutil.8
new file mode 100644
index 0000000..437a69d
--- /dev/null
+++ b/src/bin/dbutil/b10-dbutil.8
@@ -0,0 +1,92 @@
+'\" t
+.\"     Title: b10-dbutil
+.\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: March 20, 2012
+.\"    Manual: BIND10
+.\"    Source: BIND10
+.\"  Language: English
+.\"
+.TH "B10\-DBUTIL" "8" "March 20, 2012" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+b10-dbutil \- Zone Database Maintenance Utility
+.SH "SYNOPSIS"
+.HP \w'\fBb10\-dbutil\ \-\-check\fR\ 'u
+\fBb10\-dbutil \-\-check\fR [\-\-verbose] [\-\-quiet] [\fIdbfile\fR]
+.HP \w'\fBb10\-dbutil\ \-\-upgrade\fR\ 'u
+\fBb10\-dbutil \-\-upgrade\fR [\-\-noconfirm] [\-\-verbose] [\-\-quiet] [\fIdbfile\fR]
+.SH "DESCRIPTION"
+.PP
+The
+\fBb10\-dbutil\fR
+utility is a general administration utility for SQL databases\&. (Currently only SQLite is supported by BIND 10\&.) It can report the current verion of the schema, and upgrade an existing database to the latest version of the schema\&.
+.PP
+
+\fBb10\-dbutil\fR
+operates in one of two modes, check mode or upgrade mode\&.
+.PP
+In check mode (\fBb10\-dbutil \-\-check\fR), the utility reads the version of the database schema from the database and prints it\&. It will tell you whether the schema is at the latest version supported by BIND 10\&. Exit status is 0 if the schema is at the correct version, 1 if the schema is at an older version, 2 if the schema is at a version not yet supported by this version of b10\-dbutil\&. Any higher value indicates an error during command\-line parsing or execution\&.
+.PP
+When the upgrade function is selected (\fBb10\-dbutil \-\-upgrade\fR), the utility takes a copy of the database, then upgrades it to the latest version of the schema\&. The contents of the database remain intact\&. (The backup file is a file in the same directory as the database file\&. It has the same name, with "\&.backup" appended to it\&. If a file of that name already exists, the file will have the suffix "\&.backup\-1"\&. If that exists, the file will be suffixed "\&.backup\-2", and so on)\&. Exit status is 0 if the upgrade is either succesful or aborted by the user, and non\-zero if there is an error\&.
+.PP
+When upgrading the database, it is
+\fIstrongly\fR
+recommended that BIND 10 not be running while the upgrade is in progress\&.
+.SH "ARGUMENTS"
+.PP
+The arguments are as follows:
+.PP
+\fB\-\-check\fR
+.RS 4
+Selects the version check function, which reports the current version of the database\&. This is incompatible with the \-\-upgrade option\&.
+.RE
+.PP
+\fB\-\-noconfirm\fR
+.RS 4
+Only valid with \-\-upgrade, this disables the prompt\&. Normally the utility will print a warning that an upgrade is about to take place and request that you type "Yes" to continue\&. If this switch is given on the command line, no prompt will be issued: the utility will just perform the upgrade\&.
+.RE
+.PP
+\fB\-\-upgrade\fR
+.RS 4
+Selects the upgrade function, which upgrades the database to the latest version of the schema\&. This is incompatible with the \-\-upgrade option\&.
+.sp
+The upgrade function will upgrade a BIND 10 database \- no matter how old the schema \- preserving all data\&. A backup file is created before the upgrade (with the same name as the database, but with "\&.backup" suffixed to it)\&. If the upgrade fails, this file can be copied back to restore the original database\&.
+.RE
+.PP
+\fB\-\-verbose\fR
+.RS 4
+Enable verbose mode\&. Each SQL command issued by the utility will be printed to stderr before it is executed\&.
+.RE
+.PP
+\fB\-\-quiet\fR
+.RS 4
+Enable quiet mode\&. No output is printed, except errors during command\-line argument parsing, or the user confirmation dialog\&.
+.RE
+.PP
+\fB\fIdbfile\fR\fR
+.RS 4
+Name of the database file to check of upgrade\&.
+.RE
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2012 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/src/bin/dbutil/b10-dbutil.xml b/src/bin/dbutil/b10-dbutil.xml
new file mode 100644
index 0000000..c1c0dee
--- /dev/null
+++ b/src/bin/dbutil/b10-dbutil.xml
@@ -0,0 +1,192 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+               "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+	       [<!ENTITY mdash "—">]>
+<!--
+ - 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.
+-->
+
+<refentry>
+
+  <refentryinfo>
+    <date>March 20, 2012</date>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>b10-dbutil</refentrytitle>
+    <manvolnum>8</manvolnum>
+    <refmiscinfo>BIND10</refmiscinfo>
+  </refmeta>
+
+  <refnamediv>
+    <refname>b10-dbutil</refname>
+    <refpurpose>Zone Database Maintenance Utility</refpurpose>
+  </refnamediv>
+
+  <docinfo>
+    <copyright>
+      <year>2012</year>
+      <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+    </copyright>
+  </docinfo>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>b10-dbutil --check</command>
+        <arg>--verbose</arg>
+        <arg>--quiet</arg>
+        <arg><replaceable choice='req'>dbfile</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>b10-dbutil --upgrade</command>
+        <arg>--noconfirm</arg>
+        <arg>--verbose</arg>
+        <arg>--quiet</arg>
+        <arg><replaceable choice='req'>dbfile</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>DESCRIPTION</title>
+    <para>
+      The <command>b10-dbutil</command> utility is a general administration
+      utility for SQL databases. (Currently only SQLite is supported by
+      BIND 10.)  It can report the current verion of the schema, and upgrade
+      an existing database to the latest version of the schema.
+    </para>
+
+    <para>
+      <command>b10-dbutil</command> operates in one of two modes, check mode
+      or upgrade mode.
+    </para>
+
+    <para>
+      In check mode (<command>b10-dbutil --check</command>), the
+      utility reads the version of the database schema from the database
+      and prints it.  It will tell you whether the schema is at the latest
+      version supported by BIND 10. Exit status is 0 if the schema is at
+      the correct version, 1 if the schema is at an older version, 2 if
+      the schema is at a version not yet supported by this version of
+      b10-dbutil. Any higher value indicates an error during command-line
+      parsing or execution.
+    </para>
+
+    <para>
+      When the upgrade function is selected
+      (<command>b10-dbutil --upgrade</command>), the
+      utility takes a copy of the database, then upgrades it to the latest
+      version of the schema.  The contents of the database remain intact.
+      (The backup file is a file in the same directory as the database
+      file.  It has the same name, with ".backup" appended to it.  If a
+      file of that name already exists, the file will have the suffix
+      ".backup-1".  If that exists, the file will be suffixed ".backup-2",
+      and so on). Exit status is 0 if the upgrade is either succesful or
+      aborted by the user, and non-zero if there is an error.
+    </para>
+
+    <para>
+    When upgrading the database, it is <emphasis>strongly</emphasis>
+    recommended that BIND 10 not be running while the upgrade is in
+    progress.
+    </para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>ARGUMENTS</title>
+
+    <para>The arguments are as follows:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term>
+         <option>--check</option>
+        </term>
+        <listitem>
+          <para>Selects the version check function, which reports the
+          current version of the database.  This is incompatible
+          with the --upgrade option.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+         <option>--noconfirm</option>
+        </term>
+        <listitem>
+          <para>Only valid with --upgrade, this disables the prompt.
+          Normally the utility will print a warning that an upgrade is
+          about to take place and request that you type "Yes" to continue.
+          If this switch is given on the command line, no prompt will
+          be issued: the utility will just perform the upgrade.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+         <option>--upgrade</option>
+        </term>
+        <listitem>
+          <para>Selects the upgrade function, which upgrades the database
+          to the latest version of the schema.  This is incompatible
+          with the --upgrade option.
+          </para>
+          <para>
+          The upgrade function will upgrade a BIND 10 database - no matter how
+          old the schema - preserving all data.  A backup file is created
+          before the upgrade (with the same name as the database, but with
+          ".backup" suffixed to it).  If the upgrade fails, this file can
+          be copied back to restore the original database.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+         <option>--verbose</option>
+        </term>
+        <listitem>
+          <para>Enable verbose mode.  Each SQL command issued by the
+          utility will be printed to stderr before it is executed.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+         <option>--quiet</option>
+        </term>
+        <listitem>
+          <para>Enable quiet mode. No output is printed, except errors during
+            command-line argument parsing, or the user confirmation dialog.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>
+        <option><replaceable choice='req'>dbfile</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+          Name of the database file to check of upgrade.
+          </para>
+        </listitem>
+      </varlistentry>
+
+
+    </variablelist>
+  </refsect1>
+</refentry>
diff --git a/src/bin/dbutil/dbutil.py.in b/src/bin/dbutil/dbutil.py.in
new file mode 100755
index 0000000..81f351e
--- /dev/null
+++ b/src/bin/dbutil/dbutil.py.in
@@ -0,0 +1,608 @@
+#!@PYTHON@
+
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+ at file Dabase Utilities
+
+This file holds the "dbutil" program, a general utility program for doing
+management of the BIND 10 database.  There are two modes of operation:
+
+      b10-dbutil --check [--verbose] database
+      b10-dbutil --upgrade [--noconfirm] [--verbose] database
+
+The first form checks the version of the given database.  The second form
+upgrades the database to the latest version of the schema, omitting the
+warning prompt if --noconfirm is given.
+
+For maximum safety, prior to the upgrade a backup database is created.
+The is the database name with ".backup" appended to it (or ".backup-n" if
+".backup" already exists).  This is used to restore the database if the
+upgrade fails.
+"""
+
+# Exit codes
+# These are defined here because one of them is already used before most
+# of the import statements.
+EXIT_SUCCESS = 0
+EXIT_NEED_UPDATE = 1
+EXIT_VERSION_TOO_HIGH = 2
+EXIT_COMMAND_ERROR = 3
+EXIT_READ_ERROR = 4
+EXIT_UPGRADE_ERROR = 5
+EXIT_UNCAUGHT_EXCEPTION = 6
+
+import sys; sys.path.append("@@PYTHONPATH@@")
+
+# Normally, python exits with a status code of 1 on uncaught exceptions
+# Since we reserve exit status 1 for 'database needs upgrade', we
+# override the excepthook to exit with a different status
+def my_except_hook(a, b, c):
+    sys.__excepthook__(a,b,c)
+    sys.exit(EXIT_UNCAUGHT_EXCEPTION)
+sys.excepthook = my_except_hook
+
+import os, sqlite3, shutil
+from optparse import OptionParser
+import isc.util.process
+import isc.log
+from isc.log_messages.dbutil_messages import *
+
+isc.log.init("b10-dbutil")
+logger = isc.log.Logger("dbutil")
+isc.util.process.rename()
+
+TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
+
+
+# @brief Version String
+# This is the version displayed to the user.  It comprises the module name,
+# the module version number, and the overall BIND 10 version number (set in
+# configure.ac)
+VERSION = "b10-dbutil 20120319 (BIND 10 @PACKAGE_VERSION@)"
+
+# @brief Statements to Update the Database
+# These are in the form of a list of dictionaries, each of which contains the
+# information to perform an incremental upgrade from one version of the
+# database to the next.  The information is:
+#
+# a) from: (major, minor) version that the database is expected to be at
+#    to perform this upgrade.
+# b) to: (major, minor) version of the database to which this set of statements
+#    upgrades the database to.  (This is used for documentation purposes,
+#    and to update the schema_version table when the upgrade is complete.)
+# c) statements: List of SQL statments to perform the upgrade.
+#
+# The incremental upgrades are performed one after the other.  If the version
+# of the database does not exactly match that required for the incremental
+# upgrade, the upgrade is skipped.  For this reason, the list must be in
+# ascending order (e.g. upgrade 1.0 to 2.0, 2.0 to 2.1, 2.1 to 2.2 etc.).
+#
+# Note that apart from the 1.0 to 2.0 upgrade, no upgrade need alter the
+# schema_version table: that is done by the upgrade process using the
+# information in the "to" field.
+UPGRADES = [
+    {'from': (1, 0), 'to': (2, 0),
+        'statements': [
+
+            # Move to the latest "V1" state of the database if not there
+            # already.
+            "CREATE TABLE IF NOT EXISTS diffs (" +
+                "id INTEGER PRIMARY KEY, " +
+                "zone_id INTEGER NOT NULL," +
+                "version INTEGER NOT NULL, " +
+                "operation INTEGER NOT NULL, " +
+                "name STRING NOT NULL COLLATE NOCASE, " +
+                "rrtype STRING NOT NULL COLLATE NOCASE, " +
+                "ttl INTEGER NOT NULL, " +
+                "rdata STRING NOT NULL)",
+
+            # Within SQLite with can only rename tables and add columns; we
+            # can't drop columns nor can we alter column characteristics.
+            # So the strategy is to rename the table, create the new table,
+            # then copy all data across.  This means creating new indexes
+            # as well; these are created after the data has been copied.
+
+            # zones table
+            "DROP INDEX zones_byname",
+            "ALTER TABLE zones RENAME TO old_zones",
+            "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)",
+            "INSERT INTO ZONES " +
+                "SELECT id, name, rdclass, dnssec FROM old_zones",
+            "CREATE INDEX zones_byname ON zones (name)",
+            "DROP TABLE old_zones",
+
+            # records table
+            "DROP INDEX records_byname",
+            "DROP INDEX records_byrname",
+            "ALTER TABLE records RENAME TO old_records",
+            "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)",
+            "INSERT INTO records " +
+                "SELECT id, zone_id, name, rname, ttl, rdtype, sigtype, " +
+                    "rdata FROM old_records",
+            "CREATE INDEX records_byname ON records (name)",
+            "CREATE INDEX records_byrname ON records (rname)",
+            "CREATE INDEX records_bytype_and_rname ON records (rdtype, rname)",
+            "DROP TABLE old_records",
+
+            # nsec3 table
+            "DROP INDEX nsec3_byhash",
+            "ALTER TABLE nsec3 RENAME TO old_nsec3",
+            "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)",
+            "INSERT INTO nsec3 " +
+                "SELECT id, zone_id, hash, owner, ttl, rdtype, rdata " +
+                    "FROM old_nsec3",
+            "CREATE INDEX nsec3_byhash ON nsec3 (hash)",
+            "DROP TABLE old_nsec3",
+
+            # diffs table
+            "ALTER TABLE diffs RENAME TO old_diffs",
+            "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)",
+            "INSERT INTO diffs " +
+                "SELECT id, zone_id, version, operation, name, rrtype, " +
+                    "ttl, rdata FROM old_diffs",
+            "DROP TABLE old_diffs",
+
+            # Schema table.  This is updated to include a second column for
+            # future changes.  The idea is that if a version of BIND 10 is
+            # written for schema M.N, it should be able to work for all
+            # versions of N; if not, M must be incremented.
+            #
+            # For backwards compatibility, the column holding the major
+            # version number is left named "version".
+            "ALTER TABLE schema_version " +
+                "ADD COLUMN minor INTEGER NOT NULL DEFAULT 0"
+        ]
+    }
+
+# To extend this, leave the above statements in place and add another
+# dictionary to the list.  The "from" version should be (2, 0), the "to" 
+# version whatever the version the update is to, and the SQL statements are
+# the statements required to perform the upgrade.  This way, the upgrade
+# program will be able to upgrade both a V1.0 and a V2.0 database.
+]
+
+class DbutilException(Exception):
+    """
+    @brief Exception class to indicate error exit
+    """
+    pass
+
+class Database:
+    """
+    @brief Database Encapsulation
+
+    Encapsulates the SQL database, both the connection and the cursor.  The
+    methods will cause a program exit on any error.
+    """
+    def __init__(self, db_file):
+        """
+        @brief Constructor
+
+        @param db_file Name of the database file
+        """
+        self.connection = None
+        self.cursor = None
+        self.db_file = db_file
+        self.backup_file = None
+
+    def open(self):
+        """
+        @brief Open Database
+
+        Opens the passed file as an sqlite3 database and stores a connection
+        and a cursor.
+        """
+        if not os.path.exists(self.db_file):
+            raise DbutilException("database " + self.db_file +
+                                 " does not exist");
+
+        try:
+            self.connection = sqlite3.connect(self.db_file)
+            self.connection.isolation_level = None  # set autocommit
+            self.cursor = self.connection.cursor()
+        except sqlite3.OperationalError as ex:
+            raise DbutilException("unable to open " + self.db_file +
+                                  " - " + str(ex))
+
+    def close(self):
+        """
+        @brief Closes the database
+        """
+        if self.connection is not None:
+            self.connection.close()
+
+    def execute(self, statement):
+        """
+        @brief Execute Statement
+
+        Executes the given statement, exiting the program on error.
+
+        @param statement SQL statement to execute
+        """
+        logger.debug(TRACE_BASIC, DBUTIL_EXECUTE, statement)
+
+        try:
+            self.cursor.execute(statement)
+        except Exception as ex:
+            logger.error(DBUTIL_STATEMENT_ERROR, statement, ex)
+            raise DbutilException(str(ex))
+
+    def result(self):
+        """
+        @brief Return result of last execute
+
+        Returns a single row that is the result of the last "execute".
+        """
+        return self.cursor.fetchone()
+
+    def backup(self):
+        """
+        @brief Backup Database
+
+        Attempts to copy the given database file to a backup database, the
+        backup database file being the file name with ".backup" appended.
+        If the ".backup" file exists, a new name is constructed by appending
+        ".backup-n" (n starting at 1) and the action repeated until an
+        unused filename is found.
+
+        @param db_file Database file to backup
+        """
+        if not os.path.exists(self.db_file):
+            raise DbutilException("database " + self.db_file +
+                                  " does not exist");
+
+        self.backup_file = self.db_file + ".backup"
+        count = 0
+        while os.path.exists(self.backup_file):
+            count = count + 1
+            self.backup_file = self.db_file + ".backup-" + str(count)
+
+        # Do the backup
+        shutil.copyfile(self.db_file, self.backup_file)
+        logger.info(DBUTIL_BACKUP, self.db_file, self.backup_file)
+
+def prompt_user():
+    """
+    @brief Prompt the User
+
+    Explains about the upgrade and requests authorisation to continue.
+
+    @return True if user entered 'Yes', False if 'No'
+    """
+    sys.stdout.write(
+"""You have selected the upgrade option.  This will upgrade the schema of the
+selected BIND 10 zone database to the latest version.
+
+The utility will take a copy of the zone database file before executing so, in
+the event of a problem, you will be able to restore the zone database from
+the backup.  To ensure that the integrity of this backup, please ensure that
+BIND 10 is not running before continuing.
+""")
+    yes_entered = False
+    no_entered = False
+    while (not yes_entered) and (not no_entered):
+        sys.stdout.write("Enter 'Yes' to proceed with the upgrade, " +
+                         "'No' to exit the program: \n")
+        response = sys.stdin.readline()
+        if response.lower() == "yes\n":
+            yes_entered = True
+        elif response.lower() == "no\n":
+            no_entered = True
+        else:
+            sys.stdout.write("Please enter 'Yes' or 'No'\n")
+
+    return yes_entered
+
+
+def version_string(version):
+    """
+    @brief Format Database Version
+
+    Converts a (major, minor) tuple into a 'Vn.m' string.
+
+    @param version Version tuple to convert
+
+    @return Version string
+    """
+    return "V" + str(version[0]) + "." + str(version[1])
+
+
+def compare_versions(first, second):
+    """
+    @brief Compare Versions
+
+    Compares two database version numbers.
+
+    @param first First version number to check (in the form of a
+           "(major, minor)" tuple).
+    @param second Second version number to check (in the form of a
+           "(major, minor)" tuple).
+
+    @return -1, 0, +1 if "first" is <, ==, > "second"
+    """
+    if first == second:
+        return 0
+
+    elif ((first[0] < second[0]) or
+          ((first[0] == second[0]) and (first[1] < second[1]))):
+        return -1
+
+    else:
+        return 1
+
+
+def get_latest_version():
+    """
+    @brief Returns the version to which this utility can upgrade the database
+
+    This is the 'to' version held in the last element of the upgrades list
+    """
+    return UPGRADES[-1]['to']
+
+
+def get_version(db):
+    """
+    @brief Return version of database
+
+    @return Version of database in form (major version, minor version)
+    """
+
+    # Get the version information.
+    db.execute("SELECT * FROM schema_version")
+    result = db.result()
+    if result is None:
+        raise DbutilException("nothing in schema_version table")
+
+    major = result[0]
+    if (major == 1):
+        # If the version number is 1, there will be no "minor" column, so
+        # assume a minor version number of 0.
+        minor = 0
+    else:
+        minor = result[1]
+
+    result = db.result()
+    if result is not None:
+        raise DbutilException("too many rows in schema_version table")
+
+    return (major, minor)
+
+
+def check_version(db):
+    """
+    @brief Check the version
+
+    Checks the version of the database and the latest version, and advises if
+    an upgrade is needed.
+
+    @param db Database object
+
+    returns 0 if the database is up to date
+    returns EXIT_NEED_UPDATE if the database needs updating
+    returns EXIT_VERSION_TOO_HIGH if the database is at a later version
+            than this program knows about
+    These return values are intended to be passed on to sys.exit.
+    """
+    current = get_version(db)
+    latest = get_latest_version()
+
+    match = compare_versions(current, latest)
+    if match == 0:
+        logger.info(DBUTIL_VERSION_CURRENT, version_string(current))
+        logger.info(DBUTIL_CHECK_OK)
+        return EXIT_SUCCESS
+    elif match < 0:
+        logger.info(DBUTIL_VERSION_LOW, version_string(current),
+                    version_string(latest))
+        logger.info(DBUTIL_CHECK_UPGRADE_NEEDED)
+        return EXIT_NEED_UPDATE
+    else:
+        logger.warn(DBUTIL_VERSION_HIGH, version_string(current),
+                    version_string(get_latest_version()))
+        logger.info(DBUTIL_UPGRADE_DBUTIL)
+        return EXIT_VERSION_TOO_HIGH
+
+def perform_upgrade(db, upgrade):
+    """
+    @brief Perform upgrade
+
+    Performs the upgrade.  At the end of the upgrade, updates the schema_version
+    table with the expected version.
+
+    @param db Database object
+    @param upgrade Upgrade dictionary, holding "from", "to" and "statements".
+    """
+    logger.info(DBUTIL_UPGRADING, version_string(upgrade['from']),
+         version_string(upgrade['to']))
+    for statement in upgrade['statements']:
+        db.execute(statement)
+
+    # Update the version information
+    db.execute("DELETE FROM schema_version")
+    db.execute("INSERT INTO schema_version VALUES (" +
+                    str(upgrade['to'][0]) + "," + str(upgrade['to'][1]) + ")")
+
+
+def perform_all_upgrades(db):
+    """
+    @brief Performs all the upgrades
+
+    @brief db Database object
+
+    For each upgrade, checks that the database is at the expected version.
+    If so, calls perform_upgrade to update the database.
+    """
+    match = compare_versions(get_version(db), get_latest_version())
+    if match == 0:
+        logger.info(DBUTIL_UPGRADE_NOT_NEEDED)
+
+    elif match > 0:
+        logger.warn(DBUTIL_UPGRADE_NOT_POSSIBLE)
+
+    else:
+        # Work our way through all upgrade increments
+        count = 0
+        for upgrade in UPGRADES:
+            if compare_versions(get_version(db), upgrade['from']) == 0:
+                perform_upgrade(db, upgrade)
+                count = count + 1
+
+        if count > 0:
+            logger.info(DBUTIL_UPGRADE_SUCCESFUL)
+        else:
+            # Should not get here, as we established earlier that the database
+            # was not at the latest version so we should have upgraded.
+            raise DbutilException("internal error in upgrade tool - no " +
+                                  "upgrade was performed on an old version " +
+                                  "the database")
+
+
+def parse_command():
+    """
+    @brief Parse Command
+
+    Parses the command line and sets the global command options.
+
+    @return Tuple of parser options and parser arguments
+    """
+    usage = ("usage: %prog --check [options] db_file\n" +
+             "       %prog --upgrade [--noconfirm] [options] db_file")
+    parser = OptionParser(usage = usage, version = VERSION)
+    parser.add_option("-c", "--check", action="store_true",
+                      dest="check", default=False,
+                      help="Print database version and check if it " +
+                           "needs upgrading")
+    parser.add_option("-n", "--noconfirm", action="store_true",
+                      dest="noconfirm", default=False,
+                      help="Do not prompt for confirmation before upgrading")
+    parser.add_option("-u", "--upgrade", action="store_true",
+                      dest="upgrade", default=False,
+                      help="Upgrade the database file to the latest version")
+    parser.add_option("-v", "--verbose", action="store_true",
+                      dest="verbose", default=False,
+                      help="Print SQL statements as they are executed")
+    parser.add_option("-q", "--quiet", action="store_true",
+                      dest="quiet", default=False,
+                      help="Don't print any info, warnings or errors")
+    (options, args) = parser.parse_args()
+
+    # Set the database file on which to operate
+    if (len(args) > 1):
+        logger.error(DBUTIL_TOO_MANY_ARGUMENTS)
+        parser.print_usage()
+        sys.exit(EXIT_COMMAND_ERROR)
+    elif len(args) == 0:
+        logger.error(DBUTIL_NO_FILE)
+        parser.print_usage()
+        sys.exit(EXIT_COMMAND_ERROR)
+
+    # Check for conflicting options.  If some are found, output a suitable
+    # error message and print the usage.
+    if options.check and options.upgrade:
+        logger.error(DBUTIL_COMMAND_UPGRADE_CHECK)
+    elif (not options.check) and (not options.upgrade):
+        logger.error(DBUTIL_COMMAND_NONE)
+    elif (options.check and options.noconfirm):
+        logger.error(DBUTIL_CHECK_NOCONFIRM)
+    else:
+        return (options, args)
+
+    # Only get here on conflicting options
+    parser.print_usage()
+    sys.exit(EXIT_COMMAND_ERROR)
+
+
+if __name__ == "__main__":
+    (options, args) = parse_command()
+
+    if options.verbose:
+        isc.log.init("b10-dbutil", "DEBUG", 99)
+        logger = isc.log.Logger("dbutil")
+    elif options.quiet:
+        # We don't use FATAL, so setting the logger to use
+        # it should essentially make it silent.
+        isc.log.init("b10-dbutil", "FATAL")
+        logger = isc.log.Logger("dbutil")
+
+    db = Database(args[0])
+    exit_code = EXIT_SUCCESS
+
+    logger.info(DBUTIL_FILE, args[0])
+    if options.check:
+        # Check database - open, report, and close
+        try:
+            db.open()
+            exit_code = check_version(db)
+            db.close()
+        except Exception as ex:
+            logger.error(DBUTIL_CHECK_ERROR, ex)
+            exit_code = EXIT_READ_ERROR
+
+    elif options.upgrade:
+        # Upgrade.  Check if this is what they really want to do
+        if not options.noconfirm:
+            proceed = prompt_user()
+            if not proceed:
+                logger.info(DBUTIL_UPGRADE_CANCELED)
+                sys.exit(EXIT_SUCCESS)
+
+        # It is.  Do a backup then do the upgrade.
+        in_progress = False
+        try:
+            db.backup()
+            db.open()
+            in_progress = True
+            perform_all_upgrades(db)
+            db.close()
+        except Exception as ex:
+            if in_progress:
+                logger.error(DBUTIL_UPGRADE_FAILED, ex)
+                logger.warn(DBUTIL_DATABASE_MAY_BE_CORRUPT, db.db_file,
+                            db.backup_file)
+            else:
+                logger.error(DBUTIL_UPGRADE_PREPARATION_FAILED, ex)
+                logger.info(DBUTIL_UPGRADE_NOT_ATTEMPTED)
+            exit_code = EXIT_UPGRADE_ERROR
+
+    sys.exit(exit_code)
diff --git a/src/bin/dbutil/dbutil_messages.mes b/src/bin/dbutil/dbutil_messages.mes
new file mode 100644
index 0000000..90ede92
--- /dev/null
+++ b/src/bin/dbutil/dbutil_messages.mes
@@ -0,0 +1,114 @@
+# 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.
+
+# No namespace declaration - these constants go in the global namespace
+# of the ddns messages python module.
+
+# When you add a message to this file, it is a good idea to run
+# <topsrcdir>/tools/reorder_message_file.py to make sure the
+# messages are in the correct order.
+
+% DBUTIL_BACKUP created backup of %1 in %2
+A backup for the given database file was created. Same of original file and
+backup are given in the output message.
+
+% DBUTIL_CHECK_ERROR unable to check database version: %1
+There was an error while trying to check the current version of the database
+schema. The error is shown in the message.
+
+% DBUTIL_CHECK_NOCONFIRM --noconfirm is not compatible with --check
+b10-dbutil was called with --check and --noconfirm. --noconfirm only has
+meaning with --upgrade, so this is considered an error.
+
+% DBUTIL_CHECK_OK this is the latest version of the database schema. No upgrade is required
+The database schema version has been checked, and is up to date.
+No action is required.
+
+% DBUTIL_CHECK_UPGRADE_NEEDED re-run this program with the --upgrade switch to upgrade
+The database schema version is not up to date, and an update is required.
+Please run the dbutil tool again, with the --upgrade argument.
+
+% DBUTIL_COMMAND_NONE must select one of --check or --upgrade
+b10-dbutil was called with neither --check nor --upgrade. One action must be
+provided.
+
+% DBUTIL_COMMAND_UPGRADE_CHECK --upgrade is not compatible with --check
+b10-dbutil was called with both the commands --upgrade and --check. Only one
+action can be performed at a time.
+
+% DBUTIL_DATABASE_MAY_BE_CORRUPT database file %1 may be corrupt, restore it from backup (%2)
+The upgrade failed while it was in progress; the database may now be in an
+inconsistent state, and it is advised to restore it from the backup that was
+created when b10-dbutil started.
+
+% DBUTIL_EXECUTE Executing SQL statement: %1
+Debug message; the given SQL statement is executed
+
+% DBUTIL_FILE Database file: %1
+The database file that is being checked.
+
+% DBUTIL_NO_FILE must supply name of the database file to upgrade
+b10-dbutil was called without a database file. Currently, it cannot find this
+file on its own, and it must be provided.
+
+% DBUTIL_STATEMENT_ERROR failed to execute %1: %2
+The given database statement failed to execute. The error is shown in the
+message.
+
+% DBUTIL_TOO_MANY_ARGUMENTS too many arguments to the command, maximum of one expected
+There were too many command-line arguments to b10-dbutil
+
+% DBUTIL_UPGRADE_CANCELED upgrade canceled; database has not been changed
+The user aborted the upgrade, and b10-dbutil will now exit.
+
+% DBUTIL_UPGRADE_DBUTIL please get the latest version of b10-dbutil and re-run
+A database schema was found that was newer than this version of dbutil, which
+is apparently out of date and should be upgraded itself.
+
+% DBUTIL_UPGRADE_FAILED upgrade failed: %1
+While the upgrade was in progress, an unexpected error occurred. The error
+is shown in the message.
+
+% DBUTIL_UPGRADE_NOT_ATTEMPTED database upgrade was not attempted
+Due to the earlier failure, the database schema upgrade was not attempted,
+and b10-dbutil will now exit.
+
+% DBUTIL_UPGRADE_NOT_NEEDED database already at latest version, no upgrade necessary
+b10-dbutil was told to upgrade the database schema, but it is already at the
+latest version.
+
+% DBUTIL_UPGRADE_NOT_POSSIBLE database at a later version than this utility can support
+b10-dbutil was told to upgrade the database schema, but it is at a higher
+version than this tool currently supports. Please update b10-dbutil and try
+again.
+
+% DBUTIL_UPGRADE_PREPARATION_FAILED upgrade preparation failed: %1
+An unexpected error occurred while b10-dbutil was preparing to upgrade the
+database schema. The error is shown in the message
+
+% DBUTIL_UPGRADE_SUCCESFUL database upgrade successfully completed
+The database schema update was completed successfully.
+
+% DBUTIL_UPGRADING upgrading database from %1 to %2
+An upgrade is in progress, the versions of the current upgrade action are shown.
+
+% DBUTIL_VERSION_CURRENT database version %1
+The current version of the database schema.
+
+% DBUTIL_VERSION_HIGH database is at a later version (%1) than this program can cope with (%2)
+The database schema is at a higher version than b10-dbutil knows about.
+
+% DBUTIL_VERSION_LOW database version %1, latest version is %2.
+The database schema is not up to date, the current version and the latest
+version are in the message.
diff --git a/src/bin/dbutil/run_dbutil.sh.in b/src/bin/dbutil/run_dbutil.sh.in
new file mode 100755
index 0000000..fea7482
--- /dev/null
+++ b/src/bin/dbutil/run_dbutil.sh.in
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
+export PYTHON_EXEC
+
+DBUTIL_PATH=@abs_top_builddir@/src/bin/dbutil
+
+PYTHONPATH=@abs_top_srcdir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/bin:@abs_top_srcdir@/src/lib/python
+export PYTHONPATH
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
+	export @ENV_LIBRARY_PATH@
+fi
+
+B10_FROM_SOURCE=@abs_top_srcdir@
+export B10_FROM_SOURCE
+
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE
+
+exec ${PYTHON_EXEC} -O ${DBUTIL_PATH}/b10-dbutil "$@"
diff --git a/src/bin/dbutil/tests/.gitignore b/src/bin/dbutil/tests/.gitignore
new file mode 100644
index 0000000..8248611
--- /dev/null
+++ b/src/bin/dbutil/tests/.gitignore
@@ -0,0 +1,2 @@
+/dbutil_test.sh
+/dbutil_test_verfile_*
diff --git a/src/bin/dbutil/tests/Makefile.am b/src/bin/dbutil/tests/Makefile.am
new file mode 100644
index 0000000..c03b262
--- /dev/null
+++ b/src/bin/dbutil/tests/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = . testdata
+
+# Tests of the update script.
+
+check-local:
+	$(SHELL) $(abs_builddir)/dbutil_test.sh
diff --git a/src/bin/dbutil/tests/dbutil_test.sh.in b/src/bin/dbutil/tests/dbutil_test.sh.in
new file mode 100755
index 0000000..f82eeb0
--- /dev/null
+++ b/src/bin/dbutil/tests/dbutil_test.sh.in
@@ -0,0 +1,481 @@
+#!/bin/sh
+# 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.
+
+# Checks that the logger will limit the output of messages less severe than
+# the severity/debug setting.
+
+testname="Database Upgrade Test"
+echo $testname
+
+failcount=0
+tempfile=@abs_builddir@/dbutil_test_tempfile_$$
+backupfile=${tempfile}.backup
+testdata=@abs_srcdir@/testdata
+verfile=@abs_builddir@/dbutil_test_verfile_$$
+
+# @brief Record a success
+succeed() {
+    echo "--- PASS"
+}
+
+
+# @brief Record a fail
+#
+# @param $1 Optional additional reason to output
+fail() {
+    if [ "$1" != "" ]
+    then
+        echo "ERROR: $1"
+    fi
+    echo "*** FAIL"
+    failcount=`expr $failcount + 1`
+}
+
+
+# @brief Record a pass if the argument is zero
+#
+# @param $1 Value to test
+passzero() {
+    if [ $1 -eq 0 ]; then
+        succeed
+    else
+        fail
+    fi
+}
+
+
+# @brief Record a fail if the argument is non-zero
+#
+# @param $1 Value to test
+failzero() {
+    if [ $1 -ne 0 ]; then
+        succeed
+    else
+        fail
+    fi
+}
+
+
+# @brief Copy File
+#
+# Executes a "cp" operation followed by a "chmod" to make the target writeable.
+#
+# @param $1 Source file
+# @param $2 Target file
+copy_file () {
+    cp $1 $2
+    chmod a+w $2
+}
+
+
+
+# @brief Check backup file
+#
+# Record a failure if the backup file does not exist or if it is different
+# to the data file. (N.B. No success is recorded if they are the same.)
+#
+# @param $1 Source database file
+# @param $2 Backup file
+check_backup() {
+    if [ ! -e $1 ]
+    then
+        fail "database file $1 not found"
+
+    elif [ ! -e $2 ]
+    then
+        fail "backup file $2 not found"
+
+    else
+        diff $1 $2 > /dev/null
+        if [ $? -ne 0 ]
+        then
+            fail "database file $1 different to backup file $2"
+        fi
+    fi
+}
+
+
+# @brief Check No Backup File
+#
+# Record a failure if the backup file exists.  (N.B. No success is recorded if
+# it does not.)
+#
+# @param $1 Source database file (unused, present for symmetry)
+# @param $2 Backup file
+check_no_backup() {
+    if [ -e $2 ]
+    then
+        fail "backup of database $2 exists when it should not"
+    fi
+}
+
+
+# @brief Get Database Schema
+#
+# As the schema stored in the database is format-dependent - how it is printed
+# depends on how the commands were entered (on one line, split across two
+# lines etc.) - comparing schema is awkward.
+#
+# The function sets the local variable db_schema to the output of the
+# .schema command, with spaces removed and upper converted to lowercase.
+#
+# The database is copied before the schema is taken (and removed after)
+# as SQLite3 assummes a writeable database, which may not be the case if
+# getting the schema from a reference copy.
+#
+# @param $1 Database for which the schema is required
+get_schema() {
+    db1=@abs_builddir@/dbutil_test_schema_$$
+    copy_file $1 $db1
+
+    db_schema=`sqlite3 $db1 '.schema' | \
+               awk '{line = line $0} END {print line}' | \
+               sed -e 's/ //g' | \
+               tr [:upper:] [:lower:]`
+    rm -f $db1
+}
+
+
+# @brief Successful Schema Upgrade Test
+#
+# This test is done where the upgrade is expected to be successful - when
+# the end result of the test is that the test database is upgraded to a
+# database of the expected schema.
+#
+# Note: the caller must ensure that $tempfile and $backupfile do not exist
+#       on entry, and is responsible for removing them afterwards.
+#
+# @param $1 Database to upgrade
+# @param $2 Expected backup file
+upgrade_ok_test() {
+    copy_file $1 $tempfile
+    ../run_dbutil.sh --upgrade --noconfirm $tempfile
+    if [ $? -eq 0 ]
+    then
+        # Compare schema with the reference
+        get_schema $testdata/v2_0.sqlite3
+        expected_schema=$db_schema
+        get_schema $tempfile
+        actual_schema=$db_schema
+        if [ "$expected_schema" = "$actual_schema" ]
+        then
+            succeed
+        else
+            fail "upgraded schema not as expected"
+        fi
+
+        # Check the version is set correctly
+        check_version $tempfile "V2.0"
+
+        # Check that a backup was made
+        check_backup $1 $2
+    else
+        # Error should have been output already
+        fail
+    fi
+}
+
+
+# @brief Unsuccessful Upgrade Test
+#
+# Checks that an upgrade of the specified database fails.
+#
+# Note: the caller must ensure that $tempfile and $backupfile do not exist
+#       on entry, and is responsible for removing them afterwards.
+#
+# @param $1 Database to upgrade
+# @param $2 Expected backup file
+upgrade_fail_test() {
+    copy_file $1 $tempfile
+    ../run_dbutil.sh --upgrade --noconfirm $tempfile
+    failzero $?
+    check_backup $1 $backupfile
+}
+
+
+# @brief Record Count Test
+#
+# Checks that the count of records in each table is preserved in the upgrade.
+#
+# Note 1: This test assumes that the "diffs" table is present.
+# Note 2: The caller must ensure that $tempfile and $backupfile do not exist
+#         on entry, and is responsible for removing them afterwards.
+#
+# @brief $1 Database to upgrade
+record_count_test() {
+    copy_file $1 $tempfile
+
+    diffs_count=`sqlite3 $tempfile 'select count(*) from diffs'`
+    nsec3_count=`sqlite3 $tempfile 'select count(*) from nsec3'`
+    records_count=`sqlite3 $tempfile 'select count(*) from records'`
+    zones_count=`sqlite3 $tempfile 'select count(*) from zones'`
+
+    ../run_dbutil.sh --upgrade --noconfirm $tempfile
+    if [ $? -ne 0 ]
+    then
+        # Reason for failure should already have been output
+        fail
+    else
+        new_diffs_count=`sqlite3 $tempfile 'select count(*) from diffs'`
+        new_nsec3_count=`sqlite3 $tempfile 'select count(*) from nsec3'`
+        new_records_count=`sqlite3 $tempfile 'select count(*) from records'`
+        new_zones_count=`sqlite3 $tempfile 'select count(*) from zones'`
+
+        if [ $diffs_count -ne $new_diffs_count ]
+        then
+            fail "diffs table was not completely copied"
+        fi
+
+        if [ $nsec3_count -ne $new_nsec3_count ]
+        then
+            fail "nsec3 table was not completely copied"
+        fi
+
+        if [ $records_count -ne $new_records_count ]
+        then
+            fail "records table was not completely copied"
+        fi
+
+        if [ $zones_count -ne $new_zones_count ]
+        then
+            fail "zones table was not completely copied"
+        fi
+
+        # As an extra check, test that the backup was successful
+        check_backup $1 $backupfile
+    fi
+}
+
+
+# @brief Version Check
+#
+# Checks that the database is at the specified version (and so checks the
+# --check function).  On success, a pass is recorded.
+#
+# @param $1 Database to check
+# @param $2 Expected version string
+check_version() {
+    copy_file $1 $verfile
+    ../run_dbutil.sh --check $verfile
+    if [ $? -gt 2 ]
+    then
+        fail "version check failed on database $1; return code $?"
+    else
+        ../run_dbutil.sh --check $verfile 2>&1 | grep "$2" > /dev/null
+        if [ $? -ne 0 ]
+        then
+            fail "database $1 not at expected version $2 (output: $?)"
+        else
+            succeed
+        fi
+    fi
+    rm -f $verfile
+}
+
+
+# @brief Version Check Fail
+#
+# Does a version check but expected the check to fail
+#
+# @param $1 Database to check
+# @param $2 Backup file
+check_version_fail() {
+    copy_file $1 $verfile
+    ../run_dbutil.sh --check $verfile
+    failzero $?
+    check_no_backup $tempfile $backupfile
+}
+
+
+# Main test sequence
+
+rm -f $tempfile $backupfile
+
+# Test 1 - check that the utility fails if the database does not exist
+echo "1.1. Non-existent database - check"
+../run_dbutil.sh --check $tempfile
+failzero $?
+check_no_backup $tempfile $backupfile
+
+echo "1.2. Non-existent database - upgrade"
+../run_dbutil.sh --upgrade --noconfirm $tempfile
+failzero $?
+check_no_backup $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+
+# Test 2 - should fail to check an empty file and fail to upgrade it
+echo "2.1. Database is an empty file - check"
+touch $tempfile
+check_version_fail $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+echo "2.2. Database is an empty file - upgrade"
+touch $tempfile
+../run_dbutil.sh --upgrade --noconfirm $tempfile
+failzero $?
+# A backup is performed before anything else, so the backup should exist.
+check_backup $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "3.1. Database is not an SQLite file - check"
+echo "This is not an sqlite3 database" > $tempfile
+check_version_fail $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+echo "3.2. Database is not an SQLite file - upgrade"
+echo "This is not an sqlite3 database" > $tempfile
+../run_dbutil.sh --upgrade --noconfirm $tempfile
+failzero $?
+# ...and as before, a backup should have been created
+check_backup $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "4.1. Database is an SQLite3 file without the schema table - check"
+check_version_fail $testdata/no_schema.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+echo "4.1. Database is an SQLite3 file without the schema table - upgrade"
+upgrade_fail_test $testdata/no_schema.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "5.1. Database is an old V1 database - check"
+check_version $testdata/old_v1.sqlite3 "V1.0"
+check_no_backup $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+echo "5.2. Database is an old V1 database - upgrade"
+upgrade_ok_test $testdata/old_v1.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "6.1. Database is new V1 database - check"
+check_version $testdata/new_v1.sqlite3 "V1.0"
+check_no_backup $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+echo "6.2. Database is a new V1 database - upgrade"
+upgrade_ok_test $testdata/new_v1.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "7.1. Database is V2.0 database - check"
+check_version $testdata/v2_0.sqlite3 "V2.0"
+check_no_backup $tempfile $backupfile
+rm -f $tempfile $backupfile
+
+echo "7.2. Database is a V2.0 database - upgrade"
+upgrade_ok_test $testdata/v2_0.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "8.1. Database is V2.0 database with empty schema table - check"
+check_version_fail $testdata/empty_version.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+echo "8.2. Database is V2.0 database with empty schema table - upgrade"
+upgrade_fail_test $testdata/empty_version.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "9.1. Database is V2.0 database with over-full schema table - check"
+check_version_fail $testdata/too_many_version.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+echo "9.2. Database is V2.0 database with over-full schema table - upgrade"
+upgrade_fail_test $testdata/too_many_version.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "10.0. Upgrade corrupt database"
+upgrade_fail_test $testdata/corrupt.sqlite3 $backupfile
+rm -f $tempfile $backupfile
+
+
+echo "11. Record count test"
+record_count_test $testdata/new_v1.sqlite3
+rm -f $tempfile $backupfile
+
+
+echo "12. Backup file already exists"
+touch $backupfile
+touch ${backupfile}-1
+upgrade_ok_test $testdata/v2_0.sqlite3 ${backupfile}-2
+rm -f $tempfile $backupfile ${backupfile}-1 ${backupfile}-2
+
+
+echo "13.1 Command-line errors"
+copy_file $testdata/old_v1.sqlite3 $tempfile
+../run_dbutil.sh $tempfile
+failzero $?
+../run_dbutil.sh --upgrade --check $tempfile
+failzero $?
+../run_dbutil.sh --noconfirm --check $tempfile
+failzero $?
+../run_dbutil.sh --check
+failzero $?
+../run_dbutil.sh --upgrade --noconfirm
+failzero $?
+../run_dbutil.sh --check $tempfile $backupfile
+failzero $?
+../run_dbutil.sh --upgrade --noconfirm $tempfile $backupfile
+failzero $?
+rm -f $tempfile $backupfile
+
+echo "13.2 verbose flag"
+copy_file $testdata/old_v1.sqlite3 $tempfile
+../run_dbutil.sh --upgrade --noconfirm --verbose $tempfile
+passzero $?
+rm -f $tempfile $backupfile
+
+echo "13.3 Interactive prompt - yes"
+copy_file $testdata/old_v1.sqlite3 $tempfile
+../run_dbutil.sh --upgrade $tempfile << .
+Yes
+.
+passzero $?
+check_version $tempfile "V2.0"
+rm -f $tempfile $backupfile
+
+echo "13.4 Interactive prompt - no"
+copy_file $testdata/old_v1.sqlite3 $tempfile
+../run_dbutil.sh --upgrade $tempfile << .
+no
+.
+passzero $?
+diff $testdata/old_v1.sqlite3 $tempfile > /dev/null
+passzero $?
+rm -f $tempfile $backupfile
+
+echo "13.5 quiet flag"
+copy_file $testdata/old_v1.sqlite3 $tempfile
+../run_dbutil.sh --check --quiet $tempfile 2>&1 | grep .
+failzero $?
+rm -f $tempfile $backupfile
+
+# Report the result
+if [ $failcount -eq 0 ]; then
+    echo "PASS: $testname"
+elif [ $failcount -eq 1 ]; then
+    echo "FAIL: $testname - 1 test failed"
+else
+    echo "FAIL: $testname - $failcount tests failed"
+fi
+
+# Exit with appropriate error status
+exit $failcount
diff --git a/src/bin/dbutil/tests/testdata/Makefile.am b/src/bin/dbutil/tests/testdata/Makefile.am
new file mode 100644
index 0000000..0d850a7
--- /dev/null
+++ b/src/bin/dbutil/tests/testdata/Makefile.am
@@ -0,0 +1,12 @@
+EXTRA_DIST =
+EXTRA_DIST += corrupt.sqlite3
+EXTRA_DIST += empty_schema.sqlite3
+EXTRA_DIST += empty_v1.sqlite3
+EXTRA_DIST += empty_version.sqlite3
+EXTRA_DIST += invalid_v1.sqlite3
+EXTRA_DIST += new_v1.sqlite3
+EXTRA_DIST += no_schema.sqlite3
+EXTRA_DIST += old_v1.sqlite3
+EXTRA_DIST += README
+EXTRA_DIST += too_many_version.sqlite3
+EXTRA_DIST += v2_0.sqlite3
diff --git a/src/bin/dbutil/tests/testdata/README b/src/bin/dbutil/tests/testdata/README
new file mode 100644
index 0000000..83ce01f
--- /dev/null
+++ b/src/bin/dbutil/tests/testdata/README
@@ -0,0 +1,41 @@
+The versioning of BIND 10 databases to date has not been the best:
+
+The original database is known here as the "old V1" schema.  It had a
+schema_version table, with the single "version" value set to 1.
+
+The schema was then updated with a "diffs" table.  This is referred to
+here as the "new V1" schema.
+
+The Spring 2012 release of BIND 10 modified the schema.  The
+schema_version table was updated to include a "minor" column, holding the
+minor version number. Other changes to the database included redefining
+"STRING" columns as "TEXT" columns.  This is referred to as the "V2.0
+schema".
+
+The following test data files are present:
+
+empty_schema.sqlite3: A database conforming to the new V1 schema.
+However, there is nothing in the schema_version table.
+
+empty_v1.sqlite3: A database conforming to the new V1 schema.
+The database is empty, except for the schema_version table, where the
+"version" column is set to 1.
+
+empty_version.sqlite3: A database conforming to the V2.0 schema but without
+anything in the schema_version table.
+
+no_schema.sqlite3: A valid SQLite3 database, but without a schema_version
+table.
+
+old_v1.sqlite3: A valid SQLite3 database conforming to the old V1 schema.
+It does not have a diffs table.
+
+invalid_v1.sqlite3: A valid SQLite3 database that, although the schema
+is marked as V1, does not have the nsec3 table.
+
+new_v1.sqlite3: A valid SQLite3 database with data in all the tables
+(although the single rows in both the nsec3 and diffs table make no
+sense, but are valid).
+
+too_many_version.sqlite3: A database conforming to the V2.0 schema but with
+too many rows of data.
diff --git a/src/bin/dbutil/tests/testdata/corrupt.sqlite3 b/src/bin/dbutil/tests/testdata/corrupt.sqlite3
new file mode 100644
index 0000000..69683b7
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/corrupt.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/empty_schema.sqlite3 b/src/bin/dbutil/tests/testdata/empty_schema.sqlite3
new file mode 100644
index 0000000..b803149
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/empty_schema.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/empty_v1.sqlite3 b/src/bin/dbutil/tests/testdata/empty_v1.sqlite3
new file mode 100644
index 0000000..5ad2136
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/empty_v1.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/empty_version.sqlite3 b/src/bin/dbutil/tests/testdata/empty_version.sqlite3
new file mode 100644
index 0000000..b820fa9
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/empty_version.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/invalid_v1.sqlite3 b/src/bin/dbutil/tests/testdata/invalid_v1.sqlite3
new file mode 100644
index 0000000..e411fd0
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/invalid_v1.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/new_v1.sqlite3 b/src/bin/dbutil/tests/testdata/new_v1.sqlite3
new file mode 100644
index 0000000..9a885a4
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/new_v1.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/no_schema.sqlite3 b/src/bin/dbutil/tests/testdata/no_schema.sqlite3
new file mode 100644
index 0000000..9dd0614
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/no_schema.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/old_v1.sqlite3 b/src/bin/dbutil/tests/testdata/old_v1.sqlite3
new file mode 100644
index 0000000..32dbb9b
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/old_v1.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/too_many_version.sqlite3 b/src/bin/dbutil/tests/testdata/too_many_version.sqlite3
new file mode 100644
index 0000000..5dc8ae3
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/too_many_version.sqlite3 differ
diff --git a/src/bin/dbutil/tests/testdata/v2_0.sqlite3 b/src/bin/dbutil/tests/testdata/v2_0.sqlite3
new file mode 100644
index 0000000..18784fd
Binary files /dev/null and b/src/bin/dbutil/tests/testdata/v2_0.sqlite3 differ
diff --git a/src/bin/ddns/.gitignore b/src/bin/ddns/.gitignore
new file mode 100644
index 0000000..92b86f3
--- /dev/null
+++ b/src/bin/ddns/.gitignore
@@ -0,0 +1,2 @@
+/b10-ddns
+/ddns.py
diff --git a/src/bin/ddns/b10-ddns.8 b/src/bin/ddns/b10-ddns.8
index 67a5059..131b6cc 100644
--- a/src/bin/ddns/b10-ddns.8
+++ b/src/bin/ddns/b10-ddns.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-ddns
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 9, 2011
+.\"      Date: February 28, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-DDNS" "8" "December 9, 2011" "BIND10" "BIND10"
+.TH "B10\-DDNS" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -81,8 +81,10 @@ The module commands are:
 .PP
 
 \fBshutdown\fR
-Exits
-\fBb10\-ddns\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+exits
+\fBb10\-ddns\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .SH "SEE ALSO"
 .PP
 
@@ -98,5 +100,5 @@ The
 daemon was first implemented in December 2011 for the ISC BIND 10 project\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/ddns/b10-ddns.xml b/src/bin/ddns/b10-ddns.xml
index b9cd117..15fcb1a 100644
--- a/src/bin/ddns/b10-ddns.xml
+++ b/src/bin/ddns/b10-ddns.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
  -
  - Permission to use, copy, modify, and/or distribute this software for any
  - purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 9, 2011</date>
+    <date>February 28, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2011</year>
+      <year>2011-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -122,8 +122,11 @@
       The module commands are:
     </para>
     <para>
-      <command>shutdown</command> Exits <command>b10-ddns</command>.
-      (Note that the BIND 10 boss process will restart this service.)
+      <command>shutdown</command> exits <command>b10-ddns</command>.
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
     </para>
 
   </refsect1>
diff --git a/src/bin/dhcp4/.gitignore b/src/bin/dhcp4/.gitignore
new file mode 100644
index 0000000..f7e9973
--- /dev/null
+++ b/src/bin/dhcp4/.gitignore
@@ -0,0 +1,3 @@
+/b10-dhcp4
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index 513ae1c..6c52c05 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -15,7 +15,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 CLEANFILES = spec_config.h
 
 man_MANS = b10-dhcp4.8
-EXTRA_DIST = $(man_MANS) dhcp4.spec
+EXTRA_DIST = $(man_MANS) b10-dhcp4.xml dhcp4.spec
 
 if ENABLE_MAN
 
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 89b48c6..d065eda 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -59,10 +59,9 @@ Dhcpv4Srv::~Dhcpv4Srv() {
 bool
 Dhcpv4Srv::run() {
     while (!shutdown_) {
-        boost::shared_ptr<Pkt4> query; // client's message
-        boost::shared_ptr<Pkt4> rsp;   // server's response
-
-        query = IfaceMgr::instance().receive4();
+        // client's message and server's response
+        Pkt4Ptr query = IfaceMgr::instance().receive4();
+        Pkt4Ptr rsp;
 
         if (query) {
             try {
@@ -141,14 +140,13 @@ Dhcpv4Srv::setServerID() {
 #if 0
     // uncomment this once ticket 1350 is merged.
     IOAddress srvId("127.0.0.1");
-    serverid_ = boost::shared_ptr<Option>(
+    serverid_ = OptionPtr(
       new Option4AddrLst(Option::V4, DHO_DHCP_SERVER_IDENTIFIER, srvId));
 #endif
 }
 
 
-void Dhcpv4Srv::copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
-                                  boost::shared_ptr<Pkt4>& answer) {
+void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
     answer->setIface(question->getIface());
     answer->setIndex(question->getIndex());
     answer->setCiaddr(question->getCiaddr());
@@ -174,17 +172,17 @@ void Dhcpv4Srv::copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
 
 }
 
-void Dhcpv4Srv::appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type) {
-    boost::shared_ptr<Option> opt;
+void Dhcpv4Srv::appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type) {
+    OptionPtr opt;
 
     // add Message Type Option (type 53)
     std::vector<uint8_t> tmp;
     tmp.push_back(static_cast<uint8_t>(msg_type));
-    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
+    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE, tmp));
     msg->addOption(opt);
 
     // DHCP Server Identifier (type 54)
-    opt = boost::shared_ptr<Option>
+    opt = OptionPtr
         (new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, IOAddress(HARDCODED_SERVER_ID)));
     msg->addOption(opt);
 
@@ -192,47 +190,43 @@ void Dhcpv4Srv::appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_t
 }
 
 
-void Dhcpv4Srv::appendRequestedOptions(boost::shared_ptr<Pkt4>& msg) {
-    boost::shared_ptr<Option> opt;
+void Dhcpv4Srv::appendRequestedOptions(Pkt4Ptr& msg) {
+    OptionPtr opt;
 
     // Domain name (type 15)
     vector<uint8_t> domain(HARDCODED_DOMAIN_NAME.begin(), HARDCODED_DOMAIN_NAME.end());
-    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
+    opt = OptionPtr(new Option(Option::V4, DHO_DOMAIN_NAME, domain));
     msg->addOption(opt);
     // TODO: Add Option_String class
 
     // DNS servers (type 6)
-    opt = boost::shared_ptr<Option>
-        (new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
+    opt = OptionPtr(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS, IOAddress(HARDCODED_DNS_SERVER)));
     msg->addOption(opt);
 }
 
-void Dhcpv4Srv::tryAssignLease(boost::shared_ptr<Pkt4>& msg) {
-    boost::shared_ptr<Option> opt;
+void Dhcpv4Srv::tryAssignLease(Pkt4Ptr& msg) {
+    OptionPtr opt;
 
     // TODO: Implement actual lease assignment here
     msg->setYiaddr(IOAddress(HARDCODED_LEASE));
 
     // IP Address Lease time (type 51)
-    opt = boost::shared_ptr<Option>(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
+    opt = OptionPtr(new Option(Option::V4, DHO_DHCP_LEASE_TIME));
     opt->setUint32(HARDCODED_LEASE_TIME);
     msg->addOption(opt);
     // TODO: create Option_IntArray that holds list of integers, similar to Option4_AddrLst
 
     // Subnet mask (type 1)
-    opt = boost::shared_ptr<Option>
-        (new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
+    opt = OptionPtr(new Option4AddrLst(DHO_SUBNET_MASK, IOAddress(HARDCODED_NETMASK)));
     msg->addOption(opt);
 
     // Router (type 3)
-    opt = boost::shared_ptr<Option>
-        (new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
+    opt = OptionPtr(new Option4AddrLst(DHO_ROUTERS, IOAddress(HARDCODED_GATEWAY)));
     msg->addOption(opt);
 }
 
-boost::shared_ptr<Pkt4>
-Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
-    boost::shared_ptr<Pkt4> offer = boost::shared_ptr<Pkt4>
+Pkt4Ptr Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
+    Pkt4Ptr offer = Pkt4Ptr
         (new Pkt4(DHCPOFFER, discover->getTransid()));
 
     copyDefaultFields(discover, offer);
@@ -244,9 +238,8 @@ Dhcpv4Srv::processDiscover(boost::shared_ptr<Pkt4>& discover) {
     return (offer);
 }
 
-boost::shared_ptr<Pkt4>
-Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
-    boost::shared_ptr<Pkt4> ack = boost::shared_ptr<Pkt4>
+Pkt4Ptr Dhcpv4Srv::processRequest(Pkt4Ptr& request) {
+    Pkt4Ptr ack = Pkt4Ptr
         (new Pkt4(DHCPACK, request->getTransid()));
 
     copyDefaultFields(request, ack);
@@ -258,17 +251,17 @@ Dhcpv4Srv::processRequest(boost::shared_ptr<Pkt4>& request) {
     return (ack);
 }
 
-void Dhcpv4Srv::processRelease(boost::shared_ptr<Pkt4>& release) {
+void Dhcpv4Srv::processRelease(Pkt4Ptr& release) {
     /// TODO: Implement this.
     cout << "Received RELEASE on " << release->getIface() << " interface." << endl;
 }
 
-void Dhcpv4Srv::processDecline(boost::shared_ptr<Pkt4>& decline) {
+void Dhcpv4Srv::processDecline(Pkt4Ptr& decline) {
     /// TODO: Implement this.
     cout << "Received DECLINE on " << decline->getIface() << " interface." << endl;
 }
 
-boost::shared_ptr<Pkt4> Dhcpv4Srv::processInform(boost::shared_ptr<Pkt4>& inform) {
+Pkt4Ptr Dhcpv4Srv::processInform(Pkt4Ptr& inform) {
     /// TODO: Currently implemented echo mode. Implement this for real
     return (inform);
 }
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index cd46977..745a4ec 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,6 @@
 #ifndef DHCPV4_SRV_H
 #define DHCPV4_SRV_H
 
-#include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
 #include <dhcp/dhcp4.h>
 #include <dhcp/pkt4.h>
@@ -68,11 +67,10 @@ protected:
     /// should be served. In particular, a lease is selected and sent
     /// as an offer to a client if it should be served.
     ///
-    /// @param solicit DISCOVER message received from client
+    /// @param discover DISCOVER message received from client
     ///
     /// @return OFFER message or NULL
-    boost::shared_ptr<Pkt4>
-    processDiscover(boost::shared_ptr<Pkt4>& discover);
+    Pkt4Ptr processDiscover(Pkt4Ptr& discover);
 
     /// @brief Processes incoming REQUEST and returns REPLY response.
     ///
@@ -86,7 +84,7 @@ protected:
     /// @param request a message received from client
     ///
     /// @return ACK or NACK message
-    boost::shared_ptr<Pkt4> processRequest(boost::shared_ptr<Pkt4>& request);
+    Pkt4Ptr processRequest(Pkt4Ptr& request);
 
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
@@ -94,17 +92,17 @@ protected:
     /// this function does not return anything.
     ///
     /// @param release message received from client
-    void processRelease(boost::shared_ptr<Pkt4>& release);
+    void processRelease(Pkt4Ptr& release);
 
     /// @brief Stub function that will handle incoming DHCPDECLINE messages.
     ///
     /// @param decline message received from client
-    void processDecline(boost::shared_ptr<Pkt4>& decline);
+    void processDecline(Pkt4Ptr& decline);
 
     /// @brief Stub function that will handle incoming INFORM messages.
     ///
-    /// @param infRequest message received from client
-    boost::shared_ptr<Pkt4> processInform(boost::shared_ptr<Pkt4>& inform);
+    /// @param inform message received from client
+    Pkt4Ptr processInform(Pkt4Ptr& inform);
 
     /// @brief Copies default parameters from client's to server's message
     ///
@@ -113,9 +111,7 @@ protected:
     ///
     /// @param question any message sent by client
     /// @param answer any message server is going to send as response
-    void copyDefaultFields(const boost::shared_ptr<Pkt4>& question,
-                           boost::shared_ptr<Pkt4>& answer);
-
+    void copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer);
 
     /// @brief Appends options requested by client.
     ///
@@ -123,8 +119,7 @@ protected:
     /// (sent in PRL) or are enforced by server.
     ///
     /// @param msg outgoing message (options will be added here)
-    void appendRequestedOptions(boost::shared_ptr<Pkt4>& msg);
-
+    void appendRequestedOptions(Pkt4Ptr& msg);
 
     /// @brief Assigns a lease and appends corresponding options
     ///
@@ -136,20 +131,18 @@ protected:
     /// used fixed, hardcoded lease.
     ///
     /// @param msg OFFER or ACK message (lease options will be added here)
-    void tryAssignLease(boost::shared_ptr<Pkt4>& msg);
-
+    void tryAssignLease(Pkt4Ptr& msg);
 
     /// @brief Appends default options to a message
     ///
     /// @param msg message object (options will be added to it)
     /// @param msg_type specifies message type
-    void appendDefaultOptions(boost::shared_ptr<Pkt4>& msg, uint8_t msg_type);
+    void appendDefaultOptions(Pkt4Ptr& msg, uint8_t msg_type);
 
     /// @brief Returns server-intentifier option
     ///
     /// @return server-id option
-    boost::shared_ptr<isc::dhcp::Option>
-    getServerID() { return serverid_; }
+    OptionPtr getServerID() { return serverid_; }
 
     /// @brief Sets server-identifier.
     ///
@@ -163,7 +156,7 @@ protected:
     void setServerID();
 
     /// server DUID (to be sent in server-identifier option)
-    boost::shared_ptr<isc::dhcp::Option> serverid_;
+    OptionPtr serverid_;
 
     /// indicates if shutdown is in progress. Setting it to true will
     /// initiate server shutdown procedure.
diff --git a/src/bin/dhcp4/tests/.gitignore b/src/bin/dhcp4/tests/.gitignore
new file mode 100644
index 0000000..5d14dac
--- /dev/null
+++ b/src/bin/dhcp4/tests/.gitignore
@@ -0,0 +1 @@
+/dhcp4_unittests
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index b1b0798..abfffb9 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -17,7 +17,7 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 CLEANFILES = *.gcno *.gcda spec_config.h
 
 man_MANS = b10-dhcp6.8
-EXTRA_DIST = $(man_MANS) dhcp6.spec interfaces.txt
+EXTRA_DIST = $(man_MANS) b10-dhcp6.xml dhcp6.spec interfaces.txt
 
 if ENABLE_MAN
 
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 4e69f05..cc4219e 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <time.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/iface_mgr.h>
@@ -21,11 +22,14 @@
 #include <dhcp/option6_addrlst.h>
 #include <asiolink/io_address.h>
 #include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
+#include <util/range_utilities.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
+using namespace isc::util;
 
 const std::string HARDCODED_LEASE = "2001:db8:1::1234:abcd";
 const uint32_t HARDCODED_T1 = 1500; // in seconds
@@ -67,13 +71,12 @@ Dhcpv6Srv::~Dhcpv6Srv() {
     IfaceMgr::instance().closeSockets();
 }
 
-bool
-Dhcpv6Srv::run() {
+bool Dhcpv6Srv::run() {
     while (!shutdown) {
-        boost::shared_ptr<Pkt6> query; // client's message
-        boost::shared_ptr<Pkt6> rsp;   // server's response
 
-        query = IfaceMgr::instance().receive6();
+        // client's message and server's response
+        Pkt6Ptr query = IfaceMgr::instance().receive6();
+        Pkt6Ptr rsp;
 
         if (query) {
             if (!query->unpack()) {
@@ -110,16 +113,16 @@ Dhcpv6Srv::run() {
                      << query->getType() << endl;
             }
 
-            cout << "Received " << query->data_len_ << " bytes packet type="
+            cout << "Received " << query->getBuffer().getLength() << " bytes packet type="
                  << query->getType() << endl;
             cout << query->toText();
             if (rsp) {
-                rsp->remote_addr_ = query->remote_addr_;
-                rsp->local_addr_ = query->local_addr_;
-                rsp->remote_port_ = DHCP6_CLIENT_PORT;
-                rsp->local_port_ = DHCP6_SERVER_PORT;
-                rsp->ifindex_ = query->ifindex_;
-                rsp->iface_ = query->iface_;
+                rsp->setRemoteAddr(query->getRemoteAddr());
+                rsp->setLocalAddr(query->getLocalAddr());
+                rsp->setRemotePort(DHCP6_CLIENT_PORT);
+                rsp->setLocalPort(DHCP6_SERVER_PORT);
+                rsp->setIndex(query->getIndex());
+                rsp->setIface(query->getIface());
                 cout << "Replying with:" << rsp->getType() << endl;
                 cout << rsp->toText();
                 cout << "----" << endl;
@@ -138,28 +141,93 @@ Dhcpv6Srv::run() {
     return (true);
 }
 
-void
-Dhcpv6Srv::setServerID() {
-    /// TODO implement this for real once interface detection is done.
-    /// Use hardcoded server-id for now
-
-    boost::shared_array<uint8_t> srvid(new uint8_t[14]);
-    srvid[0] = 0;
-    srvid[1] = 1; // DUID type 1 = DUID-LLT (see section 9.2 of RFC3315)
-    srvid[2] = 0;
-    srvid[3] = 6; // HW type = ethernet (I think. I'm typing this from my head
-                  // in hotel, without Internet connection)
-    for (int i=4; i<14; i++) {
-        srvid[i]=i-4;
+void Dhcpv6Srv::setServerID() {
+
+    /// @todo: DUID should be generated once and then stored, rather
+    /// than generated each time
+
+    /// @todo: This code implements support for DUID-LLT (the recommended one).
+    /// We should eventually add support for other DUID types: DUID-LL, DUID-EN
+    /// and DUID-UUID
+
+    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+
+    // let's find suitable interface
+    for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
+         iface != ifaces.end(); ++iface) {
+        // All the following checks could be merged into one multi-condition
+        // statement, but let's keep them separated as perhaps one day
+        // we will grow knobs to selectively turn them on or off. Also,
+        // this code is used only *once* during first start on a new machine
+        // and then server-id is stored. (or at least it will be once
+        // DUID storage is implemente
+
+        // I wish there was a this_is_a_real_physical_interface flag...
+
+        // MAC address should be at least 6 bytes. Although there is no such
+        // requirement in any RFC, all decent physical interfaces (Ethernet,
+        // WiFi, Infiniband, etc.) have 6 bytes long MAC address. We want to
+        // base our DUID on real hardware address, rather than virtual
+        // interface that pretends that underlying IP address is its MAC.
+        if (iface->getMacLen() < MIN_MAC_LEN) {
+            continue;
+        }
+
+        // let's don't use loopback
+        if (iface->flag_loopback_) {
+            continue;
+        }
+
+        // let's skip downed interfaces. It is better to use working ones.
+        if (!iface->flag_up_) {
+            continue;
+        }
+
+        // some interfaces (like lo on Linux) report 6-bytes long
+        // MAC adress 00:00:00:00:00:00. Let's not use such weird interfaces
+        // to generate DUID.
+        if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
+            continue;
+        }
+
+        // Ok, we have useful MAC. Let's generate DUID-LLT based on
+        // it. See RFC3315, Section 9.2 for details.
+
+        // DUID uses seconds since midnight of 01-01-2000, time() returns
+        // seconds since 01-01-1970. DUID_TIME_EPOCH substution corrects that.
+        time_t seconds = time(NULL);
+        seconds -= DUID_TIME_EPOCH;
+
+        OptionBuffer srvid(8 + iface->getMacLen());
+        writeUint16(DUID_LLT, &srvid[0]);
+        writeUint16(HWTYPE_ETHERNET, &srvid[2]);
+        writeUint32(static_cast<uint32_t>(seconds), &srvid[4]);
+        memcpy(&srvid[0]+8, iface->getMac(), iface->getMacLen());
+
+        serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
+                                         srvid.begin(), srvid.end()));
+        return;
     }
-    serverid_ = boost::shared_ptr<Option>(new Option(Option::V6,
-                                                     D6O_SERVERID,
-                                                     srvid,
-                                                     0, 14));
+
+    // if we reached here, there are no suitable interfaces found.
+    // Either interface detection is not supported on this platform or
+    // this is really weird box. Let's use DUID-EN instead.
+    // See Section 9.3 of RFC3315 for details.
+
+    OptionBuffer srvid(12);
+    writeUint16(DUID_EN, &srvid[0]);
+    writeUint32(ENTERPRISE_ID_ISC, &srvid[2]);
+
+    // Length of the identifier is company specific. I hereby declare
+    // ISC "standard" of 6 bytes long pseudo-random numbers.
+    srand(time(NULL));
+    fillRandom(&srvid[6],&srvid[12]);
+
+    serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
+                                     srvid.begin(), srvid.end()));
 }
 
-void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
-                                   boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     // add client-id
     boost::shared_ptr<Option> clientid = question->getOption(D6O_CLIENTID);
     if (clientid) {
@@ -169,8 +237,7 @@ void Dhcpv6Srv::copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
     // TODO: Should throw if there is no client-id (except anonymous INF-REQUEST)
 }
 
-void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/,
-                                   boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
     // TODO: question is currently unused, but we need it at least to know
     // message type we are answering
 
@@ -179,8 +246,7 @@ void Dhcpv6Srv::appendDefaultOptions(const boost::shared_ptr<Pkt6>& /*question*/
 }
 
 
-void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question*/,
-                                       boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& /*question*/, Pkt6Ptr& answer) {
     // TODO: question is currently unused, but we need to extract ORO from it
     // and act on its content. Now we just send DNS-SERVERS option.
 
@@ -190,8 +256,7 @@ void Dhcpv6Srv::appendRequestedOptions(const boost::shared_ptr<Pkt6>& /*question
     answer->addOption(dnsservers);
 }
 
-void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
-                             boost::shared_ptr<Pkt6>& answer) {
+void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
     /// TODO Rewrite this once LeaseManager is implemented.
 
     // answer client's IA (this is mostly a dummy,
@@ -217,11 +282,8 @@ void Dhcpv6Srv::assignLeases(const boost::shared_ptr<Pkt6>& question,
     }
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
-
-    boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE,
-                                               solicit->getTransid()));
+Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
+    Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
     copyDefaultOptions(solicit, advertise);
     appendDefaultOptions(solicit, advertise);
@@ -232,11 +294,8 @@ Dhcpv6Srv::processSolicit(const boost::shared_ptr<Pkt6>& solicit) {
     return (advertise);
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
-
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           request->getTransid()));
+Pkt6Ptr Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
 
     copyDefaultOptions(request, reply);
     appendDefaultOptions(request, reply);
@@ -247,50 +306,38 @@ Dhcpv6Srv::processRequest(const boost::shared_ptr<Pkt6>& request) {
     return (reply);
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRenew(const boost::shared_ptr<Pkt6>& renew) {
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           renew->getTransid(),
-                                           Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
+    /// @todo: Implement this
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
     return reply;
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRebind(const boost::shared_ptr<Pkt6>& rebind) {
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           rebind->getTransid(),
-                                           Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
+    /// @todo: Implement this
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
     return reply;
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processConfirm(const boost::shared_ptr<Pkt6>& confirm) {
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           confirm->getTransid(),
-                                           Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
+    /// @todo: Implement this
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
     return reply;
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processRelease(const boost::shared_ptr<Pkt6>& release) {
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           release->getTransid(),
-                                           Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
+    /// @todo: Implement this
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
     return reply;
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processDecline(const boost::shared_ptr<Pkt6>& decline) {
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           decline->getTransid(),
-                                           Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
+    /// @todo: Implement this
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
     return reply;
 }
 
-boost::shared_ptr<Pkt6>
-Dhcpv6Srv::processInfRequest(const boost::shared_ptr<Pkt6>& infRequest) {
-    boost::shared_ptr<Pkt6> reply(new Pkt6(DHCPV6_REPLY,
-                                           infRequest->getTransid(),
-                                           Pkt6::UDP));
+Pkt6Ptr Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
+    /// @todo: Implement this
+    Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
     return reply;
 }
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 44a9f8a..b6ae637 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,6 @@
 #ifndef DHCPV6_SRV_H
 #define DHCPV6_SRV_H
 
-#include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt6.h>
@@ -36,6 +35,10 @@ namespace dhcp {
 class Dhcpv6Srv : public boost::noncopyable {
 
 public:
+
+    /// @brief Minimum length of a MAC address to be used in DUID generation.
+    static const size_t MIN_MAC_LEN = 6;
+
     /// @brief Default constructor.
     ///
     /// Instantiates necessary services, required to run DHCPv6 server.
@@ -52,8 +55,7 @@ public:
     /// @brief Returns server-intentifier option
     ///
     /// @return server-id option
-    boost::shared_ptr<isc::dhcp::Option>
-    getServerID() { return serverid_; }
+    OptionPtr getServerID() { return serverid_; }
 
     /// @brief Main server processing loop.
     ///
@@ -80,8 +82,7 @@ protected:
     /// @param solicit SOLICIT message received from client
     ///
     /// @return ADVERTISE, REPLY message or NULL
-    boost::shared_ptr<Pkt6>
-    processSolicit(const boost::shared_ptr<Pkt6>& solicit);
+    Pkt6Ptr processSolicit(const Pkt6Ptr& solicit);
 
     /// @brief Processes incoming REQUEST and returns REPLY response.
     ///
@@ -94,44 +95,37 @@ protected:
     /// @param request a message received from client
     ///
     /// @return REPLY message or NULL
-    boost::shared_ptr<Pkt6>
-    processRequest(const boost::shared_ptr<Pkt6>& request);
+    Pkt6Ptr processRequest(const Pkt6Ptr& request);
 
     /// @brief Stub function that will handle incoming RENEW messages.
     ///
     /// @param renew message received from client
-    boost::shared_ptr<Pkt6>
-    processRenew(const boost::shared_ptr<Pkt6>& renew);
+    Pkt6Ptr processRenew(const Pkt6Ptr& renew);
 
     /// @brief Stub function that will handle incoming REBIND messages.
     ///
     /// @param rebind message received from client
-    boost::shared_ptr<Pkt6>
-    processRebind(const boost::shared_ptr<Pkt6>& rebind);
+    Pkt6Ptr processRebind(const Pkt6Ptr& rebind);
 
     /// @brief Stub function that will handle incoming CONFIRM messages.
     ///
     /// @param confirm message received from client
-    boost::shared_ptr<Pkt6>
-    processConfirm(const boost::shared_ptr<Pkt6>& confirm);
+    Pkt6Ptr processConfirm(const Pkt6Ptr& confirm);
 
     /// @brief Stub function that will handle incoming RELEASE messages.
     ///
     /// @param release message received from client
-    boost::shared_ptr<Pkt6>
-    processRelease(const boost::shared_ptr<Pkt6>& release);
+    Pkt6Ptr processRelease(const Pkt6Ptr& release);
 
     /// @brief Stub function that will handle incoming DECLINE messages.
     ///
     /// @param decline message received from client
-    boost::shared_ptr<Pkt6>
-    processDecline(const boost::shared_ptr<Pkt6>& decline);
+    Pkt6Ptr processDecline(const Pkt6Ptr& decline);
 
     /// @brief Stub function that will handle incoming INF-REQUEST messages.
     ///
     /// @param infRequest message received from client
-    boost::shared_ptr<Pkt6>
-    processInfRequest(const boost::shared_ptr<Pkt6>& infRequest);
+    Pkt6Ptr processInfRequest(const Pkt6Ptr& infRequest);
 
     /// @brief Copies required options from client message to server answer
     ///
@@ -141,8 +135,7 @@ protected:
     ///
     /// @param question client's message (options will be copied from here)
     /// @param answer server's message (options will be copied here)
-    void copyDefaultOptions(const boost::shared_ptr<Pkt6>& question,
-                            boost::shared_ptr<Pkt6>& answer);
+    void copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
     /// @brief Appends default options to server's answer.
     ///
@@ -152,8 +145,7 @@ protected:
     ///
     /// @param question client's message
     /// @param answer server's message (options will be added here)
-    void appendDefaultOptions(const boost::shared_ptr<Pkt6>& question,
-                              boost::shared_ptr<Pkt6>& answer);
+    void appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
     /// @brief Appends requested options to server's answer.
     ///
@@ -163,8 +155,7 @@ protected:
     ///
     /// @param question client's message
     /// @param answer server's message (options will be added here)
-    void appendRequestedOptions(const boost::shared_ptr<Pkt6>& question,
-                                boost::shared_ptr<Pkt6>& answer);
+    void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
     /// @brief Assigns leases.
     ///
@@ -174,8 +165,7 @@ protected:
     ///
     /// @param question client's message (with requested IA_NA)
     /// @param answer server's message (IA_NA options will be added here)
-    void assignLeases(const boost::shared_ptr<Pkt6>& question,
-                      boost::shared_ptr<Pkt6>& answer);
+    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer);
 
     /// @brief Sets server-identifier.
     ///
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 09d1cec..a42324f 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -20,13 +20,16 @@
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 
-#include "dhcp/dhcp6.h"
-#include "dhcp6/dhcp6_srv.h"
-#include "dhcp/option6_ia.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp6/dhcp6_srv.h>
+#include <util/buffer.h>
+#include <util/range_utilities.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::util;
 
 // namespace has to be named, because friends are defined in Dhcpv6Srv class
 // Maybe it should be isc::test?
@@ -79,18 +82,83 @@ TEST_F(Dhcpv6SrvTest, basic) {
     delete srv;
 }
 
+TEST_F(Dhcpv6SrvTest, DUID) {
+    // tests that DUID is generated properly
+
+    Dhcpv6Srv* srv = NULL;
+    ASSERT_NO_THROW( {
+        srv = new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000);
+    });
+
+    OptionPtr srvid = srv->getServerID();
+    ASSERT_TRUE(srvid);
+
+    EXPECT_EQ(D6O_SERVERID, srvid->getType());
+
+    OutputBuffer buf(32);
+    srvid->pack(buf);
+
+    // length of the actual DUID
+    size_t len = buf.getLength() - srvid->getHeaderLen();
+
+    InputBuffer data(buf.getData(), buf.getLength());
+
+    // ignore first four bytes (standard DHCPv6 header)
+    data.readUint32();
+
+    uint16_t duid_type = data.readUint16();
+    cout << "Duid-type=" << duid_type << endl;
+    switch(duid_type) {
+    case DUID_LLT: {
+        // DUID must contain at least 6 bytes long MAC
+        // + 8 bytes of fixed header
+        EXPECT_GE(14, len);
+
+        uint16_t hw_type = data.readUint16();
+        // there's no real way to find out "correct"
+        // hardware type
+        EXPECT_GT(hw_type, 0);
+
+        // check that timer is counted since 1.1.2000,
+        // not from 1.1.1970.
+        uint32_t seconds = data.readUint32();
+        EXPECT_LE(seconds, DUID_TIME_EPOCH);
+        // this test will start failing after 2030.
+        // Hopefully we will be at BIND12 by then.
+
+        // MAC must not be zeros
+        vector<uint8_t> mac(len-8);
+        vector<uint8_t> zeros(len-8, 0);
+        data.readVector(mac, len-8);
+        EXPECT_NE(mac, zeros);
+        break;
+    }
+    case DUID_EN: {
+        // there's not much we can check. Just simple
+        // check if it is not all zeros
+        vector<uint8_t> content(len-2);
+        data.readVector(content, len-2);
+        EXPECT_FALSE(isRangeZero(content.begin(), content.end()));
+    }
+    case DUID_LL: // not supported yet
+    case DUID_UUID: // not supported yet
+    default:
+        cout << "Not supported duid type=" << duid_type << endl;
+        FAIL();
+    }
+}
+
 TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     NakedDhcpv6Srv* srv = NULL;
     ASSERT_NO_THROW( srv = new NakedDhcpv6Srv(); );
 
     // a dummy content for client-id
-    boost::shared_array<uint8_t> clntDuid(new uint8_t[32]);
-    for (int i = 0; i < 32; i++)
+    OptionBuffer clntDuid(32);
+    for (int i = 0; i < 32; i++) {
         clntDuid[i] = 100 + i;
+    }
 
-    boost::shared_ptr<Pkt6> sol =
-        boost::shared_ptr<Pkt6>(new Pkt6(DHCPV6_SOLICIT,
-                                         1234, Pkt6::UDP));
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
 
     boost::shared_ptr<Option6IA> ia =
         boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, 234));
@@ -113,9 +181,9 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     // - server-id
     // - IA that includes IAADDR
 
-    boost::shared_ptr<Option> clientid =
-        boost::shared_ptr<Option>(new Option(Option::V6, D6O_CLIENTID,
-                                             clntDuid, 0, 16));
+    OptionPtr clientid = OptionPtr(new Option(Option::V6, D6O_CLIENTID,
+                                              clntDuid.begin(),
+                                              clntDuid.begin() + 16));
     sol->addOption(clientid);
 
     boost::shared_ptr<Pkt6> reply = srv->processSolicit(sol);
@@ -126,7 +194,7 @@ TEST_F(Dhcpv6SrvTest, Solicit_basic) {
     EXPECT_EQ( DHCPV6_ADVERTISE, reply->getType() );
     EXPECT_EQ( 1234, reply->getTransid() );
 
-    boost::shared_ptr<Option> tmp = reply->getOption(D6O_IA_NA);
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE( tmp );
 
     Option6IA* reply_ia = dynamic_cast<Option6IA*>(tmp.get());
diff --git a/src/bin/dhcp6/tests/dhcp6_test.py b/src/bin/dhcp6/tests/dhcp6_test.py
index d63e04d..e080c77 100644
--- a/src/bin/dhcp6/tests/dhcp6_test.py
+++ b/src/bin/dhcp6/tests/dhcp6_test.py
@@ -36,6 +36,9 @@ class TestDhcpv6Daemon(unittest.TestCase):
         # to the main program ASAP in each test... this prevents
         # hangs reading from the child process (as the pipe is only
         # open in the child), and also insures nice pretty output
+        print("Please ignore any socket errors. Purpose of this test is to")
+        print("verify that DHCPv6 process could be started, not that socket")
+        print("could be bound. Binding fails when run as non-root user.")
 
     def tearDown(self):
         # clean up our stdout munging
diff --git a/src/bin/host/.gitignore b/src/bin/host/.gitignore
new file mode 100644
index 0000000..0073523
--- /dev/null
+++ b/src/bin/host/.gitignore
@@ -0,0 +1 @@
+/b10-host
diff --git a/src/bin/host/host.cc b/src/bin/host/host.cc
index f0df0c8..a5c6522 100644
--- a/src/bin/host/host.cc
+++ b/src/bin/host/host.cc
@@ -70,8 +70,7 @@ host_lookup(const char* const name, const char* const dns_class,
                              RRClass(dns_class),
                              any ? RRType::ANY() : RRType(type)));  // if NULL then:
 
-    OutputBuffer obuffer(512);
-    MessageRenderer renderer(obuffer);
+    MessageRenderer renderer;
     msg.toWire(renderer);
 
     struct addrinfo hints, *res;
@@ -111,7 +110,7 @@ host_lookup(const char* const name, const char* const dns_class,
         gettimeofday(&before_time, NULL);
     }
 
-    sendto(s, obuffer.getData(), obuffer.getLength(), 0, res->ai_addr,
+    sendto(s, renderer.getData(), renderer.getLength(), 0, res->ai_addr,
            res->ai_addrlen);
 
     struct sockaddr_storage ss;
@@ -233,7 +232,7 @@ main(int argc, char* argv[]) {
     argv += optind;
 
     if (argc < 1) {
-        cout << "Usage: host [-adprv] [-c class] [-t type] hostname [server]\n";
+        cout << "Usage: host [-adrv] [-c class] [-p port] [-t type] hostname [server]\n";
         exit(1);
     }
 
diff --git a/src/bin/loadzone/.gitignore b/src/bin/loadzone/.gitignore
new file mode 100644
index 0000000..86761ee
--- /dev/null
+++ b/src/bin/loadzone/.gitignore
@@ -0,0 +1,3 @@
+/b10-loadzone
+/b10-loadzone.py
+/run_loadzone.sh
diff --git a/src/bin/loadzone/b10-loadzone.8 b/src/bin/loadzone/b10-loadzone.8
index d563ff2..cf1c26b 100644
--- a/src/bin/loadzone/b10-loadzone.8
+++ b/src/bin/loadzone/b10-loadzone.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-loadzone
 .\"    Author: [see the "AUTHORS" section]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: March 8, 2010
+.\"      Date: March 26, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-LOADZONE" "8" "March 8, 2010" "BIND10" "BIND10"
+.TH "B10\-LOADZONE" "8" "March 26, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -22,7 +22,7 @@
 b10-loadzone \- Load DNS Zone File
 .SH "SYNOPSIS"
 .HP \w'\fBb10\-loadzone\fR\ 'u
-\fBb10\-loadzone\fR [\fB\-d\ \fR\fB\fIdatabase\fR\fR] [\fB\-o\ \fR\fB\fIorigin\fR\fR] [filename]
+\fBb10\-loadzone\fR [\fB\-d\ \fR\fB\fIdatabase\fR\fR] [\fB\-o\ \fR\fB\fIorigin\fR\fR] {filename}
 .SH "DESCRIPTION"
 .PP
 The
diff --git a/src/bin/loadzone/b10-loadzone.xml b/src/bin/loadzone/b10-loadzone.xml
index 25e23a5..8c41e54 100644
--- a/src/bin/loadzone/b10-loadzone.xml
+++ b/src/bin/loadzone/b10-loadzone.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>March 8, 2010</date>
+    <date>March 26, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -46,7 +46,7 @@
       <command>b10-loadzone</command>
       <arg><option>-d <replaceable class="parameter">database</replaceable></option></arg>
       <arg><option>-o <replaceable class="parameter">origin</replaceable></option></arg>
-      <arg chose="req">filename</arg>
+      <arg choice="req">filename</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
diff --git a/src/bin/loadzone/tests/correct/.gitignore b/src/bin/loadzone/tests/correct/.gitignore
new file mode 100644
index 0000000..2d58698
--- /dev/null
+++ b/src/bin/loadzone/tests/correct/.gitignore
@@ -0,0 +1 @@
+/correct_test.sh
diff --git a/src/bin/loadzone/tests/error/.gitignore b/src/bin/loadzone/tests/error/.gitignore
new file mode 100644
index 0000000..5d20adb
--- /dev/null
+++ b/src/bin/loadzone/tests/error/.gitignore
@@ -0,0 +1 @@
+/error_test.sh
diff --git a/src/bin/msgq/.gitignore b/src/bin/msgq/.gitignore
new file mode 100644
index 0000000..ee1d942
--- /dev/null
+++ b/src/bin/msgq/.gitignore
@@ -0,0 +1,3 @@
+/b10-msgq
+/msgq.py
+/run_msgq.sh
diff --git a/src/bin/msgq/tests/.gitignore b/src/bin/msgq/tests/.gitignore
new file mode 100644
index 0000000..70acfff
--- /dev/null
+++ b/src/bin/msgq/tests/.gitignore
@@ -0,0 +1 @@
+/msgq_test
diff --git a/src/bin/resolver/.gitignore b/src/bin/resolver/.gitignore
new file mode 100644
index 0000000..95abd50
--- /dev/null
+++ b/src/bin/resolver/.gitignore
@@ -0,0 +1,7 @@
+/b10-resolver
+/resolver.spec
+/resolver.spec.pre
+/resolver_messages.cc
+/resolver_messages.h
+/spec_config.h
+/spec_config.h.pre
diff --git a/src/bin/resolver/b10-resolver.8 b/src/bin/resolver/b10-resolver.8
index 7a4cb6d..eed69b8 100644
--- a/src/bin/resolver/b10-resolver.8
+++ b/src/bin/resolver/b10-resolver.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-resolver
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 28, 2011
+.\"      Date: February 28, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-RESOLVER" "8" "December 28, 2011" "BIND10" "BIND10"
+.TH "B10\-RESOLVER" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -118,7 +118,9 @@ The configuration command is:
 
 \fBshutdown\fR
 exits
-\fBb10\-resolver\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-resolver\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .SH "SEE ALSO"
 .PP
 
@@ -134,5 +136,5 @@ The
 daemon was first coded in September 2010\&. The initial implementation only provided forwarding\&. Iteration was introduced in January 2011\&. Caching was implemented in February 2011\&. Access control was introduced in June 2011\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml
index 05472b5..aca8fb2 100644
--- a/src/bin/resolver/b10-resolver.xml
+++ b/src/bin/resolver/b10-resolver.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010-2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 28, 2011</date>
+    <date>February 28, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -201,7 +201,10 @@ once that is merged you can for instance do 'config add Resolver/forward_address
 
     <para>
       <command>shutdown</command> exits <command>b10-resolver</command>.
-      (Note that the BIND 10 boss process will restart this service.)
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
     </para>
 
   </refsect1>
diff --git a/src/bin/resolver/main.cc b/src/bin/resolver/main.cc
index c56e6f3..d14fb0b 100644
--- a/src/bin/resolver/main.cc
+++ b/src/bin/resolver/main.cc
@@ -100,7 +100,8 @@ my_command_handler(const string& command, ConstElementPtr args) {
                     return (answer);
                 }
             }
-            LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT, RESOLVER_SHUTDOWN);
+            LOG_DEBUG(resolver_logger, RESOLVER_DBG_INIT,
+                      RESOLVER_SHUTDOWN_RECEIVED);
             io_service.stop();
         }
 
diff --git a/src/bin/resolver/resolver.cc b/src/bin/resolver/resolver.cc
index 367c16c..9536608 100644
--- a/src/bin/resolver/resolver.cc
+++ b/src/bin/resolver/resolver.cc
@@ -15,6 +15,7 @@
 #include <config.h>
 
 #include <stdint.h>
+#include <sys/types.h>
 #include <netinet/in.h>
 
 #include <algorithm>
@@ -91,7 +92,7 @@ public:
         queryShutdown();
     }
 
-    void querySetup(DNSService& dnss,
+    void querySetup(DNSServiceBase& dnss,
                     isc::nsas::NameserverAddressStore& nsas,
                     isc::cache::ResolverCache& cache)
     {
@@ -120,10 +121,10 @@ public:
     }
 
     void setForwardAddresses(const AddressList& upstream,
-        DNSService *dnss)
+                             DNSServiceBase* dnss)
     {
         upstream_ = upstream;
-        if (dnss) {
+        if (dnss != NULL) {
             if (!upstream_.empty()) {
                 BOOST_FOREACH(const AddressPair& address, upstream) {
                     LOG_INFO(resolver_logger, RESOLVER_FORWARD_ADDRESS)
@@ -136,10 +137,10 @@ public:
     }
 
     void setRootAddresses(const AddressList& upstream_root,
-                          DNSService *dnss)
+                          DNSServiceBase* dnss)
     {
         upstream_root_ = upstream_root;
-        if (dnss) {
+        if (dnss != NULL) {
             if (!upstream_root_.empty()) {
                 BOOST_FOREACH(const AddressPair& address, upstream_root) {
                     LOG_INFO(resolver_logger, RESOLVER_SET_ROOT_ADDRESS)
@@ -252,7 +253,8 @@ makeErrorMessage(MessagePtr message, MessagePtr answer_message,
     }
     for_each(questions.begin(), questions.end(), QuestionInserter(message));
     message->setRcode(rcode);
-    MessageRenderer renderer(*buffer);
+    MessageRenderer renderer;
+    renderer.setBuffer(buffer.get());
     message->toWire(renderer);
 }
 
@@ -303,7 +305,8 @@ public:
 
         // Now we can clear the buffer and render the new message into it
         buffer->clear();
-        MessageRenderer renderer(*buffer);
+        MessageRenderer renderer;
+        renderer.setBuffer(buffer.get());
 
         ConstEDNSPtr edns(query_message->getEDNS());
         const bool dnssec_ok = edns && edns->getDNSSECAwareness();
@@ -327,6 +330,7 @@ public:
         }
 
         answer_message->toWire(renderer);
+        renderer.setBuffer(NULL);
 
         LOG_DEBUG(resolver_logger, RESOLVER_DBG_DETAIL,
                   RESOLVER_DNS_MESSAGE_SENT)
@@ -373,7 +377,7 @@ Resolver::~Resolver() {
 }
 
 void
-Resolver::setDNSService(isc::asiodns::DNSService& dnss) {
+Resolver::setDNSService(isc::asiodns::DNSServiceBase& dnss) {
     dnss_ = &dnss;
 }
 
diff --git a/src/bin/resolver/resolver.h b/src/bin/resolver/resolver.h
index 096f522..e91192e 100644
--- a/src/bin/resolver/resolver.h
+++ b/src/bin/resolver/resolver.h
@@ -104,7 +104,7 @@ public:
                                             bool startup = false);
 
     /// \brief Assign an ASIO IO Service queue to this Resolver object
-    void setDNSService(isc::asiodns::DNSService& dnss);
+    void setDNSService(isc::asiodns::DNSServiceBase& dnss);
 
     /// \brief Assign a NameserverAddressStore to this Resolver object
     void setNameserverAddressStore(isc::nsas::NameserverAddressStore &nsas);
@@ -113,7 +113,7 @@ public:
     void setCache(isc::cache::ResolverCache& cache);
 
     /// \brief Return this object's ASIO IO Service queue
-    isc::asiodns::DNSService& getDNSService() const { return (*dnss_); }
+    isc::asiodns::DNSServiceBase& getDNSService() const { return (*dnss_); }
 
     /// \brief Returns this object's NSAS
     isc::nsas::NameserverAddressStore& getNameserverAddressStore() const {
@@ -258,7 +258,7 @@ public:
 
 private:
     ResolverImpl* impl_;
-    isc::asiodns::DNSService* dnss_;
+    isc::asiodns::DNSServiceBase* dnss_;
     isc::asiolink::SimpleCallback* checkin_;
     isc::asiodns::DNSLookup* dns_lookup_;
     isc::asiodns::DNSAnswer* dns_answer_;
diff --git a/src/bin/resolver/resolver_messages.mes b/src/bin/resolver/resolver_messages.mes
index bd9c818..4999dbe 100644
--- a/src/bin/resolver/resolver_messages.mes
+++ b/src/bin/resolver/resolver_messages.mes
@@ -247,6 +247,6 @@ The log message shows the query in the form of <query name>/<query
 type>/<query class>, and the client that sends the query in the form of
 <Source IP address>#<source port>.
 
-% RESOLVER_SHUTDOWN asked to shut down, doing so
+% RESOLVER_SHUTDOWN_RECEIVED received command to shut down
 A debug message noting that the server was asked to terminate and is
 complying to the request.
diff --git a/src/bin/resolver/tests/.gitignore b/src/bin/resolver/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/resolver/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/resolver/tests/resolver_config_unittest.cc b/src/bin/resolver/tests/resolver_config_unittest.cc
index ea42ba0..369ca5c 100644
--- a/src/bin/resolver/tests/resolver_config_unittest.cc
+++ b/src/bin/resolver/tests/resolver_config_unittest.cc
@@ -48,6 +48,7 @@
 
 #include <dns/tests/unittest_util.h>
 #include <testutils/srv_test.h>
+#include <testutils/mockups.h>
 #include <testutils/portconfig.h>
 #include <testutils/socket_request.h>
 
@@ -76,15 +77,13 @@ public:
 
 class ResolverConfig : public ::testing::Test {
 protected:
-    IOService ios;
-    DNSService dnss;
+    MockDNSService dnss;
     Resolver server;
     scoped_ptr<const IOEndpoint> endpoint;
     scoped_ptr<const IOMessage> query_message;
     scoped_ptr<const Client> client;
     scoped_ptr<const RequestContext> request;
     ResolverConfig() :
-        dnss(ios, NULL, NULL, NULL),
         // The empty string is expected value of the parameter of
         // requestSocket, not the app_name (there's no fallback, it checks
         // the empty string is passed).
@@ -320,6 +319,15 @@ TEST_F(ResolverConfig, invalidForwardAddresses) {
 // Try setting the addresses directly
 TEST_F(ResolverConfig, listenAddresses) {
     isc::testutils::portconfig::listenAddresses(server);
+
+    // listenAddressConfig should have attempted to create 4 DNS server
+    // objects: two IP addresses, TCP and UDP for each.  For UDP, the "SYNC_OK"
+    // option (or anything else) should have NOT been specified.
+    EXPECT_EQ(2, dnss.getTCPFdParams().size());
+    EXPECT_EQ(2, dnss.getUDPFdParams().size());
+    EXPECT_EQ(DNSService::SERVER_DEFAULT, dnss.getUDPFdParams().at(0).options);
+    EXPECT_EQ(DNSService::SERVER_DEFAULT, dnss.getUDPFdParams().at(1).options);
+
     // Check it requests the correct addresses
     const char* tokens[] = {
         "TCP:127.0.0.1:53210:1",
diff --git a/src/bin/sockcreator/.gitignore b/src/bin/sockcreator/.gitignore
new file mode 100644
index 0000000..2985184
--- /dev/null
+++ b/src/bin/sockcreator/.gitignore
@@ -0,0 +1 @@
+/b10-sockcreator
diff --git a/src/bin/sockcreator/sockcreator.cc b/src/bin/sockcreator/sockcreator.cc
index d7fe1f8..167e3f0 100644
--- a/src/bin/sockcreator/sockcreator.cc
+++ b/src/bin/sockcreator/sockcreator.cc
@@ -157,7 +157,7 @@ handleRequest(const int input_fd, const int output_fd,
     }
 
     // Obtain the socket
-    const int result = get_sock(sock_type, addr, addr_len);
+    const int result = get_sock(sock_type, addr, addr_len, close_fun);
     if (result >= 0) {
         // Got the socket, send it to the client.
         writeMessage(output_fd, "S", 1);
@@ -186,6 +186,52 @@ handleRequest(const int input_fd, const int output_fd,
     }
 }
 
+// Sets the MTU related flags for IPv6 UDP sockets.
+// It is borrowed from bind-9 lib/isc/unix/socket.c and modified
+// to compile here.
+//
+// The function returns -2 if it fails or the socket file descriptor
+// on success (for convenience, so the result can be just returned).
+int
+mtu(int fd) {
+#ifdef IPV6_USE_MIN_MTU        /* RFC 3542, not too common yet*/
+    const int on(1);
+    // use minimum MTU
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &on, sizeof(on)) < 0) {
+        return (-2);
+    }
+#else // Try the following as fallback
+#ifdef IPV6_MTU
+    // Use minimum MTU on systems that don't have the IPV6_USE_MIN_MTU
+    const int mtu = 1280;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU, &mtu, sizeof(mtu)) < 0) {
+        return (-2);
+    }
+#endif
+#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
+    // Turn off Path MTU discovery on IPv6/UDP sockets.
+    const int action = IPV6_PMTUDISC_DONT;
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &action,
+                   sizeof(action)) < 0) {
+
+        return (-2);
+    }
+#endif
+#endif
+    return (fd);
+}
+
+// This one closes the socket if result is negative. Used not to leak socket
+// on error.
+int maybeClose(const int result, const int socket, const close_t close_fun) {
+    if (result < 0) {
+        if (close_fun(socket) == -1) {
+            isc_throw(InternalError, "Error closing socket");
+        }
+    }
+    return (result);
+}
+
 } // Anonymous namespace
 
 namespace isc {
@@ -193,7 +239,8 @@ namespace socket_creator {
 
 // Get the socket and bind to it.
 int
-getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len) {
+getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
+        const close_t close_fun) {
     const int sock = socket(bind_addr->sa_family, type, 0);
     if (sock == -1) {
         return (-1);
@@ -201,15 +248,19 @@ getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len) {
     const int on = 1;
     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
         // This is part of the binding process, so it's a bind error
-        return (-2);
+        return (maybeClose(-2, sock, close_fun));
     }
     if (bind_addr->sa_family == AF_INET6 &&
         setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) {
         // This is part of the binding process, so it's a bind error
-        return (-2);
+        return (maybeClose(-2, sock, close_fun));
     }
     if (bind(sock, bind_addr, addr_len) == -1) {
-        return (-2);
+        return (maybeClose(-2, sock, close_fun));
+    }
+    if (type == SOCK_DGRAM && bind_addr->sa_family == AF_INET6) {
+        // Set some MTU flags on IPv6 UDP sockets.
+        return (maybeClose(mtu(sock), sock, close_fun));
     }
     return (sock);
 }
diff --git a/src/bin/sockcreator/sockcreator.h b/src/bin/sockcreator/sockcreator.h
index 012d8c3..e5d4783 100644
--- a/src/bin/sockcreator/sockcreator.h
+++ b/src/bin/sockcreator/sockcreator.h
@@ -73,6 +73,9 @@ public:
 };
 
 
+// Type of the close() function, so it can be passed as a parameter.
+// Argument is the same as that for close(2).
+typedef int (*close_t)(int);
 
 /// \short Create a socket and bind it.
 ///
@@ -82,13 +85,16 @@ public:
 /// \param type The type of socket to create (SOCK_STREAM, SOCK_DGRAM, etc).
 /// \param bind_addr The address to bind.
 /// \param addr_len The actual length of bind_addr.
+/// \param close_fun The furction used to close a socket if there's an error
+///     after the creation.
 ///
 /// \return The file descriptor of the newly created socket, if everything
 ///         goes well. A negative number is returned if an error occurs -
 ///         -1 if the socket() call fails or -2 if bind() fails. In case of
 ///         error, errno is set (or left intact from socket() or bind()).
 int
-getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len);
+getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len,
+        const close_t close_fun);
 
 // Define some types for functions used to perform socket-related operations.
 // These are typedefed so that alternatives can be passed through to the
@@ -96,16 +102,13 @@ getSock(const int type, struct sockaddr* bind_addr, const socklen_t addr_len);
 
 // Type of the function to get a socket and to pass it as parameter.
 // Arguments are those described above for getSock().
-typedef int (*get_sock_t)(const int, struct sockaddr *, const socklen_t);
+typedef int (*get_sock_t)(const int, struct sockaddr *, const socklen_t,
+                          const close_t close_fun);
 
 // Type of the send_fd() function, so it can be passed as a parameter.
 // Arguments are the same as those of the send_fd() function.
 typedef int (*send_fd_t)(const int, const int);
 
-// Type of the close() function, so it can be passed as a parameter.
-// Argument is the same as that for close(2).
-typedef int (*close_t)(int);
-
 
 /// \brief Infinite loop parsing commands and returning the sockets.
 ///
diff --git a/src/bin/sockcreator/tests/.gitignore b/src/bin/sockcreator/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/bin/sockcreator/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/bin/sockcreator/tests/sockcreator_tests.cc b/src/bin/sockcreator/tests/sockcreator_tests.cc
index eccc3ed..9604567 100644
--- a/src/bin/sockcreator/tests/sockcreator_tests.cc
+++ b/src/bin/sockcreator/tests/sockcreator_tests.cc
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <arpa/inet.h>
 #include <unistd.h>
 
 #include <iostream>
@@ -105,17 +106,47 @@ typedef void (*socket_check_t)(const int);
 // The other argument is the socket descriptor number.
 
 // IPv4 check
-void addressFamilySpecificCheck(const sockaddr_in*, const int) {
+void addressFamilySpecificCheck(const sockaddr_in*, const int, const int) {
 }
 
 // IPv6 check
-void addressFamilySpecificCheck(const sockaddr_in6*, const int socknum) {
+void addressFamilySpecificCheck(const sockaddr_in6*, const int socknum,
+                                const int socket_type)
+{
     int options;
     socklen_t len = sizeof(options);
-    EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_V6ONLY, &options, &len));
+    EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_V6ONLY, &options,
+                            &len));
     EXPECT_NE(0, options);
+    if (socket_type == SOCK_DGRAM) {
+    // Some more checks for UDP - MTU
+#ifdef IPV6_USE_MIN_MTU        /* RFC 3542, not too common yet*/
+        // use minimum MTU
+        EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
+                                &options, &len)) << strerror(errno);
+        EXPECT_NE(0, options);
+#else
+        // We do not check for the IPV6_MTU, because while setting works (eg.
+        // the packets are fragmented correctly), the getting does not. If
+        // we try to getsockopt it, an error complaining it can't be accessed
+        // on unconnected socket is returned. If we try to connect it, the
+        // MTU of the interface is returned, not the one we set. So we live
+        // in belief it works because we examined the packet dump.
+#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
+        // Turned off Path MTU discovery on IPv6/UDP sockets?
+        EXPECT_EQ(0, getsockopt(socknum, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
+                                &options, &len)) << strerror(errno);
+        EXPECT_EQ(IPV6_PMTUDISC_DONT, options);
+#endif
+#endif
+    }
 }
 
+// Just ignore the fd and pretend success. We close invalid fds in the tests.
+int
+closeIgnore(int) {
+    return (0);
+}
 
 // Generic version of the socket test.  It creates the socket and checks that
 // it is a valid descriptor.  The family-specific check functions are called
@@ -133,7 +164,8 @@ void testAnyCreate(int socket_type, socket_check_t socket_check) {
     memset(&addr, 0, sizeof(addr));
     setAddressFamilyFields(&addr);
     sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&addr);
-    const int socket = getSock(socket_type, addr_ptr, sizeof(addr));
+    const int socket = getSock(socket_type, addr_ptr, sizeof(addr),
+                               closeIgnore);
     ASSERT_GE(socket, 0) << "Couldn't create socket: failed with " <<
         "return code " << socket << " and error " << strerror(errno);
 
@@ -147,7 +179,7 @@ void testAnyCreate(int socket_type, socket_check_t socket_check) {
     EXPECT_NE(0, options);
 
     // ...and the address-family specific tests.
-    addressFamilySpecificCheck(&addr, socket);
+    addressFamilySpecificCheck(&addr, socket, socket_type);
 
     // Tidy up and exit.
     EXPECT_EQ(0, close(socket));
@@ -171,12 +203,40 @@ TEST(get_sock, tcp6_create) {
     testAnyCreate<sockaddr_in6>(SOCK_STREAM, tcpCheck);
 }
 
+bool close_called(false);
+
+// You can use it as a close mockup. If you care about checking if it was really
+// called, you can use the close_called variable. But set it to false before the
+// test.
+int closeCall(int socket) {
+    close(socket);
+    close_called = true;
+    return (0);
+}
+
 // Ask the get_sock function for some nonsense and test if it is able to report
 // an error.
 TEST(get_sock, fail_with_nonsense) {
     sockaddr addr;
     memset(&addr, 0, sizeof(addr));
-    ASSERT_LT(getSock(0, &addr, sizeof addr), 0);
+    close_called = false;
+    ASSERT_EQ(-1, getSock(0, &addr, sizeof addr, closeCall));
+    ASSERT_FALSE(close_called); // The "socket" call should have failed already
+}
+
+// Bind should have failed here
+TEST(get_sock, fail_with_bind) {
+    sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = 1;
+    // No host should have this address on the interface, so it should not be
+    // possible to bind it.
+    addr.sin_addr.s_addr = inet_addr("192.0.2.1");
+    close_called = false;
+    ASSERT_EQ(-2, getSock(SOCK_STREAM, reinterpret_cast<sockaddr*>(&addr),
+                          sizeof addr, closeCall));
+    ASSERT_TRUE(close_called); // The "socket" call should have failed already
 }
 
 // The main run() function in the socket creator takes three functions to
@@ -198,7 +258,8 @@ TEST(get_sock, fail_with_nonsense) {
 // -1: The simulated bind() call has failed
 // -2: The simulated socket() call has failed
 int
-getSockDummy(const int type, struct sockaddr* addr, const socklen_t) {
+getSockDummy(const int type, struct sockaddr* addr, const socklen_t,
+             const close_t) {
     int result = 0;
     int port = 0;
 
@@ -253,12 +314,6 @@ send_FdDummy(const int destination, const int what) {
     return (status ? 0 : -1);
 }
 
-// Just ignore the fd and pretend success. We close invalid fds in the tests.
-int
-closeIgnore(int) {
-    return (0);
-}
-
 // Generic test that it works, with various inputs and outputs.
 // It uses different functions to create the socket and send it and pass
 // data to it and check it returns correct data back, to see if the run()
diff --git a/src/bin/stats/.gitignore b/src/bin/stats/.gitignore
new file mode 100644
index 0000000..7ff3797
--- /dev/null
+++ b/src/bin/stats/.gitignore
@@ -0,0 +1,4 @@
+/b10-stats
+/b10-stats-httpd
+/stats.py
+/stats_httpd.py
diff --git a/src/bin/stats/b10-stats-httpd.8 b/src/bin/stats/b10-stats-httpd.8
index 1206e1d..6e53543 100644
--- a/src/bin/stats/b10-stats-httpd.8
+++ b/src/bin/stats/b10-stats-httpd.8
@@ -1,22 +1,13 @@
 '\" t
 .\"     Title: b10-stats-httpd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
-.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
-.\"      Date: Mar 8, 2011
+.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
+.\"      Date: February 28, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-STATS\-HTTPD" "8" "Mar 8, 2011" "BIND10" "BIND10"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el       .ds Aq '
+.TH "B10\-STATS\-HTTPD" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -110,7 +101,9 @@ with its PID\&.
 .RS 4
 exits the
 \fBb10\-stats\-httpd\fR
-process\&. (Note that the BIND 10 boss process will restart this service\&.)
+process\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .RE
 .SH "SEE ALSO"
 .PP
@@ -125,8 +118,8 @@ BIND 10 Guide\&.
 .PP
 
 \fBb10\-stats\-httpd\fR
-was designed and implemented by Naoki Kambe of JPRS in Mar 2011\&.
+was designed and implemented by Naoki Kambe of JPRS in March 2011\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml
index c8df9b8..a372244 100644
--- a/src/bin/stats/b10-stats-httpd.xml
+++ b/src/bin/stats/b10-stats-httpd.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2011-2012  Internet Systems Consortium, Inc. ("ISC")
  -
  - Permission to use, copy, modify, and/or distribute this software for any
  - purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>Mar 8, 2011</date>
+    <date>February 28, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2011</year>
+      <year>2011-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -171,9 +171,12 @@
         <term><command>shutdown</command></term>
         <listitem>
 	  <para>
-	    exits the <command>b10-stats-httpd</command> process. (Note that
-	    the BIND 10 boss process will restart this service.)
-	  </para>
+	    exits the <command>b10-stats-httpd</command> process.
+            This has an optional <varname>pid</varname> argument to
+            select the process ID to stop.
+            (Note that the BIND 10 boss process may restart this service
+            if configured.)
+          </para>
         </listitem>
       </varlistentry>
     </variablelist>
@@ -205,7 +208,7 @@
     <title>HISTORY</title>
     <para>
       <command>b10-stats-httpd</command> was designed and implemented by Naoki
-      Kambe of JPRS in Mar 2011.
+      Kambe of JPRS in March 2011.
     </para>
   </refsect1>
 </refentry><!--
diff --git a/src/bin/stats/b10-stats.8 b/src/bin/stats/b10-stats.8
index 0204ca1..e72ec0e 100644
--- a/src/bin/stats/b10-stats.8
+++ b/src/bin/stats/b10-stats.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-stats
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: August 11, 2011
+.\"      Date: March 1, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-STATS" "8" "August 11, 2011" "BIND10" "BIND10"
+.TH "B10\-STATS" "8" "March 1, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -59,29 +59,25 @@ command does not have any configurable settings\&.
 The configuration commands are:
 .PP
 
-
-\fBremove\fR
-removes the named statistics name and data\&.
-.PP
-
-
-\fBreset\fR
-will reset all statistics data to default values except for constant names\&. This may re\-add previously removed statistics names\&.
-.PP
-
 \fBset\fR
+will set new statistics data specified in arguments\&. Statistics data to be set and the module name which owns statistics data are required in argument\&. Pid of the module in argument is optional\&.
 .PP
 
 \fBshow\fR
 will send the statistics data in JSON format\&. By default, it outputs all the statistics data it has collected\&. An optional item name may be specified to receive individual output\&.
 .PP
 
+\fBshowschema\fR
+will send the schema of the statistics data in JSON format\&. The output is equivalent to the statistics part of
+stats\&.spec\&.
+.PP
+
 \fBshutdown\fR
 will shutdown the
 \fBb10\-stats\fR
-process\&. (Note that the
-\fBbind10\fR
-parent may restart it\&.)
+process\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .PP
 
 \fBstatus\fR
@@ -90,25 +86,22 @@ simply indicates that the daemon is running\&.
 .PP
 The
 \fBb10\-stats\fR
-daemon contains these statistics:
+daemon contains these
+\(lqStats\(rq
+statistics:
 .PP
-report_time
-.RS 4
-The latest report date and time in ISO 8601 format\&.
-.RE
-.PP
-stats\&.boot_time
+boot_time
 .RS 4
 The date and time when this daemon was started in ISO 8601 format\&. This is a constant which can\'t be reset except by restarting
 \fBb10\-stats\fR\&.
 .RE
 .PP
-stats\&.last_update_time
+last_update_time
 .RS 4
 The date and time (in ISO 8601 format) when this daemon last received data from another component\&.
 .RE
 .PP
-stats\&.lname
+lname
 .RS 4
 This is the name used for the
 \fBb10\-msgq\fR
@@ -116,14 +109,19 @@ command\-control channel\&. (This is a constant which can\'t be reset except by
 \fBb10\-stats\fR\&.)
 .RE
 .PP
-stats\&.start_time
+report_time
+.RS 4
+The latest report date and time in ISO 8601 format\&.
+.RE
+.PP
+start_time
 .RS 4
 This is the date and time (in ISO 8601 format) when this daemon started collecting data\&.
 .RE
 .PP
-stats\&.timestamp
+timestamp
 .RS 4
-The current date and time represented in seconds since UNIX epoch (1970\-01\-01T0 0:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&.
+The current date and time represented in seconds since UNIX epoch (1970\-01\-01T00:00:00Z) with precision (delimited with a period) up to one hundred thousandth of second\&.
 .RE
 .PP
 See other manual pages for explanations for their statistics that are kept track by
@@ -150,5 +148,5 @@ The
 daemon was initially designed and implemented by Naoki Kambe of JPRS in October 2010\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml
index 13ada7a..b353f8f 100644
--- a/src/bin/stats/b10-stats.xml
+++ b/src/bin/stats/b10-stats.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010,2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>August 11, 2011</date>
+    <date>March 1, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -101,20 +101,10 @@
     </para>
 
     <para>
-<!-- TODO: remove is removed in trac930 -->
-      <command>remove</command> removes the named statistics name and data.
-    </para>
-
-    <para>
-<!-- TODO: reset is removed in trac930 -->
-      <command>reset</command> will reset all statistics data to
-      default values except for constant names.
-      This may re-add previously removed statistics names.
-    </para>
-
-    <para>
-      <command>set</command>
-<!-- TODO: document this -->
+      <command>set</command> will set new statistics data specified in
+      arguments. Statistics data to be set and the module name which owns
+      statistics data are required in argument. Pid of the module in argument
+      is optional.
     </para>
 
     <para>
@@ -124,12 +114,19 @@
       An optional item name may be specified to receive individual output.
     </para>
 
-<!-- TODO: document showschema -->
+    <para>
+      <command>showschema</command> will send the schema of the statistics data
+      in JSON format. The output is equivalent to the statistics part
+      of <filename>stats.spec</filename>.
+    </para>
 
     <para>
       <command>shutdown</command> will shutdown the
       <command>b10-stats</command> process.
-      (Note that the <command>bind10</command> parent may restart it.)
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
     </para>
 
     <para>
@@ -143,20 +140,15 @@
     <title>STATISTICS DATA</title>
 
     <para>
-      The <command>b10-stats</command> daemon contains these statistics:
+      The <command>b10-stats</command> daemon contains these
+      <quote>Stats</quote> statistics:
     </para>
 
     <variablelist>
 
-      <varlistentry>
-        <term>report_time</term>
-<!-- TODO: why not named stats.report_time? -->
-        <listitem><simpara>The latest report date and time in
-          ISO 8601 format.</simpara></listitem>
-      </varlistentry>
 
       <varlistentry>
-        <term>stats.boot_time</term>
+        <term>boot_time</term>
         <listitem><simpara>The date and time when this daemon was
           started in ISO 8601 format.
           This is a constant which can't be reset except by restarting
@@ -165,14 +157,14 @@
       </varlistentry>
 
       <varlistentry>
-        <term>stats.last_update_time</term>
+        <term>last_update_time</term>
         <listitem><simpara>The date and time (in ISO 8601 format)
           when this daemon last received data from another component.
         </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>stats.lname</term>
+        <term>lname</term>
         <listitem><simpara>This is the name used for the
           <command>b10-msgq</command> command-control channel.
           (This is a constant which can't be reset except by restarting
@@ -181,16 +173,22 @@
       </varlistentry>
 
       <varlistentry>
-        <term>stats.start_time</term>
+        <term>report_time</term>
+        <listitem><simpara>The latest report date and time in
+          ISO 8601 format.</simpara></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>start_time</term>
         <listitem><simpara>This is the date and time (in ISO 8601 format)
           when this daemon started collecting data.
         </simpara></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>stats.timestamp</term>
+        <term>timestamp</term>
         <listitem><simpara>The current date and time represented in
-          seconds since UNIX epoch (1970-01-01T0 0:00:00Z) with
+          seconds since UNIX epoch (1970-01-01T00:00:00Z) with
           precision (delimited with a period) up to
           one hundred thousandth of second.</simpara></listitem>
       </varlistentry>
diff --git a/src/bin/stats/stats.py.in b/src/bin/stats/stats.py.in
index 938a062..fd59c3c 100755
--- a/src/bin/stats/stats.py.in
+++ b/src/bin/stats/stats.py.in
@@ -129,6 +129,8 @@ class Stats:
         self.module_name = self.mccs.get_module_spec().get_module_name()
         self.modules = {}
         self.statistics_data = {}
+        # statistics data by each pid
+        self.statistics_data_bypid = {}
         # get commands spec
         self.commands_spec = self.mccs.get_module_spec().get_commands_spec()
         # add event handler related command_handler of ModuleCCSession
@@ -265,36 +267,129 @@ class Stats:
                          + "owner: " + str(owner) + ", "
                          + "name: " + str(name))
 
-    def update_statistics_data(self, owner=None, **data):
+    def update_statistics_data(self, owner=None, pid=-1, **data):
         """
         change statistics date of specified module into specified
         data. It updates information of each module first, and it
         updates statistics data. If specified data is invalid for
         statistics spec of specified owner, it returns a list of error
-        messeges. If there is no error or if neither owner nor data is
-        specified in args, it returns None.
+        messages. If there is no error or if neither owner nor data is
+        specified in args, it returns None. pid is the process id of
+        the sender module in order for stats to identify which
+        instance sends statistics data in the situation that multiple
+        instances are working.
         """
+        # Note:
+        # The fix of #1751 is for multiple instances working. It is
+        # assumed here that they send different statistics data with
+        # each PID. Stats should save their statistics data by
+        # PID. The statistics data, which is the existing variable, is
+        # preserved by accumlating from statistics data by PID. This
+        # is an ad-hoc fix because administrators can not see
+        # statistics by each instance via bindctl or HTTP/XML. These
+        # interfaces aren't changed in this fix.
+
+        def _accum_bymodule(statistics_data_bypid):
+            # This is an internal function for the superordinate
+            # function. It accumulates statistics data of each PID by
+            # module. It returns the accumulation result.
+            def _accum(a, b):
+                # If the first arg is dict or list type, two values
+                # would be merged and accumlated.
+                if type(a) is dict:
+                    return dict([ (k, _accum(v, b[k])) \
+                                      if k in b else (k, v) \
+                                      for (k, v) in a.items() ] \
+                                    + [ (k, v) \
+                                            for (k, v) in b.items() \
+                                            if k not in a ])
+                elif type(a) is list:
+                    return [ _accum(a[i], b[i]) \
+                                 if len(b) > i else a[i] \
+                                 for i in range(len(a)) ] \
+                                 + [ b[i] \
+                                         for i in range(len(b)) \
+                                         if len(a) <= i ]
+                # If the first arg is integer or float type, two
+                # values are just added.
+                elif type(a) is int or type(a) is float:
+                    return a + b
+                # If the first arg is str or other types than above,
+                # then it just returns the first arg which is assumed
+                # to be the newer value.
+                return a
+            ret = {}
+            for data in statistics_data_bypid.values():
+                ret.update(_accum(data, ret))
+            return ret
+
+        # Firstly, it gets default statistics data in each spec file.
         self.update_modules()
         statistics_data = {}
         for (name, module) in self.modules.items():
             value = get_spec_defaults(module.get_statistics_spec())
             if module.validate_statistics(True, value):
                 statistics_data[name] = value
-        for (name, value) in self.statistics_data.items():
-            if name in statistics_data:
-                statistics_data[name].update(value)
-            else:
-                statistics_data[name] = value
         self.statistics_data = statistics_data
+
+        # If the "owner" and "data" arguments in this function are
+        # specified, then the variable of statistics data of each pid
+        # would be updated.
+        errors = []
         if owner and data:
-            errors = []
             try:
                 if self.modules[owner].validate_statistics(False, data, errors):
-                    self.statistics_data[owner].update(data)
-                    return
+                    if owner in self.statistics_data_bypid:
+                        if pid in self.statistics_data_bypid[owner]:
+                            self.statistics_data_bypid[owner][pid].update(data)
+                        else:
+                            self.statistics_data_bypid[owner][pid] = data
+                    else:
+                        self.statistics_data_bypid[owner] = { pid : data }
             except KeyError:
                 errors.append("unknown module name: " + str(owner))
-            return errors
+
+        # If there are inactive instances, which was actually running
+        # on the system before, their statistics data would be
+        # removed. To find inactive instances, it invokes the
+        # "show_processes" command to Boss via the cc session. Then it
+        # gets active instance list and compares its PIDs with PIDs in
+        # statistics data which it already has. If inactive instances
+        # are found, it would remove their statistics data.
+        seq = self.cc_session.group_sendmsg(
+            isc.config.ccsession.create_command("show_processes", None),
+            "Boss")
+        (answer, env) = self.cc_session.group_recvmsg(False, seq)
+        if answer:
+            (rcode, value) = isc.config.ccsession.parse_answer(answer)
+            if rcode == 0:
+                if type(value) is list and len(value) > 0 \
+                        and type(value[0]) is list and len(value[0]) > 1:
+                    mlist = [ k for k in self.statistics_data_bypid.keys() ]
+                    for m in mlist:
+                        # PID list which it has before except for -1
+                        plist1 = [ p for p in self.statistics_data_bypid[m]\
+                                       .keys() if p != -1]
+                        # PID list of active instances which is
+                        # received from Boss
+                        plist2 = [ v[0] for v in value \
+                                       if v[1].lower().find(m.lower()) \
+                                       >= 0 ]
+                        # get inactive instance list by the difference
+                        # between plist1 and plist2
+                        nplist = set(plist1).difference(set(plist2))
+                        for p in nplist:
+                            self.statistics_data_bypid[m].pop(p)
+                        if self.statistics_data_bypid[m]:
+                            if m in self.statistics_data:
+                                self.statistics_data[m].update(
+                                    _accum_bymodule(
+                                        self.statistics_data_bypid[m]))
+                        # remove statistics data of the module with no
+                        # PID
+                        else:
+                            self.statistics_data_bypid.pop(m)
+        if errors: return errors
 
     def command_status(self):
         """
@@ -379,11 +474,11 @@ class Stats:
                 1, "specified arguments are incorrect: " \
                     + "owner: " + str(owner) + ", name: " + str(name))
 
-    def command_set(self, owner, data):
+    def command_set(self, owner, pid=-1, data={}):
         """
         handle set command
         """
-        errors = self.update_statistics_data(owner, **data)
+        errors = self.update_statistics_data(owner, pid, **data)
         if errors:
             return isc.config.create_answer(
                 1, "errors while setting statistics data: " \
diff --git a/src/bin/stats/stats.spec b/src/bin/stats/stats.spec
index d3bdcca..e25dfad 100644
--- a/src/bin/stats/stats.spec
+++ b/src/bin/stats/stats.spec
@@ -72,6 +72,13 @@
             "item_description": "module name of the owner of the statistics data"
           },
 	  {
+	    "item_name": "pid",
+            "item_type": "integer",
+            "item_optional": true,
+            "item_default": -1,
+            "item_description": "process id of the owner module"
+          },
+	  {
 	    "item_name": "data",
             "item_type": "map",
             "item_optional": false,
diff --git a/src/bin/stats/stats_httpd.py.in b/src/bin/stats/stats_httpd.py.in
index c9bd0f5..7e4da96 100644
--- a/src/bin/stats/stats_httpd.py.in
+++ b/src/bin/stats/stats_httpd.py.in
@@ -189,7 +189,12 @@ class StatsHttpd:
         self.load_config()
         self.http_addrs = []
         self.mccs.start()
-        self.open_httpd()
+        try:
+            self.open_httpd()
+        except HttpServerError:
+            # if some exception, e.g. address in use, is raised, then it closes mccs and httpd
+            self.close_mccs()
+            raise
 
     def open_mccs(self):
         """Opens a ModuleCCSession object"""
diff --git a/src/bin/stats/tests/b10-stats-httpd_test.py b/src/bin/stats/tests/b10-stats-httpd_test.py
index e74405a..57abed9 100644
--- a/src/bin/stats/tests/b10-stats-httpd_test.py
+++ b/src/bin/stats/tests/b10-stats-httpd_test.py
@@ -676,6 +676,24 @@ class TestStatsHttpd(unittest.TestCase):
         self.assertTrue('address' in self.stats_httpd.config['listen_on'][0])
         self.assertTrue('port' in self.stats_httpd.config['listen_on'][0])
         self.assertTrue(server_address in set(self.stats_httpd.http_addrs))
+        ans = send_command(
+            isc.config.ccsession.COMMAND_GET_MODULE_SPEC,
+            "ConfigManager", {"module_name":"StatsHttpd"})
+        # assert StatsHttpd is added to ConfigManager
+        self.assertNotEqual(ans, (0,{}))
+        self.assertTrue(ans[1]['module_name'], 'StatsHttpd')
+
+    def test_init_hterr(self):
+        orig_open_httpd = stats_httpd.StatsHttpd.open_httpd
+        def err_open_httpd(arg): raise stats_httpd.HttpServerError
+        stats_httpd.StatsHttpd.open_httpd = err_open_httpd
+        self.assertRaises(stats_httpd.HttpServerError, stats_httpd.StatsHttpd)
+        ans = send_command(
+            isc.config.ccsession.COMMAND_GET_MODULE_SPEC,
+            "ConfigManager", {"module_name":"StatsHttpd"})
+        # assert StatsHttpd is removed from ConfigManager
+        self.assertEqual(ans, (0,{}))
+        stats_httpd.StatsHttpd.open_httpd = orig_open_httpd
 
     def test_openclose_mccs(self):
         self.stats_httpd = MyStatsHttpd(get_availaddr())
diff --git a/src/bin/stats/tests/b10-stats_test.py b/src/bin/stats/tests/b10-stats_test.py
index d9f8d37..5262b8a 100644
--- a/src/bin/stats/tests/b10-stats_test.py
+++ b/src/bin/stats/tests/b10-stats_test.py
@@ -373,6 +373,68 @@ class TestStats(unittest.TestCase):
         self.assertEqual(self.stats.update_statistics_data(owner='Dummy', foo='bar'),
                          ['unknown module name: Dummy'])
 
+    def test_update_statistics_data_withpid(self):
+        # one pid of Auth
+        self.stats.update_statistics_data(owner='Auth',
+                                          pid=9999,
+                                          **{'queries.tcp':1001})
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 1001)
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9999 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9999])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9999]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid,
+                         {'Auth': {9999: {'queries.tcp': 1001}}})
+        # non-existent pid of Auth, but no changes in statistics data
+        self.stats.update_statistics_data(owner='Auth',
+                                          pid=10000,
+                                          **{'queries.tcp':2001})
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 1001)
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9999 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9999])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9999]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid,
+                         {'Auth': {9999: {'queries.tcp': 1001}}})
+        # kill running Auth, then statistics is reset
+        self.assertEqual(self.base.boss.server.pid_list[0][0], 9999)
+        killed = self.base.boss.server.pid_list.pop(0)
+        self.stats.update_statistics_data()
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 0)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'], 0)
+        self.assertFalse('Auth' in self.stats.statistics_data_bypid)
+        # restore statistics data of killed auth
+        self.base.boss.server.pid_list = [ killed ] + self.base.boss.server.pid_list[:]
+        self.stats.update_statistics_data(owner='Auth',
+                                          pid=9999,
+                                          **{'queries.tcp':1001})
+        # another pid of Auth
+        self.stats.update_statistics_data(owner='Auth',
+                                          pid=9998,
+                                          **{'queries.tcp':1002,
+                                             'queries.udp':1003})
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 2003)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'], 1003)
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9999 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue(9998 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9999])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9998])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9998])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9999]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9998]['queries.tcp'], 1002)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9998]['queries.udp'], 1003)
+
     def test_commands(self):
         # status
         self.assertEqual(self.stats.command_status(),
@@ -710,6 +772,123 @@ class TestStats(unittest.TestCase):
         self.assertRaises(stats.StatsError,
                           self.stats.command_set, owner='Stats', data={ 'dummy' : '_xxxx_yyyy_zzz_' })
 
+    def test_command_set_withpid(self):
+        # one pid of Auth
+        retval = isc.config.ccsession.parse_answer(
+            self.stats.command_set(owner='Auth',
+                                   pid=9997,
+                                   data={ 'queries.tcp' : 1001,
+                                          'queries.perzone':
+                                              [{ 'zonename': 'test1.example',
+                                                 'queries.tcp': 1 },
+                                               { 'zonename': 'test2.example',
+                                                 'queries.tcp': 2,
+                                                 'queries.udp': 3 }]}))
+        self.assertEqual(retval, (0,None))
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 1 },
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 2,
+                            'queries.udp': 3 }])
+        self.assertTrue('Stats' in self.stats.statistics_data)
+        self.assertTrue('last_update_time' in self.stats.statistics_data['Stats'])
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9997 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9997])
+        self.assertTrue('queries.perzone' in self.stats.statistics_data_bypid['Auth'][9997])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 1 },
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 2,
+                            'queries.udp': 3 }])
+        # non-existent pid of Auth, but no changes in statistics data
+        retval = isc.config.ccsession.parse_answer(
+            self.stats.command_set(owner='Auth',
+                                   pid=10000,
+                                   data={ 'queries.tcp' : 2001,
+                                          'queries.perzone':
+                                              [{ 'zonename': 'test1.example',
+                                                 'queries.tcp': 101 },
+                                               { 'zonename': 'test2.example',
+                                                 'queries.tcp': 102,
+                                                 'queries.udp': 103 }]}))
+        self.assertEqual(retval, (0,None))
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 1 },
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 2,
+                            'queries.udp': 3 }])
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9997 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9997])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 1 },
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 2,
+                            'queries.udp': 3 }])
+        # another pid of Auth
+        retval = isc.config.ccsession.parse_answer(
+            self.stats.command_set(owner='Auth',
+                                   pid=9996,
+                                   data={ 'queries.tcp' : 1002,
+                                          'queries.udp' : 1003,
+                                          'queries.perzone':
+                                              [{ 'zonename': 'test1.example',
+                                                 'queries.tcp': 10,
+                                                 'queries.udp': 11},
+                                               { 'zonename': 'test2.example',
+                                                 'queries.tcp': 12,
+                                                 'queries.udp': 13 }]}))
+        self.assertEqual(retval, (0,None))
+        self.assertTrue('Auth' in self.stats.statistics_data)
+        self.assertTrue('queries.tcp' in self.stats.statistics_data['Auth'])
+        self.assertTrue('queries.udp' in self.stats.statistics_data['Auth'])
+        self.assertTrue('queries.perzone' in self.stats.statistics_data['Auth'])
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.tcp'], 2003)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.udp'], 1003)
+        self.assertEqual(self.stats.statistics_data['Auth']['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 11,
+                            'queries.udp': 11},
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 14,
+                            'queries.udp': 16 }])
+        self.assertTrue('Auth' in self.stats.statistics_data_bypid)
+        self.assertTrue(9997 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue(9996 in self.stats.statistics_data_bypid['Auth'])
+        self.assertTrue('queries.tcp' in self.stats.statistics_data_bypid['Auth'][9997])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9996])
+        self.assertTrue('queries.udp' in self.stats.statistics_data_bypid['Auth'][9996])
+        self.assertTrue('queries.perzone' in self.stats.statistics_data_bypid['Auth'][9996])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.tcp'], 1001)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9997]['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 1 },
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 2,
+                            'queries.udp': 3 }])
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9996]['queries.tcp'], 1002)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9996]['queries.udp'], 1003)
+        self.assertEqual(self.stats.statistics_data_bypid['Auth'][9996]['queries.perzone'],
+                         [{ 'zonename': 'test1.example',
+                            'queries.tcp': 10,
+                            'queries.udp': 11},
+                          { 'zonename': 'test2.example',
+                            'queries.tcp': 12,
+                            'queries.udp': 13 }])
+
 class TestOSEnv(unittest.TestCase):
     def test_osenv(self):
         """
diff --git a/src/bin/stats/tests/test_utils.py b/src/bin/stats/tests/test_utils.py
index 29fc785..d91c1f2 100644
--- a/src/bin/stats/tests/test_utils.py
+++ b/src/bin/stats/tests/test_utils.py
@@ -150,6 +150,11 @@ class MockBoss:
         "command_name": "sendstats",
         "command_description": "Send data to a statistics module at once",
         "command_args": []
+      },
+      {
+        "command_name": "show_processes",
+        "command_description": "List the running BIND 10 processes",
+        "command_args": []
       }
     ],
     "statistics": [
@@ -180,6 +185,10 @@ class MockBoss:
         self.spec_file.close()
         self.cc_session = self.mccs._session
         self.got_command_name = ''
+        self.pid_list = [[ 9999, "b10-auth"   ],
+                         [ 9998, "b10-auth-2" ],
+                         [ 9997, "b10-auth-3" ],
+                         [ 9996, "b10-auth-4" ]]
 
     def run(self):
         self.mccs.start()
@@ -210,6 +219,10 @@ class MockBoss:
             return isc.config.create_answer(0)
         elif command == 'getstats':
             return isc.config.create_answer(0, params)
+        elif command == 'show_processes':
+            # Return dummy pids
+            return isc.config.create_answer(
+                0, self.pid_list)
         return isc.config.create_answer(1, "Unknown Command")
 
 class MockAuth:
@@ -340,7 +353,7 @@ class MockAuth:
             params = { "owner": "Auth",
                        "data": { 'queries.tcp': self.queries_tcp,
                                  'queries.udp': self.queries_udp,
-                                 'queries.per-zone' : self.queries_per_zone } }
+                                 'queries.perzone' : self.queries_per_zone } }
             return send_command("set", "Stats", params=params, session=self.cc_session)
         return isc.config.create_answer(1, "Unknown Command")
 
diff --git a/src/bin/tests/.gitignore b/src/bin/tests/.gitignore
new file mode 100644
index 0000000..b39aa86
--- /dev/null
+++ b/src/bin/tests/.gitignore
@@ -0,0 +1 @@
+/process_rename_test.py
diff --git a/src/bin/usermgr/.gitignore b/src/bin/usermgr/.gitignore
new file mode 100644
index 0000000..e116052
--- /dev/null
+++ b/src/bin/usermgr/.gitignore
@@ -0,0 +1,3 @@
+/b10-cmdctl-usermgr
+/b10-cmdctl-usermgr.py
+/run_b10-cmdctl-usermgr.sh
diff --git a/src/bin/xfrin/.gitignore b/src/bin/xfrin/.gitignore
new file mode 100644
index 0000000..5ac1942
--- /dev/null
+++ b/src/bin/xfrin/.gitignore
@@ -0,0 +1,3 @@
+/b10-xfrin
+/run_b10-xfrin.sh
+/xfrin.py
diff --git a/src/bin/xfrin/tests/.gitignore b/src/bin/xfrin/tests/.gitignore
new file mode 100644
index 0000000..4de3f47
--- /dev/null
+++ b/src/bin/xfrin/tests/.gitignore
@@ -0,0 +1 @@
+/xfrin_test
diff --git a/src/bin/xfrin/tests/testdata/example.com.sqlite3 b/src/bin/xfrin/tests/testdata/example.com.sqlite3
index 3538e3d..1008249 100644
Binary files a/src/bin/xfrin/tests/testdata/example.com.sqlite3 and b/src/bin/xfrin/tests/testdata/example.com.sqlite3 differ
diff --git a/src/bin/xfrin/tests/xfrin_test.py b/src/bin/xfrin/tests/xfrin_test.py
index 89e4d96..b88d6a9 100644
--- a/src/bin/xfrin/tests/xfrin_test.py
+++ b/src/bin/xfrin/tests/xfrin_test.py
@@ -76,6 +76,27 @@ example_soa_question = Question(TEST_ZONE_NAME, TEST_RRCLASS, RRType.SOA())
 default_questions = [example_axfr_question]
 default_answers = [soa_rrset]
 
+def get_fake_time_time():
+    '''Returns a temporary replacement function for time.time(), which
+       always returns 0.1 more than the previous call. This is to make
+       sure these tests do not fail on systems where the time.time()
+       function has a high minimal accuracy.
+       This fake time.time() is usually set in place of the real one
+       where we need testing of get_running_time(). It is done is
+       as low a scope as possible, so as not to mess up unit test
+       framework time related tests. It must be set before
+       XfrinTransferState (or any class that initializes that) is
+       initialized.
+       And every time it is set up, in must be reset later (again, so
+       as not to mess up the framework's concept of time).
+    '''
+    fake_time = 0.0
+    def fake_time_time():
+        nonlocal fake_time
+        fake_time += 0.1
+        return fake_time
+    return fake_time_time
+
 def check_diffs(assert_fn, expected, actual):
     '''A helper function checking the differences made in the XFR session.
 
@@ -118,6 +139,9 @@ class MockCC(MockModuleCCSession):
         if identifier == "zones/use_ixfr":
             return False
 
+    def remove_remote_config(self, module_name):
+        pass
+
 class MockDataSourceClient():
     '''A simple mock data source client.
 
@@ -561,7 +585,7 @@ class TestXfrinIXFREnd(TestXfrinState):
     def test_finish_message(self):
         self.assertFalse(self.state.finish_message(self.conn))
 
-class TestXfrinIXFREnd(TestXfrinState):
+class TestXfrinIXFREndUpToDate(TestXfrinState):
     def setUp(self):
         super().setUp()
         self.state = XfrinIXFRUptodate()
@@ -759,9 +783,15 @@ class TestXfrinConnection(unittest.TestCase):
 
 class TestAXFR(TestXfrinConnection):
     def setUp(self):
+        # replace time.time with a steadily increasing fake one
+        self.orig_time_time = time.time
+        time.time = get_fake_time_time()
         super().setUp()
         XfrinInitialSOA().set_xfrstate(self.conn, XfrinInitialSOA())
 
+    def tearDown(self):
+        time.time = self.orig_time_time
+
     def __create_mock_tsig(self, key, error):
         # This helper function creates a MockTSIGContext for a given key
         # and TSIG error to be used as a result of verify (normally faked
@@ -1318,6 +1348,14 @@ class TestAXFR(TestXfrinConnection):
         self.assertEqual(self.conn.do_xfrin(False), XFRIN_OK)
         self.assertFalse(self.conn._datasrc_client._journaling_enabled)
 
+        self.assertEqual(2, self.conn._transfer_stats.message_count)
+        self.assertEqual(2, self.conn._transfer_stats.axfr_rr_count)
+        self.assertEqual(0, self.conn._transfer_stats.ixfr_changeset_count)
+        self.assertEqual(0, self.conn._transfer_stats.ixfr_deletion_count)
+        self.assertEqual(0, self.conn._transfer_stats.ixfr_addition_count)
+        self.assertEqual(177, self.conn._transfer_stats.byte_count)
+        self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
     def test_do_xfrin_with_tsig(self):
         # use TSIG with a mock context.  we fake all verify results to
         # emulate successful verification.
@@ -1461,6 +1499,10 @@ class TestAXFR(TestXfrinConnection):
 
 class TestIXFRResponse(TestXfrinConnection):
     def setUp(self):
+        # replace time.time with a steadily increasing fake one
+        self.orig_time_time = time.time
+        time.time = get_fake_time_time()
+
         super().setUp()
         self.conn._query_id = self.conn.qid = 1035
         self.conn._request_serial = isc.dns.Serial(1230)
@@ -1468,6 +1510,9 @@ class TestIXFRResponse(TestXfrinConnection):
         self.conn._datasrc_client = MockDataSourceClient()
         XfrinInitialSOA().set_xfrstate(self.conn, XfrinInitialSOA())
 
+    def tearDown(self):
+        time.time = self.orig_time_time
+
     def test_ixfr_response(self):
         '''A simplest form of IXFR response.
 
@@ -1662,8 +1707,14 @@ class TestIXFRSession(TestXfrinConnection):
     the general logic flow.
     '''
     def setUp(self):
+        # replace time.time with a steadily increasing fake one
+        self.orig_time_time = time.time
+        time.time = get_fake_time_time()
         super().setUp()
 
+    def tearDown(self):
+        time.time = self.orig_time_time
+
     def test_do_xfrin(self):
         def create_ixfr_response():
             self.conn.reply_data = self.conn.create_response_data(
@@ -1687,6 +1738,14 @@ class TestIXFRSession(TestXfrinConnection):
         self.assertEqual(TEST_ZONE_NAME, qmsg.get_question()[0].get_name())
         self.assertEqual(RRType.IXFR(), qmsg.get_question()[0].get_type())
 
+        self.assertEqual(1, self.conn._transfer_stats.message_count)
+        self.assertEqual(0, self.conn._transfer_stats.axfr_rr_count)
+        self.assertEqual(1, self.conn._transfer_stats.ixfr_changeset_count)
+        self.assertEqual(1, self.conn._transfer_stats.ixfr_deletion_count)
+        self.assertEqual(1, self.conn._transfer_stats.ixfr_addition_count)
+        self.assertEqual(188, self.conn._transfer_stats.byte_count)
+        self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
     def test_do_xfrin_fail(self):
         '''IXFR fails due to a protocol error.
 
@@ -1719,6 +1778,14 @@ class TestIXFRSession(TestXfrinConnection):
         self.conn.response_generator = create_response
         self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, RRType.IXFR()))
 
+        self.assertEqual(1, self.conn._transfer_stats.message_count)
+        self.assertEqual(0, self.conn._transfer_stats.axfr_rr_count)
+        self.assertEqual(0, self.conn._transfer_stats.ixfr_changeset_count)
+        self.assertEqual(0, self.conn._transfer_stats.ixfr_deletion_count)
+        self.assertEqual(0, self.conn._transfer_stats.ixfr_addition_count)
+        self.assertEqual(80, self.conn._transfer_stats.byte_count)
+        self.assertGreater(self.conn._transfer_stats.get_running_time(), 0)
+
 class TestXFRSessionWithSQLite3(TestXfrinConnection):
     '''Tests for XFR sessions using an SQLite3 DB.
 
@@ -1734,6 +1801,9 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
         self.empty_sqlite3db_obj = TESTDATA_OBJDIR + '/empty.sqlite3'
         self.sqlite3db_cfg = "{ \"database_file\": \"" +\
                              self.sqlite3db_obj + "\"}"
+        # replace time.time with a steadily increasing fake one
+        self.orig_time_time = time.time
+        time.time = get_fake_time_time()
         super().setUp()
         if os.path.exists(self.sqlite3db_obj):
             os.unlink(self.sqlite3db_obj)
@@ -1748,6 +1818,7 @@ class TestXFRSessionWithSQLite3(TestXfrinConnection):
             os.unlink(self.sqlite3db_obj)
         if os.path.exists(self.empty_sqlite3db_obj):
             os.unlink(self.empty_sqlite3db_obj)
+        time.time = self.orig_time_time
 
     def get_zone_serial(self):
         result, finder = self.conn._datasrc_client.find_zone(TEST_ZONE_NAME)
@@ -2506,6 +2577,134 @@ class TestXfrin(unittest.TestCase):
         self.common_ixfr_setup('refresh', False)
         self.assertEqual(RRType.AXFR(), self.xfr.xfrin_started_request_type)
 
+class TextXfrinMemoryZones(unittest.TestCase):
+    def setUp(self):
+        self.xfr = MockXfrin()
+        # Configuration snippet containing 2 memory datasources,
+        # one for IN and one for CH. Both contain a zone 'example.com'
+        # the IN ds also contains a zone example2.com, and a zone example3.com,
+        # which is of file type 'text' (and hence, should be ignored)
+        self.config = { 'datasources': [
+                          { 'type': 'memory',
+                            'class': 'IN',
+                            'zones': [
+                              { 'origin': 'example.com',
+                                'filetype': 'sqlite3' },
+                              { 'origin': 'EXAMPLE2.com.',
+                                'filetype': 'sqlite3' },
+                              { 'origin': 'example3.com',
+                                'filetype': 'text' }
+                            ]
+                          },
+                          { 'type': 'memory',
+                            'class': 'ch',
+                            'zones': [
+                              { 'origin': 'example.com',
+                                'filetype': 'sqlite3' }
+                            ]
+                          }
+                      ] }
+
+    def test_updates(self):
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+        # add them all
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
+        # Remove the CH data source from the self.config snippet, and update
+        del self.config['datasources'][1]
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+        # Remove example2.com from the datasource, and update
+        del self.config['datasources'][0]['zones'][1]
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+        # If 'datasources' is not in the self.config update list (i.e. its
+        # self.config has not changed), no difference should be found
+        self.xfr._set_memory_zones({}, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+        # If datasources list becomes empty, everything should be removed
+        self.config['datasources'][0]['zones'] = []
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_normalization(self):
+        self.xfr._set_memory_zones(self.config, None)
+        # make sure it is case insensitive, root-dot-insensitive,
+        # and supports CLASSXXX notation
+        self.assertTrue(self.xfr._is_memory_zone("EXAMPLE.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "in"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com.", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CLASS3"))
+
+    def test_bad_name(self):
+        # First set it to some config
+        self.xfr._set_memory_zones(self.config, None)
+
+        # Error checking; bad owner name should result in no changes
+        self.config['datasources'][1]['zones'][0]['origin'] = ".."
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_bad_class(self):
+        # First set it to some config
+        self.xfr._set_memory_zones(self.config, None)
+
+        # Error checking; bad owner name should result in no changes
+        self.config['datasources'][1]['class'] = "Foo"
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_no_filetype(self):
+        # omitting the filetype should leave that zone out, but not
+        # the rest
+        del self.config['datasources'][1]['zones'][0]['filetype']
+        self.xfr._set_memory_zones(self.config, None)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example.com", "CH"))
+
+    def test_class_filetype(self):
+        # omitting the class should have it default to what is in the
+        # specfile for Auth.
+        AuthConfigData = isc.config.config_data.ConfigData(
+            isc.config.module_spec_from_file(xfrin.AUTH_SPECFILE_LOCATION))
+        del self.config['datasources'][0]['class']
+        self.xfr._set_memory_zones(self.config, AuthConfigData)
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example2.com", "IN"))
+        self.assertFalse(self.xfr._is_memory_zone("example3.com", "IN"))
+        self.assertTrue(self.xfr._is_memory_zone("example.com", "CH"))
+
 def raise_interrupt():
     raise KeyboardInterrupt()
 
@@ -2715,6 +2914,68 @@ class TestFormatting(unittest.TestCase):
         self.assertRaises(TypeError, format_addrinfo,
                                      (socket.AF_INET, "asdf", ()))
 
+class TestXfrinTransferStats(unittest.TestCase):
+    def setUp(self):
+        # replace time.time with a steadily increasing fake one
+        self.orig_time_time = time.time
+        time.time = get_fake_time_time()
+        self.stats = XfrinTransferStats()
+
+    def tearDown(self):
+        time.time = self.orig_time_time
+
+    def zero_check(self):
+        # Checks whether all counters are zero
+        self.assertEqual(0, self.stats.message_count)
+        self.assertEqual(0, self.stats.axfr_rr_count)
+        self.assertEqual(0, self.stats.byte_count)
+        self.assertEqual(0, self.stats.ixfr_changeset_count)
+        self.assertEqual(0, self.stats.ixfr_deletion_count)
+        self.assertEqual(0, self.stats.ixfr_addition_count)
+
+    def test_init(self):
+        self.zero_check()
+        self.assertIsNone(self.stats._end_time)
+
+    def test_get_running_time(self):
+        self.assertIsNone(self.stats._end_time)
+        runtime = self.stats.get_running_time()
+        self.assertIsNotNone(self.stats._end_time)
+        self.assertGreater(runtime, 0)
+        # make sure a second get does not change anything
+        runtime2 = self.stats.get_running_time()
+        self.assertEqual(runtime, runtime2)
+        # And that no counters have been modified
+        self.zero_check()
+
+    def test_bytes_per_second(self):
+        zbps = self.stats.get_bytes_per_second()
+        self.assertEqual(0, zbps)
+
+        self.stats._start_time = 1
+        self.stats._end_time = 2
+        self.stats.byte_count += 4
+        zbps = self.stats.get_bytes_per_second()
+        self.assertEqual(4, zbps)
+
+        self.stats._start_time = float(1)
+        self.stats._end_time = float(11)
+        self.assertEqual(10, self.stats.get_running_time())
+        self.stats.byte_count = 1234
+        zbps = self.stats.get_bytes_per_second()
+        self.assertEqual(123.4, zbps)
+
+        # if for some reason the runtime is 0, depending
+        # on whether bytes have actually been seen, bps is either
+        # 0 or 'infinite'
+        self.stats._end_time = self.stats._start_time
+        zbps = self.stats.get_bytes_per_second()
+        self.assertEqual(float("inf"), zbps)
+
+        self.stats.byte_count = 0
+        zbps = self.stats.get_bytes_per_second()
+        self.assertEqual(0, zbps)
+
 if __name__== "__main__":
     try:
         isc.log.resetUnitTestRootLogger()
diff --git a/src/bin/xfrin/xfrin.py.in b/src/bin/xfrin/xfrin.py.in
index b59c2b6..27b91a9 100755
--- a/src/bin/xfrin/xfrin.py.in
+++ b/src/bin/xfrin/xfrin.py.in
@@ -24,6 +24,7 @@ import struct
 import threading
 import socket
 import random
+import time
 from functools import reduce
 from optparse import OptionParser, OptionValueError
 from isc.config.ccsession import *
@@ -37,6 +38,11 @@ from isc.log_messages.xfrin_messages import *
 isc.log.init("b10-xfrin")
 logger = isc.log.Logger("xfrin")
 
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
+DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
+
 try:
     from pydnspp import *
 except ImportError as e:
@@ -361,7 +367,6 @@ class XfrinFirstData(XfrinState):
                  conn.zone_str())
             # We are now going to add RRs to the new zone.  We need create
             # a Diff object.  It will be used throughtout the XFR session.
-            # DISABLE FOR DEBUG
             conn._diff = Diff(conn._datasrc_client, conn._zone_name, True)
             self.set_xfrstate(conn, XfrinAXFR())
         return False
@@ -381,6 +386,7 @@ class XfrinIXFRDeleteSOA(XfrinState):
         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
         return True
 
 class XfrinIXFRDelete(XfrinState):
@@ -391,6 +397,7 @@ class XfrinIXFRDelete(XfrinState):
             self.set_xfrstate(conn, XfrinIXFRAddSOA())
             return False
         conn._diff.delete_data(rr)
+        conn.get_transfer_stats().ixfr_deletion_count += 1
         return True
 
 class XfrinIXFRAddSOA(XfrinState):
@@ -402,11 +409,14 @@ class XfrinIXFRAddSOA(XfrinState):
                                  ' RR is given in IXFRAddSOA state')
         conn._diff.add_data(rr)
         self.set_xfrstate(conn, XfrinIXFRAdd())
+        conn.get_transfer_stats().ixfr_addition_count += 1
         return True
 
 class XfrinIXFRAdd(XfrinState):
     def handle_rr(self, conn, rr):
         if rr.get_type() == RRType.SOA():
+            # This SOA marks the end of a difference sequence
+            conn.get_transfer_stats().ixfr_changeset_count += 1
             soa_serial = get_soa_serial(rr.get_rdata()[0])
             if soa_serial == conn._end_serial:
                 conn._diff.commit()
@@ -422,6 +432,7 @@ class XfrinIXFRAdd(XfrinState):
                 self.set_xfrstate(conn, XfrinIXFRDeleteSOA())
                 return False
         conn._diff.add_data(rr)
+        conn.get_transfer_stats().ixfr_addition_count += 1
         return True
 
 class XfrinIXFREnd(XfrinState):
@@ -462,6 +473,7 @@ class XfrinAXFR(XfrinState):
                             conn._end_serial, soa_serial)
 
             self.set_xfrstate(conn, XfrinAXFREnd())
+        conn.get_transfer_stats().axfr_rr_count += 1
         # Yes, we've eaten this RR.
         return True
 
@@ -484,6 +496,55 @@ class XfrinAXFREnd(XfrinState):
         conn._diff.commit()
         return False
 
+class XfrinTransferStats:
+    """
+    This class keeps a record of transfer data for logging purposes.
+    It records number of messages, rrs, and bytes transfered, as well
+    as the start and end time. The start time is set upon instantiation of
+    this class. The end time is set the first time finalize(),
+    get_running_time(), or get_bytes_per_second() is called. The end time is
+    set only once; subsequent calls to any of these methods does not modify
+    it further.
+    All _count instance variables can be directly set as needed by the
+    class collecting these results.
+    """
+    def __init__(self):
+        self.message_count = 0
+        self.axfr_rr_count = 0
+        self.byte_count = 0
+        self.ixfr_changeset_count = 0;
+        self.ixfr_deletion_count = 0;
+        self.ixfr_addition_count = 0;
+        self._start_time = time.time()
+        self._end_time = None
+
+    def finalize(self):
+        """Sets the end time to time.time() if not done already."""
+        if self._end_time is None:
+            self._end_time = time.time()
+
+    def get_running_time(self):
+        """Calls finalize(), then returns the difference between creation
+           and finalization time"""
+        self.finalize()
+        return self._end_time - self._start_time
+
+    def get_bytes_per_second(self):
+        """Returns the number of bytes per second, based on the result of
+           get_running_time() and the value of bytes_count."""
+        runtime = self.get_running_time()
+        if runtime > 0.0:
+            return float(self.byte_count) / runtime
+        else:
+            # This should never happen, but if some clock is so
+            # off or reset in the meantime, we do need to return
+            # *something* (and not raise an error)
+            if self.byte_count == 0:
+                return 0.0
+            else:
+                return float("inf")
+
+
 class XfrinConnection(asyncore.dispatcher):
     '''Do xfrin in this class. '''
 
@@ -534,6 +595,10 @@ class XfrinConnection(asyncore.dispatcher):
         # easier tests (in normal case we always use the default)
         self._tsig_ctx_creator = lambda key : TSIGContext(key)
 
+        # keep a record of this specific transfer to log on success
+        # (time, rr/s, etc)
+        self._transfer_stats = XfrinTransferStats()
+
     def init_socket(self):
         '''Initialize the underlyig socket.
 
@@ -599,6 +664,11 @@ class XfrinConnection(asyncore.dispatcher):
     def get_xfrstate(self):
         return self.__state
 
+    def get_transfer_stats(self):
+        """Returns the transfer stats object, used to measure transfer time,
+           and number of messages/records/bytes transfered."""
+        return self._transfer_stats
+
     def zone_str(self):
         '''A convenience function for logging to include zone name and class'''
         return format_zone_str(self._zone_name, self._rrclass)
@@ -823,7 +893,29 @@ class XfrinConnection(asyncore.dispatcher):
             self._send_query(self._request_type)
             self.__state = XfrinInitialSOA()
             self._handle_xfrin_responses()
-            logger.info(XFRIN_XFR_TRANSFER_SUCCESS, req_str, self.zone_str())
+            # Depending what data was found, we log different status reports
+            # (In case of an AXFR-style IXFR, print the 'AXFR' message)
+            if self._transfer_stats.axfr_rr_count == 0:
+                logger.info(XFRIN_IXFR_TRANSFER_SUCCESS,
+                            self.zone_str(),
+                            self._transfer_stats.message_count,
+                            self._transfer_stats.ixfr_changeset_count,
+                            self._transfer_stats.ixfr_deletion_count,
+                            self._transfer_stats.ixfr_addition_count,
+                            self._transfer_stats.byte_count,
+                            "%.3f" % self._transfer_stats.get_running_time(),
+                            "%.f" % self._transfer_stats.get_bytes_per_second()
+                           )
+            else:
+                logger.info(XFRIN_TRANSFER_SUCCESS,
+                            req_str,
+                            self.zone_str(),
+                            self._transfer_stats.message_count,
+                            self._transfer_stats.axfr_rr_count,
+                            self._transfer_stats.byte_count,
+                            "%.3f" % self._transfer_stats.get_running_time(),
+                            "%.f" % self._transfer_stats.get_bytes_per_second()
+                           )
 
         except XfrinZoneUptodate:
             # Eventually we'll probably have to treat this case as a trigger
@@ -895,9 +987,11 @@ class XfrinConnection(asyncore.dispatcher):
         while read_next_msg:
             data_len = self._get_request_response(2)
             msg_len = socket.htons(struct.unpack('H', data_len)[0])
+            self._transfer_stats.byte_count += msg_len + 2
             recvdata = self._get_request_response(msg_len)
             msg = Message(Message.PARSE)
             msg.from_wire(recvdata, Message.PRESERVE_ORDER)
+            self._transfer_stats.message_count += 1
 
             # TSIG related checks, including an unexpected signed response
             self._check_response_tsig(msg, recvdata)
@@ -1156,6 +1250,11 @@ class Xfrin:
     def __init__(self):
         self._max_transfers_in = 10
         self._zones = {}
+        # This is a set of (zone/class) tuples (both as strings),
+        # representing the in-memory zones maintaned by Xfrin. It
+        # is used to trigger Auth/in-memory so that it reloads
+        # zones when they have been transfered in
+        self._memory_zones = set()
         self._cc_setup()
         self.recorder = XfrinRecorder()
         self._shutdown_event = threading.Event()
@@ -1174,6 +1273,8 @@ class Xfrin:
         self._module_cc.start()
         config_data = self._module_cc.get_full_config()
         self.config_handler(config_data)
+        self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION,
+                                          self._auth_config_handler)
 
     def _cc_check_command(self):
         '''This is a straightforward wrapper for cc.check_command,
@@ -1220,10 +1321,78 @@ class Xfrin:
 
         return create_answer(0)
 
+    def _auth_config_handler(self, new_config, config_data):
+        # Config handler for changes in Auth configuration
+        self._set_db_file()
+        self._set_memory_zones(new_config, config_data)
+
+    def _clear_memory_zones(self):
+        """Clears the memory_zones set; called before processing the
+           changed list of memory datasource zones that have file type
+           sqlite3"""
+        self._memory_zones.clear()
+
+    def _is_memory_zone(self, zone_name_str, zone_class_str):
+        """Returns true if the given zone/class combination is configured
+           in the in-memory datasource of the Auth process with file type
+           'sqlite3'.
+           Note: this method is not thread-safe. We are considering
+           changing the threaded model here, but if we do not, take
+           care in accessing and updating the memory zone set (or add
+           locks)
+        """
+        # Normalize them first, if either conversion fails, return false
+        # (they won't be in the set anyway)
+        try:
+            zone_name_str = Name(zone_name_str).to_text().lower()
+            zone_class_str = RRClass(zone_class_str).to_text()
+        except Exception:
+            return False
+        return (zone_name_str, zone_class_str) in self._memory_zones
+
+    def _set_memory_zones(self, new_config, config_data):
+        """Part of the _auth_config_handler function, keeps an internal set
+           of zones in the datasources config subset that have 'sqlite3' as
+           their file type.
+           Note: this method is not thread-safe. We are considering
+           changing the threaded model here, but if we do not, take
+           care in accessing and updating the memory zone set (or add
+           locks)
+        """
+        # walk through the data and collect the memory zones
+        # If this causes any exception, assume we were passed bad data
+        # and keep the original set
+        new_memory_zones = set()
+        try:
+            if "datasources" in new_config:
+                for datasource in new_config["datasources"]:
+                    if "class" in datasource:
+                        ds_class = RRClass(datasource["class"])
+                    else:
+                        # Get the default
+                        ds_class = RRClass(config_data.get_default_value(
+                                               "datasources/class"))
+                    if datasource["type"] == "memory":
+                        for zone in datasource["zones"]:
+                            if "filetype" in zone and \
+                               zone["filetype"] == "sqlite3":
+                                zone_name = Name(zone["origin"])
+                                zone_name_str = zone_name.to_text().lower()
+                                new_memory_zones.add((zone_name_str,
+                                                      ds_class.to_text()))
+                # Ok, we can use the data, update our list
+                self._memory_zones = new_memory_zones
+        except Exception:
+            # Something is wrong with the data. If this data even reached us,
+            # we cannot do more than assume the real module has logged and
+            # reported an error. Keep the old set.
+            return
+
     def shutdown(self):
         ''' shutdown the xfrin process. the thread which is doing xfrin should be
         terminated.
         '''
+        self._module_cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
         self._module_cc.send_stopping()
         self._shutdown_event.set()
         main_thread = threading.currentThread()
@@ -1356,20 +1525,19 @@ class Xfrin:
         return (addr.family, socket.SOCK_STREAM, (str(addr), port))
 
     def _get_db_file(self):
-        #TODO, the db file path should be got in auth server's configuration
-        # if we need access to this configuration more often, we
-        # should add it on start, and not remove it here
-        # (or, if we have writable ds, we might not need this in
-        # the first place)
-        self._module_cc.add_remote_config(AUTH_SPECFILE_LOCATION)
-        db_file, is_default = self._module_cc.get_remote_config_value("Auth", "database_file")
+        return self._db_file
+
+    def _set_db_file(self):
+        db_file, is_default =\
+            self._module_cc.get_remote_config_value("Auth", "database_file")
         if is_default and "B10_FROM_BUILD" in os.environ:
-            # this too should be unnecessary, but currently the
-            # 'from build' override isn't stored in the config
-            # (and we don't have writable datasources yet)
-            db_file = os.environ["B10_FROM_BUILD"] + os.sep + "bind10_zones.sqlite3"
-        self._module_cc.remove_remote_config(AUTH_SPECFILE_LOCATION)
-        return db_file
+            # override the local database setting if it is default and we
+            # are running from the source tree
+            # This should be hidden inside the data source library and/or
+            # done as a configuration, and this special case should be gone).
+            db_file = os.environ["B10_FROM_BUILD"] + os.sep +\
+                      "bind10_zones.sqlite3"
+        self._db_file = db_file
 
     def publish_xfrin_news(self, zone_name, zone_class,  xfr_result):
         '''Send command to xfrout/zone manager module.
@@ -1412,6 +1580,7 @@ class Xfrin:
                 logger.error(XFRIN_MSGQ_SEND_ERROR_ZONE_MANAGER, ZONE_MANAGER_MODULE_NAME)
 
     def startup(self):
+        logger.debug(DBG_PROCESS, XFRIN_STARTED)
         while not self._shutdown_event.is_set():
             self._cc_check_command()
 
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 5e182d8..25a1fc1 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -15,92 +15,21 @@
 # No namespace declaration - these constants go in the global namespace
 # of the xfrin messages python module.
 
-% XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created
-On starting an xfrin session, it is identified that the zone to be
-transferred is not found in the data source.  This can happen if a
-secondary DNS server first tries to perform AXFR from a primary server
-without creating the zone image beforehand (e.g. by b10-loadzone).  As
-of this writing the xfrin process provides backward compatible
-behavior to previous versions: creating a new one in the data source
-not to surprise existing users too much.  This is probably not a good
-idea, however, in terms of who should be responsible for managing
-zones at a higher level.  In future it is more likely that a separate
-zone management framework is provided, and the situation where the
-given zone isn't found in xfrout will be treated as an error.
-
-% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
-On starting an xfrin session, it is identified that the zone to be
-transferred does not have an SOA RR in the data source.  This is not
-necessarily an error; if a secondary DNS server first tries to perform
-transfer from a primary server, the zone can be empty, and therefore
-doesn't have an SOA.  Subsequent AXFR will fill in the zone; if the
-attempt is IXFR it will fail in query creation.
-
-% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
-On starting an xfrin session, it is identified that the zone to be
-transferred has multiple SOA RRs.  Such a zone is broken, but could be
-accidentally configured especially in a data source using "non
-captive" backend database.  The implementation ignores entire SOA RRs
-and tries to continue processing as if the zone were empty.  This
-means subsequent AXFR can succeed and possibly replace the zone with
-valid content, but an IXFR attempt will fail.
-
-% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
-The response to an SOA query prior to xfr indicated that the zone's
-SOA serial at the primary server is smaller than that of the xfrin
-client.  This is not necessarily an error especially if that
-particular primary server is another secondary server which hasn't got
-the latest version of the zone.  But if the primary server is known to
-be the real source of the zone, some unexpected inconsistency may have
-happened, and you may want to take a closer look.  In this case xfrin
-doesn't perform subsequent zone transfer.
-
-% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
-The XFR transfer for the given zone has failed due to a problem outside
-of the xfrin module.  Possible reasons are a broken DNS message or failure
-in database connection.  The error is shown in the log message.
-
-% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
-The XFR transfer for the given zone has failed due to a protocol
-error, such as an unexpected response from the primary server.  The
-error is shown in the log message.  It may be because the primary
-server implementation is broken or (although less likely) there was
-some attack attempt, but it can also happen due to configuration
-mismatch such as the remote server does not have authority for the
-zone any more but the local configuration hasn't been updated.  So it
-is recommended to check the primary server configuration.
-
-% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
-The XFR transfer for the given zone has failed due to an internal error.
-The error is shown in the log message.
-
-% XFRIN_XFR_TRANSFER_FALLBACK falling back from IXFR to AXFR for %1
-The IXFR transfer of the given zone failed. This might happen in many cases,
-such that the remote server doesn't support IXFR, we don't have the SOA record
-(or the zone at all), we are out of sync, etc. In many of these situations,
-AXFR could still work. Therefore we try that one in case it helps.
-
-% XFRIN_XFR_PROCESS_FAILURE %1 transfer of zone %2/%3 failed: %4
-An XFR session failed outside the main protocol handling.  This
-includes an error at the data source level at the initialization
-phase, unexpected failure in the network connection setup to the
-master server, or even more unexpected failure due to unlikely events
-such as memory allocation failure.  Details of the error are shown in
-the log message.  In general, these errors are not really expected
-ones, and indicate an installation error or a program bug.  The
-session handler thread tries to clean up all intermediate resources
-even on these errors, but it may be incomplete.  So, if this log
-message continuously appears, system resource consumption should be
-checked, and you may even want to disable the corresponding transfers.
-You may also want to file a bug report if this message appears so
-often.
-
-% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
-A connection to the master server has been made, the serial value in
-the SOA record has been checked, and a zone transfer has been started.
-
-% XFRIN_XFR_TRANSFER_SUCCESS %1 transfer of zone %2 succeeded
-The XFR transfer of the given zone was successfully completed.
+% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
+The serial fields of the first and last SOAs of AXFR (including AXFR-style
+IXFR) are not the same.  According to RFC 5936 these two SOAs must be the
+"same" (not only for the serial), but it is still not clear what the
+receiver should do if this condition does not hold.  There was a discussion
+about this at the IETF dnsext wg:
+http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
+and the general feeling seems that it would be better to reject the
+transfer if a mismatch is detected.  On the other hand, also as noted
+in that email thread, neither BIND 9 nor NSD performs any comparison
+on the SOAs.  For now, we only check the serials (ignoring other fields)
+and only leave a warning log message when a mismatch is found.  If it
+turns out to happen with a real world primary server implementation
+and that server actually feeds broken data (e.g. mixed versions of
+zone), we can consider a stricter action.
 
 % XFRIN_BAD_MASTER_ADDR_FORMAT bad format for master address: %1
 The given master address is not a valid IP address.
@@ -127,6 +56,58 @@ error is given in the log message.
 There was an error opening a connection to the master. The error is
 shown in the log message.
 
+% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
+In an attempt of IXFR processing, the begenning SOA of the first difference
+(following the initial SOA that specified the final SOA for all the
+differences) was found.  This means a connection for xfrin tried IXFR
+and really aot a response for incremental updates.
+
+% XFRIN_GOT_NONINCREMENTAL_RESP got nonincremental response for %1
+Non incremental transfer was detected at the "first data" of a transfer,
+which is the RR following the initial SOA.  Non incremental transfer is
+either AXFR or AXFR-style IXFR.  In the latter case, it means that
+in a response to IXFR query the first data is not SOA or its SOA serial
+is not equal to the requested SOA serial.
+
+% XFRIN_IMPORT_DNS error importing python DNS module: %1
+There was an error importing the python DNS module pydnspp. The most
+likely cause is a PYTHONPATH problem.
+
+% XFRIN_IXFR_TRANSFER_SUCCESS incremental IXFR transfer of zone %1 succeeded (messages: %2, changesets: %3, deletions: %4, additions: %5, bytes: %6, run time: %7 seconds, %8 bytes/second)
+The IXFR transfer for the given zone was successful.
+The provided information contains the following values:
+
+messages: Number of overhead DNS messages in the transfer.
+
+changesets: Number of difference sequences.
+
+deletions: Number of Resource Records deleted by all the changesets combined,
+including the SOA records.
+
+additions: Number of Resource Records added by all the changesets combined,
+including the SOA records.
+
+bytes: Full size of the transfer data on the wire.
+
+run time: Time (in seconds) the complete ixfr took.
+
+bytes/second: Transfer speed.
+
+Note that there is no cross-checking of additions and deletions; if the same
+RR gets added and deleted in multiple changesets, it is counted each time;
+therefore, for each changeset, there should at least be 1 deletion and 1
+addition (the updated SOA record).
+
+% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
+The first SOA record in an IXFR response indicates the zone's serial
+at the primary server is not newer than the client's.  This is
+basically unexpected event because normally the client first checks
+the SOA serial by an SOA query, but can still happen if the transfer
+is manually invoked or (although unlikely) there is a rapid change at
+the primary server between the SOA and IXFR queries.  The client
+implementation confirms the whole response is this single SOA, and
+aborts the transfer just like a successful case.
+
 % XFRIN_MSGQ_SEND_ERROR error while contacting %1 and %2
 There was a problem sending a message to the xfrout module or the
 zone manager. This most likely means that the msgq daemon has quit or
@@ -142,62 +123,119 @@ from does not match the master address in the Xfrin configuration. The notify
 is ignored. This may indicate that the configuration for the master is wrong,
 that a wrong machine is sending notifies, or that fake notifies are being sent.
 
-% XFRIN_IMPORT_DNS error importing python DNS module: %1
-There was an error importing the python DNS module pydnspp. The most
-likely cause is a PYTHONPATH problem.
-
 % XFRIN_RETRANSFER_UNKNOWN_ZONE got notification to retransfer unknown zone %1
 There was an internal command to retransfer the given zone, but the
 zone is not known to the system. This may indicate that the configuration
 for xfrin is incomplete, or there was a typographical error in the
 zone name in the configuration.
 
-% XFRIN_STARTING starting resolver with command line '%1'
-An informational message, this is output when the resolver starts up.
+% XFRIN_STARTED xfrin started
+This informational message is output by xfrin when all initialization
+has been completed and it is entering its main loop.
 
 % XFRIN_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the xfrin daemon. The
 daemon will now shut down.
 
+% XFRIN_TRANSFER_SUCCESS full %1 transfer of zone %2 succeeded (messages: %3, records: %4, bytes: %5, run time: %6 seconds, %7 bytes/second)
+The AXFR transfer of the given zone was successful.
+The provided information contains the following values:
+
+messages: Number of overhead DNS messages in the transfer
+
+records: Number of Resource Records in the full transfer, excluding the
+final SOA record that marks the end of the AXFR.
+
+bytes: Full size of the transfer data on the wire.
+
+run time: Time (in seconds) the complete axfr took
+
+bytes/second: Transfer speed
+
 % XFRIN_UNKNOWN_ERROR unknown error: %1
 An uncaught exception was raised while running the xfrin daemon. The
 exception message is printed in the log message.
 
-% XFRIN_IXFR_UPTODATE IXFR requested serial for %1 is %2, master has %3, not updating
-The first SOA record in an IXFR response indicates the zone's serial
-at the primary server is not newer than the client's.  This is
-basically unexpected event because normally the client first checks
-the SOA serial by an SOA query, but can still happen if the transfer
-is manually invoked or (although unlikely) there is a rapid change at
-the primary server between the SOA and IXFR queries.  The client
-implementation confirms the whole response is this single SOA, and
-aborts the transfer just like a successful case.
+% XFRIN_XFR_OTHER_FAILURE %1 transfer of zone %2 failed: %3
+The XFR transfer for the given zone has failed due to a problem outside
+of the xfrin module.  Possible reasons are a broken DNS message or failure
+in database connection.  The error is shown in the log message.
 
-% XFRIN_GOT_INCREMENTAL_RESP got incremental response for %1
-In an attempt of IXFR processing, the begenning SOA of the first difference
-(following the initial SOA that specified the final SOA for all the
-differences) was found.  This means a connection for xfrin tried IXFR
-and really aot a response for incremental updates.
+% XFRIN_XFR_PROCESS_FAILURE %1 transfer of zone %2/%3 failed: %4
+An XFR session failed outside the main protocol handling.  This
+includes an error at the data source level at the initialization
+phase, unexpected failure in the network connection setup to the
+master server, or even more unexpected failure due to unlikely events
+such as memory allocation failure.  Details of the error are shown in
+the log message.  In general, these errors are not really expected
+ones, and indicate an installation error or a program bug.  The
+session handler thread tries to clean up all intermediate resources
+even on these errors, but it may be incomplete.  So, if this log
+message continuously appears, system resource consumption should be
+checked, and you may even want to disable the corresponding transfers.
+You may also want to file a bug report if this message appears so
+often.
 
-% XFRIN_GOT_NONINCREMENTAL_RESP got nonincremental response for %1
-Non incremental transfer was detected at the "first data" of a transfer,
-which is the RR following the initial SOA.  Non incremental transfer is
-either AXFR or AXFR-style IXFR.  In the latter case, it means that
-in a response to IXFR query the first data is not SOA or its SOA serial
-is not equal to the requested SOA serial.
+% XFRIN_XFR_TRANSFER_FAILURE %1 transfer of zone %2 with %3 failed: %4
+The XFR transfer for the given zone has failed due to an internal error.
+The error is shown in the log message.
 
-% XFRIN_AXFR_INCONSISTENT_SOA AXFR SOAs are inconsistent for %1: %2 expected, %3 received
-The serial fields of the first and last SOAs of AXFR (including AXFR-style
-IXFR) are not the same.  According to RFC 5936 these two SOAs must be the
-"same" (not only for the serial), but it is still not clear what the
-receiver should do if this condition does not hold.  There was a discussion
-about this at the IETF dnsext wg:
-http://www.ietf.org/mail-archive/web/dnsext/current/msg07908.html
-and the general feeling seems that it would be better to reject the
-transfer if a mismatch is detected.  On the other hand, also as noted
-in that email thread, neither BIND 9 nor NSD performs any comparison
-on the SOAs.  For now, we only check the serials (ignoring other fields)
-and only leave a warning log message when a mismatch is found.  If it
-turns out to happen with a real world primary server implementation
-and that server actually feeds broken data (e.g. mixed versions of
-zone), we can consider a stricter action.
+% XFRIN_XFR_TRANSFER_FALLBACK falling back from IXFR to AXFR for %1
+The IXFR transfer of the given zone failed. This might happen in many cases,
+such that the remote server doesn't support IXFR, we don't have the SOA record
+(or the zone at all), we are out of sync, etc. In many of these situations,
+AXFR could still work. Therefore we try that one in case it helps.
+
+% XFRIN_XFR_TRANSFER_PROTOCOL_ERROR %1 transfer of zone %2 with %3 failed: %4
+The XFR transfer for the given zone has failed due to a protocol
+error, such as an unexpected response from the primary server.  The
+error is shown in the log message.  It may be because the primary
+server implementation is broken or (although less likely) there was
+some attack attempt, but it can also happen due to configuration
+mismatch such as the remote server does not have authority for the
+zone any more but the local configuration hasn't been updated.  So it
+is recommended to check the primary server configuration.
+
+% XFRIN_XFR_TRANSFER_STARTED %1 transfer of zone %2 started
+A connection to the master server has been made, the serial value in
+the SOA record has been checked, and a zone transfer has been started.
+
+% XFRIN_ZONE_CREATED Zone %1 not found in the given data source, newly created
+On starting an xfrin session, it is identified that the zone to be
+transferred is not found in the data source.  This can happen if a
+secondary DNS server first tries to perform AXFR from a primary server
+without creating the zone image beforehand (e.g. by b10-loadzone).  As
+of this writing the xfrin process provides backward compatible
+behavior to previous versions: creating a new one in the data source
+not to surprise existing users too much.  This is probably not a good
+idea, however, in terms of who should be responsible for managing
+zones at a higher level.  In future it is more likely that a separate
+zone management framework is provided, and the situation where the
+given zone isn't found in xfrout will be treated as an error.
+
+% XFRIN_ZONE_MULTIPLE_SOA Zone %1 has %2 SOA RRs
+On starting an xfrin session, it is identified that the zone to be
+transferred has multiple SOA RRs.  Such a zone is broken, but could be
+accidentally configured especially in a data source using "non
+captive" backend database.  The implementation ignores entire SOA RRs
+and tries to continue processing as if the zone were empty.  This
+means subsequent AXFR can succeed and possibly replace the zone with
+valid content, but an IXFR attempt will fail.
+
+% XFRIN_ZONE_NO_SOA Zone %1 does not have SOA
+On starting an xfrin session, it is identified that the zone to be
+transferred does not have an SOA RR in the data source.  This is not
+necessarily an error; if a secondary DNS server first tries to perform
+transfer from a primary server, the zone can be empty, and therefore
+doesn't have an SOA.  Subsequent AXFR will fill in the zone; if the
+attempt is IXFR it will fail in query creation.
+
+% XFRIN_ZONE_SERIAL_AHEAD Serial number (%1) for %2 received from master %3 < ours (%4)
+The response to an SOA query prior to xfr indicated that the zone's
+SOA serial at the primary server is smaller than that of the xfrin
+client.  This is not necessarily an error especially if that
+particular primary server is another secondary server which hasn't got
+the latest version of the zone.  But if the primary server is known to
+be the real source of the zone, some unexpected inconsistency may have
+happened, and you may want to take a closer look.  In this case xfrin
+doesn't perform subsequent zone transfer.
diff --git a/src/bin/xfrout/.gitignore b/src/bin/xfrout/.gitignore
new file mode 100644
index 0000000..2ace679
--- /dev/null
+++ b/src/bin/xfrout/.gitignore
@@ -0,0 +1,5 @@
+/b10-xfrout
+/run_b10-xfrout.sh
+/xfrout.py
+/xfrout.spec
+/xfrout.spec.pre
diff --git a/src/bin/xfrout/b10-xfrout.8 b/src/bin/xfrout/b10-xfrout.8
index c37198c..b3200b5 100644
--- a/src/bin/xfrout/b10-xfrout.8
+++ b/src/bin/xfrout/b10-xfrout.8
@@ -2,12 +2,21 @@
 .\"     Title: b10-xfrout
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 15, 2011
+.\"      Date: March 16. 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-XFROUT" "8" "December 15, 2011" "BIND10" "BIND10"
+.TH "B10\-XFROUT" "8" "March 16\&. 2012" "BIND10" "BIND10"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -70,11 +79,6 @@ The configurable settings are:
 defines the maximum number of outgoing zone transfers that can run concurrently\&. The default is 10\&.
 .PP
 
-\fItsig_key_ring\fR
-A list of TSIG keys (each of which is in the form of
-\fIname:base64\-key[:algorithm]\fR) used for access control on transfer requests\&. The default is an empty list\&.
-.PP
-
 \fItransfer_acl\fR
 A list of ACL elements that apply to all transfer requests by default (unless overridden in
 \fIzone_config\fR)\&. See the
@@ -87,26 +91,6 @@ A list of JSON objects (i\&.e\&. maps) that define per zone configuration concer
 \fBb10\-xfrout\fR\&. The supported names of each object are "origin" (the origin name of the zone), "class" (the RR class of the zone, optional, default to "IN"), and "transfer_acl" (ACL only applicable to transfer requests for that zone)\&. See the
 BIND 10 Guide
 for configuration examples\&. The default is an empty list, that is, no zone specific configuration\&.
-.PP
-
-\fIlog_name\fR
-.PP
-
-\fIlog_file\fR
-The location of the log file if using a file channel\&. If undefined, then the file channel is closed\&. The default is
-/usr/local/var/bind10\-devel/log/Xfrout\&.log\&.
-.PP
-
-\fIlog_severity\fR
-The default is "debug"\&.
-.PP
-
-\fIlog_versions\fR
-The default is 5\&.
-.PP
-
-\fIlog_max_bytes\fR
-The default is 1048576\&.
 .if n \{\
 .sp
 .\}
@@ -127,17 +111,24 @@ This prototype version uses SQLite3 as its data source backend\&. Future version
 The configuration commands are:
 .PP
 
-\fBshutdown\fR
-stops all outbound zone transfers and exits
-\fBb10\-xfrout\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBnotify\fR
+triggers
+\fBb10\-xfrout\fR
+to send NOTIFY message(s)\&. It has the following arguments:
+\fIzone_name\fR
+to define the zone to send notifies for and the optional
+\fIzone_class\fR
+to define the class (defaults to
+\(lqIN\(rq)\&.
+\fBb10-xfrin\fR(8)
+sends this command when a zone transferred in successfully\&.
 .PP
 
-\fBzone_new_data_ready\fR
-is sent from
-\fBb10-xfrin\fR(8)
-to indicate that the zone transferred in successfully\&. This triggers
-\fBb10\-xfrout\fR
-to send NOTIFY message(s)\&. This is an internal command and not exposed to the administrator\&.
+\fBshutdown\fR
+stops all outbound zone transfers and exits
+\fBb10\-xfrout\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .SH "SEE ALSO"
 .PP
 
@@ -154,5 +145,5 @@ The
 daemon was first implemented in March 2010 by Zhang Likun of CNNIC for the ISC BIND 10 project\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/xfrout/b10-xfrout.xml b/src/bin/xfrout/b10-xfrout.xml
index 87d0267..f79a42d 100644
--- a/src/bin/xfrout/b10-xfrout.xml
+++ b/src/bin/xfrout/b10-xfrout.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 15, 2011</date>
+    <date>March 16. 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -98,13 +98,6 @@
       that can run concurrently. The default is 10.
     </para>
     <para>
-      <varname>tsig_key_ring</varname>
-      A list of TSIG keys (each of which is in the form of
-      <replaceable>name:base64-key[:algorithm]</replaceable>)
-      used for access control on transfer requests.
-      The default is an empty list.
-    </para>
-    <para>
       <varname>transfer_acl</varname>
       A list of ACL elements that apply to all transfer requests by
       default (unless overridden in <varname>zone_config</varname>).
@@ -122,33 +115,6 @@
       See the <citetitle>BIND 10 Guide</citetitle> for configuration examples.
       The default is an empty list, that is, no zone specific configuration.
     </para>
-    <para>
-      <varname>log_name</varname>
-<!-- TODO -->
-    </para>
-    <para>
-      <varname>log_file</varname>
-<!-- TODO -->
-      The location of the log file if using a file channel.
-      If undefined, then the file channel is closed.
-      The default is
-      <filename>/usr/local/var/bind10-devel/log/Xfrout.log</filename>.
-    </para>
-    <para>
-      <varname>log_severity</varname>
-<!-- TODO -->
-      The default is "debug".
-    </para>
-    <para>
-      <varname>log_versions</varname>
-<!-- TODO -->
-      The default is 5.
-    </para>
-    <para>
-      <varname>log_max_bytes</varname>
-<!-- TODO -->
-      The default is 1048576.
-    </para>
 
 <!-- TODO: log configurations not documented yet in here. jreed
      has some but waiting on decisions ... -->
@@ -160,31 +126,29 @@
     </simpara></note>
 
 
-<!--
-
-tsig_key_ring list of
-tsig_key string
-
--->
-
 <!-- TODO: formating -->
     <para>
       The configuration commands are:
     </para>
+
     <para>
-      <command>shutdown</command> stops all outbound zone transfers
-      and exits <command>b10-xfrout</command>. (Note that the BIND 10
-      boss process will restart this service.)
+      <command>notify</command> triggers <command>b10-xfrout</command>
+      to send NOTIFY message(s).
+      It has the following arguments: <varname>zone_name</varname>
+      to define the zone to send notifies for and the optional
+      <varname>zone_class</varname> to define the class (defaults to
+      <quote>IN</quote>).
+      <citerefentry><refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      sends this command when a zone transferred in successfully.
     </para>
 
     <para>
-      <command>zone_new_data_ready</command> is sent from
-      <citerefentry><refentrytitle>b10-xfrin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      to indicate that the zone transferred in successfully.
-      This triggers <command>b10-xfrout</command> to send NOTIFY
-      message(s).
-      This is an internal command and not exposed to the administrator.
-<!-- not defined in spec -->
+      <command>shutdown</command> stops all outbound zone transfers
+      and exits <command>b10-xfrout</command>.
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
     </para>
 
   </refsect1>
diff --git a/src/bin/xfrout/tests/.gitignore b/src/bin/xfrout/tests/.gitignore
new file mode 100644
index 0000000..92c94aa
--- /dev/null
+++ b/src/bin/xfrout/tests/.gitignore
@@ -0,0 +1,2 @@
+/xfrout_test
+/xfrout_test.py
diff --git a/src/bin/xfrout/tests/testdata/test.sqlite3 b/src/bin/xfrout/tests/testdata/test.sqlite3
index 9eb14f1..a594b44 100644
Binary files a/src/bin/xfrout/tests/testdata/test.sqlite3 and b/src/bin/xfrout/tests/testdata/test.sqlite3 differ
diff --git a/src/bin/xfrout/tests/xfrout_test.py.in b/src/bin/xfrout/tests/xfrout_test.py.in
index 3e953da..b60535c 100644
--- a/src/bin/xfrout/tests/xfrout_test.py.in
+++ b/src/bin/xfrout/tests/xfrout_test.py.in
@@ -28,6 +28,7 @@ from xfrout import *
 import xfrout
 import isc.log
 import isc.acl.dns
+import isc.server_common.tsig_keyring
 
 TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
 TSIG_KEY = TSIGKey("example.com:SFuWd/q99SzF8Yzd1QbB9g==")
@@ -1155,6 +1156,39 @@ class TestUnixSockServer(unittest.TestCase):
         self.write_sock, self.read_sock = socket.socketpair()
         self.unix = MyUnixSockServer()
 
+    def test_tsig_keyring(self):
+        """
+        Check we use the global keyring when starting a request.
+        """
+        try:
+            # These are just so the keyring can be started
+            self.unix._cc.add_remote_config_by_name = \
+                lambda name, callback: None
+            self.unix._cc.get_remote_config_value = \
+                lambda module, name: ([], True)
+            self.unix._cc.remove_remote_config = lambda name: None
+            isc.server_common.tsig_keyring.init_keyring(self.unix._cc)
+            # These are not really interesting for the test. These are just
+            # handled over, so strings are OK.
+            self.unix._guess_remote = lambda sock: "Address"
+            self.unix._zone_config = "Zone config"
+            self.unix._acl = "acl"
+            # This would be the handler class, but we just check it is passed
+            # the right parametes, so function is enough for that.
+            keys = isc.server_common.tsig_keyring.get_keyring()
+            def handler(sock, data, server, keyring, address, acl, config):
+                self.assertEqual("sock", sock)
+                self.assertEqual("data", data)
+                self.assertEqual(self.unix, server)
+                self.assertEqual(keys, keyring)
+                self.assertEqual("Address", address)
+                self.assertEqual("acl", acl)
+                self.assertEqual("Zone config", config)
+            self.unix.RequestHandlerClass = handler
+            self.unix.finish_request("sock", "data")
+        finally:
+            isc.server_common.tsig_keyring.deinit_keyring()
+
     def test_guess_remote(self):
         """Test we can guess the remote endpoint when we have only the
            file descriptor. This is needed, because we get only that one
@@ -1214,25 +1248,12 @@ class TestUnixSockServer(unittest.TestCase):
 
     def test_update_config_data(self):
         self.check_default_ACL()
-        tsig_key_str = 'example.com:SFuWd/q99SzF8Yzd1QbB9g=='
-        tsig_key_list = [tsig_key_str]
-        bad_key_list = ['bad..example.com:SFuWd/q99SzF8Yzd1QbB9g==']
         self.unix.update_config_data({'transfers_out':10 })
         self.assertEqual(self.unix._max_transfers_out, 10)
-        self.assertTrue(self.unix.tsig_key_ring is not None)
         self.check_default_ACL()
 
-        self.unix.update_config_data({'transfers_out':9,
-                                      'tsig_key_ring':tsig_key_list})
+        self.unix.update_config_data({'transfers_out':9})
         self.assertEqual(self.unix._max_transfers_out, 9)
-        self.assertEqual(self.unix.tsig_key_ring.size(), 1)
-        self.unix.tsig_key_ring.remove(Name("example.com."))
-        self.assertEqual(self.unix.tsig_key_ring.size(), 0)
-
-        # bad tsig key
-        config_data = {'transfers_out':9, 'tsig_key_ring': bad_key_list}
-        self.assertRaises(None, self.unix.update_config_data(config_data))
-        self.assertEqual(self.unix.tsig_key_ring.size(), 0)
 
         # Load the ACL
         self.unix.update_config_data({'transfer_acl': [{'from': '127.0.0.1',
@@ -1449,7 +1470,6 @@ class TestXfroutServer(unittest.TestCase):
         self.assertTrue(self.xfrout_server._notifier.shutdown_called)
         self.assertTrue(self.xfrout_server._cc.stopped)
 
-
 if __name__== "__main__":
     isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/xfrout/xfrout.py.in b/src/bin/xfrout/xfrout.py.in
index 5c82f19..4dd12ce 100755
--- a/src/bin/xfrout/xfrout.py.in
+++ b/src/bin/xfrout/xfrout.py.in
@@ -34,11 +34,18 @@ import select
 import errno
 from optparse import OptionParser, OptionValueError
 from isc.util import socketserver_mixin
+import isc.server_common.tsig_keyring
 
 from isc.log_messages.xfrout_messages import *
 
 isc.log.init("b10-xfrout")
 logger = isc.log.Logger("xfrout")
+
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
+DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
+
 DBG_XFROUT_TRACE = logger.DBGLVL_TRACE_BASIC
 
 try:
@@ -769,7 +776,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
         zone_config = self._zone_config
         self._lock.release()
         self.RequestHandlerClass(sock_fd, request_data, self,
-                                 self.tsig_key_ring,
+                                 isc.server_common.tsig_keyring.get_keyring(),
                                  self._guess_remote(sock_fd), acl, zone_config)
 
     def _remove_unused_sock_file(self, sock_file):
@@ -833,7 +840,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
             self._acl = new_acl
             self._zone_config = new_zone_config
             self._max_transfers_out = new_config.get('transfers_out')
-            self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
         except Exception as e:
             self._lock.release()
             raise e
@@ -870,21 +876,6 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn,
                                             zclass_str + ': ' + str(e))
         return new_config
 
-    def set_tsig_key_ring(self, key_list):
-        """Set the tsig_key_ring , given a TSIG key string list representation. """
-
-        # XXX add values to configure zones/tsig options
-        self.tsig_key_ring = TSIGKeyRing()
-        # If key string list is empty, create a empty tsig_key_ring
-        if not key_list:
-            return
-
-        for key_item in key_list:
-            try:
-                self.tsig_key_ring.add(TSIGKey(key_item))
-            except InvalidParameter as ipe:
-                logger.error(XFROUT_BAD_TSIG_KEY_STRING, str(key_item))
-
     def get_db_file(self):
         file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
         # this too should be unnecessary, but currently the
@@ -920,7 +911,8 @@ class XfroutServer:
         self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
         self._config_data = self._cc.get_full_config()
         self._cc.start()
-        self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
+        self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
+        isc.server_common.tsig_keyring.init_keyring(self._cc)
         self._start_xfr_query_listener()
         self._start_notifier()
 
@@ -940,7 +932,7 @@ class XfroutServer:
         self._notifier.dispatcher()
 
     def send_notify(self, zone_name, zone_class):
-        self._notifier.send_notify(zone_name, zone_class)
+        return self._notifier.send_notify(zone_name, zone_class)
 
     def config_handler(self, new_config):
         '''Update config data. TODO. Do error check'''
@@ -996,10 +988,16 @@ class XfroutServer:
         elif cmd == notify_out.ZONE_NEW_DATA_READY_CMD:
             zone_name = args.get('zone_name')
             zone_class = args.get('zone_class')
-            if zone_name and zone_class:
+            if not zone_class:
+                zone_class = str(RRClass.IN())
+            if zone_name:
                 logger.info(XFROUT_NOTIFY_COMMAND, zone_name, zone_class)
-                self.send_notify(zone_name, zone_class)
-                answer = create_answer(0)
+                if self.send_notify(zone_name, zone_class):
+                    answer = create_answer(0)
+                else:
+                    zonestr = notify_out.format_zone_str(Name(zone_name),
+                                                         zone_class)
+                    answer = create_answer(1, "Unknown zone: " + zonestr)
             else:
                 answer = create_answer(1, "Bad command parameter:" + str(args))
 
@@ -1010,6 +1008,7 @@ class XfroutServer:
 
     def run(self):
         '''Get and process all commands sent from cfgmgr or other modules. '''
+        logger.debug(DBG_PROCESS, XFROUT_STARTED)
         while not self._shutdown_event.is_set():
             self._cc.check_command(False)
 
diff --git a/src/bin/xfrout/xfrout.spec.pre.in b/src/bin/xfrout/xfrout.spec.pre.in
index 6a97dea..30c9d56 100644
--- a/src/bin/xfrout/xfrout.spec.pre.in
+++ b/src/bin/xfrout/xfrout.spec.pre.in
@@ -9,48 +9,6 @@
          "item_default": 10
        },
        {
-         "item_name": "log_name",
-         "item_type": "string",
-         "item_optional": false,
-         "item_default": "Xfrout"
-       },
-       {
-         "item_name": "log_file",
-         "item_type": "string",
-         "item_optional": false,
-         "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/log/Xfrout.log"
-       },
-       {
-         "item_name": "log_severity",
-         "item_type": "string",
-         "item_optional": false,
-         "item_default": "debug"
-       },
-       {
-         "item_name": "log_versions",
-         "item_type": "integer",
-         "item_optional": false,
-         "item_default": 5
-       },
-       {
-         "item_name": "log_max_bytes",
-         "item_type": "integer",
-         "item_optional": false,
-         "item_default": 1048576
-       },
-       {
-         "item_name": "tsig_key_ring",
-         "item_type": "list",
-         "item_optional": true,
-         "item_default": [],
-         "list_item_spec" :
-         {
-             "item_name": "tsig_key",
-             "item_type": "string",
-             "item_optional": true
-         }
-       },
-       {
          "item_name": "transfer_acl",
          "item_type": "list",
          "item_optional": false,
@@ -113,6 +71,19 @@
             "item_optional": true
           }
         ]
+        },
+        { "command_name": "notify",
+          "command_description": "Send notifies for zone",
+          "command_args": [
+          { "item_name": "zone_name",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": "" },
+          { "item_name": "zone_class",
+            "item_type": "string",
+            "item_optional": true,
+            "item_default": "IN"
+          } ]
         }
       ]
   }
diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes
index fcc2e59..9996a5a 100644
--- a/src/bin/xfrout/xfrout_messages.mes
+++ b/src/bin/xfrout/xfrout_messages.mes
@@ -133,6 +133,10 @@ be a result of rare local error such as memory allocation failure and
 shouldn't happen under normal conditions. The error is included in the
 log message.
 
+% XFROUT_STARTED xfrout started
+This informational message is output by xfrout when all initialization
+has been completed and it is entering its main loop.
+
 % XFROUT_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the xfrout daemon. The
 daemon will now shut down.
diff --git a/src/bin/zonemgr/.gitignore b/src/bin/zonemgr/.gitignore
new file mode 100644
index 0000000..2d64f2d
--- /dev/null
+++ b/src/bin/zonemgr/.gitignore
@@ -0,0 +1,5 @@
+/b10-zonemgr
+/run_b10-zonemgr.sh
+/zonemgr.py
+/zonemgr.spec
+/zonemgr.spec.pre
diff --git a/src/bin/zonemgr/b10-zonemgr.8 b/src/bin/zonemgr/b10-zonemgr.8
index 1d24bbf..1bce2af 100644
--- a/src/bin/zonemgr/b10-zonemgr.8
+++ b/src/bin/zonemgr/b10-zonemgr.8
@@ -2,12 +2,12 @@
 .\"     Title: b10-zonemgr
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
-.\"      Date: December 8, 2011
+.\"      Date: February 28, 2012
 .\"    Manual: BIND10
 .\"    Source: BIND10
 .\"  Language: English
 .\"
-.TH "B10\-ZONEMGR" "8" "December 8, 2011" "BIND10" "BIND10"
+.TH "B10\-ZONEMGR" "8" "February 28, 2012" "BIND10" "BIND10"
 .\" -----------------------------------------------------------------
 .\" * set default formatting
 .\" -----------------------------------------------------------------
@@ -98,7 +98,9 @@ This is an internal command and not exposed to the administrator\&.
 
 \fBshutdown\fR
 exits
-\fBb10\-zonemgr\fR\&. (Note that the BIND 10 boss process will restart this service\&.)
+\fBb10\-zonemgr\fR\&. This has an optional
+\fIpid\fR
+argument to select the process ID to stop\&. (Note that the BIND 10 boss process may restart this service if configured\&.)
 .PP
 
 \fBzone_new_data_ready\fR
@@ -128,5 +130,5 @@ The
 daemon was designed in July 2010 by CNNIC for the ISC BIND 10 project\&.
 .SH "COPYRIGHT"
 .br
-Copyright \(co 2010-2011 Internet Systems Consortium, Inc. ("ISC")
+Copyright \(co 2010-2012 Internet Systems Consortium, Inc. ("ISC")
 .br
diff --git a/src/bin/zonemgr/b10-zonemgr.xml b/src/bin/zonemgr/b10-zonemgr.xml
index 5ea6041..f859d23 100644
--- a/src/bin/zonemgr/b10-zonemgr.xml
+++ b/src/bin/zonemgr/b10-zonemgr.xml
@@ -2,7 +2,7 @@
                "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
 	       [<!ENTITY mdash "—">]>
 <!--
- - Copyright (C) 2010-2011  Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2010-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
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>December 8, 2011</date>
+    <date>February 28, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010-2011</year>
+      <year>2010-2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -186,7 +186,10 @@
 
     <para>
       <command>shutdown</command> exits <command>b10-zonemgr</command>.
-      (Note that the BIND 10 boss process will restart this service.)
+      This has an optional <varname>pid</varname> argument to
+      select the process ID to stop.
+      (Note that the BIND 10 boss process may restart this service
+      if configured.)
     </para>
 
     <para>
diff --git a/src/bin/zonemgr/tests/.gitignore b/src/bin/zonemgr/tests/.gitignore
new file mode 100644
index 0000000..d3a63a3
--- /dev/null
+++ b/src/bin/zonemgr/tests/.gitignore
@@ -0,0 +1,2 @@
+/initdb.file
+/zonemgr_test
diff --git a/src/bin/zonemgr/tests/Makefile.am b/src/bin/zonemgr/tests/Makefile.am
index 8c6b904..b60fae7 100644
--- a/src/bin/zonemgr/tests/Makefile.am
+++ b/src/bin/zonemgr/tests/Makefile.am
@@ -20,6 +20,7 @@ endif
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	$(LIBRARY_PATH_PLACEHOLDER) \
+	TESTDATAOBJDIR=$(abs_top_builddir)/src/bin/zonemgr/tests/ \
 	B10_FROM_BUILD=$(abs_top_builddir) \
 	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/zonemgr:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/xfr/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
diff --git a/src/bin/zonemgr/tests/zonemgr_test.py b/src/bin/zonemgr/tests/zonemgr_test.py
index 29924c8..548d921 100644
--- a/src/bin/zonemgr/tests/zonemgr_test.py
+++ b/src/bin/zonemgr/tests/zonemgr_test.py
@@ -35,6 +35,8 @@ LOWERBOUND_RETRY = 5
 REFRESH_JITTER = 0.10
 RELOAD_JITTER = 0.75
 
+TEST_SQLITE3_DBFILE = os.getenv("TESTDATAOBJDIR") + '/initdb.file'
+
 class ZonemgrTestException(Exception):
     pass
 
@@ -57,7 +59,7 @@ class FakeCCSession(isc.config.ConfigData, MockModuleCCSession):
 
     def get_remote_config_value(self, module_name, identifier):
         if module_name == "Auth" and identifier == "database_file":
-            return "initdb.file", False
+            return TEST_SQLITE3_DBFILE, False
         else:
             return "unknown", False
 
@@ -81,7 +83,7 @@ class MyZonemgrRefresh(ZonemgrRefresh):
                 return None
         sqlite3_ds.get_zone_soa = get_zone_soa
 
-        ZonemgrRefresh.__init__(self, MySession(), "initdb.file",
+        ZonemgrRefresh.__init__(self, MySession(), TEST_SQLITE3_DBFILE,
                                 self._slave_socket, FakeCCSession())
         current_time = time.time()
         self._zonemgr_refresh_info = {
@@ -99,11 +101,18 @@ class MyZonemgrRefresh(ZonemgrRefresh):
 
 class TestZonemgrRefresh(unittest.TestCase):
     def setUp(self):
+        if os.path.exists(TEST_SQLITE3_DBFILE):
+            os.unlink(TEST_SQLITE3_DBFILE)
         self.stderr_backup = sys.stderr
         sys.stderr = open(os.devnull, 'w')
         self.zone_refresh = MyZonemgrRefresh()
         self.cc_session = FakeCCSession()
 
+    def tearDown(self):
+        if os.path.exists(TEST_SQLITE3_DBFILE):
+            os.unlink(TEST_SQLITE3_DBFILE)
+        sys.stderr = self.stderr_backup
+
     def test_random_jitter(self):
         max = 100025.120
         jitter = 0
@@ -602,13 +611,10 @@ class TestZonemgrRefresh(unittest.TestCase):
                           self.zone_refresh.update_config_data,
                           config, self.cc_session)
 
-    def tearDown(self):
-        sys.stderr= self.stderr_backup
-
 class MyZonemgr(Zonemgr):
 
     def __init__(self):
-        self._db_file = "initdb.file"
+        self._db_file = TEST_SQLITE3_DBFILE
         self._zone_refresh = None
         self._shutdown_event = threading.Event()
         self._cc = MySession()
@@ -628,8 +634,14 @@ class MyZonemgr(Zonemgr):
 class TestZonemgr(unittest.TestCase):
 
     def setUp(self):
+        if os.path.exists(TEST_SQLITE3_DBFILE):
+            os.unlink(TEST_SQLITE3_DBFILE)
         self.zonemgr = MyZonemgr()
 
+    def tearDown(self):
+        if os.path.exists(TEST_SQLITE3_DBFILE):
+            os.unlink(TEST_SQLITE3_DBFILE)
+
     def test_config_handler(self):
         config_data1 = {
                     "lowerbound_refresh" : 60,
@@ -650,8 +662,8 @@ class TestZonemgr(unittest.TestCase):
         self.zonemgr.config_handler(config_data3)
         self.assertEqual(0.5, self.zonemgr._config_data.get("refresh_jitter"))
         # The zone doesn't exist in database, simply skip loading soa for it and log an warning
-        self.zonemgr._zone_refresh = ZonemgrRefresh(None, "initdb.file", None,
-                                                    FakeCCSession())
+        self.zonemgr._zone_refresh = ZonemgrRefresh(None, TEST_SQLITE3_DBFILE,
+                                                    None, FakeCCSession())
         config_data1["secondary_zones"] = [{"name": "nonexistent.example",
                                             "class": "IN"}]
         self.assertEqual(self.zonemgr.config_handler(config_data1),
@@ -663,7 +675,7 @@ class TestZonemgr(unittest.TestCase):
         self.assertEqual(0.1, self.zonemgr._config_data.get("refresh_jitter"))
 
     def test_get_db_file(self):
-        self.assertEqual("initdb.file", self.zonemgr.get_db_file())
+        self.assertEqual(TEST_SQLITE3_DBFILE, self.zonemgr.get_db_file())
 
     def test_parse_cmd_params(self):
         params1 = {"zone_name" : "example.com.", "zone_class" : "CH", "master" : "127.0.0.1"}
@@ -691,9 +703,6 @@ class TestZonemgr(unittest.TestCase):
         self.zonemgr.run()
         self.assertTrue(self.zonemgr._module_cc.stopped)
 
-    def tearDown(self):
-        pass
-
 if __name__== "__main__":
     isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 7b16f1b..87589a8 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -44,6 +44,11 @@ from isc.log_messages.zonemgr_messages import *
 isc.log.init("b10-zonemgr")
 logger = isc.log.Logger("zonemgr")
 
+# Pending system-wide debug level definitions, the ones we
+# use here are hardcoded for now
+DBG_PROCESS = logger.DBGLVL_TRACE_BASIC
+DBG_COMMANDS = logger.DBGLVL_TRACE_DETAIL
+
 # Constants for debug levels.
 DBG_START_SHUT = logger.DBGLVL_START_SHUT
 DBG_ZONEMGR_COMMAND = logger.DBGLVL_COMMAND
@@ -657,6 +662,7 @@ class Zonemgr:
         return answer
 
     def run(self):
+        logger.debug(DBG_PROCESS, ZONEMGR_STARTED)
         self.running = True
         try:
             while not self._shutdown_event.is_set():
diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes
index 8abec5d..c866b79 100644
--- a/src/bin/zonemgr/zonemgr_messages.mes
+++ b/src/bin/zonemgr/zonemgr_messages.mes
@@ -67,6 +67,10 @@ zone manager to record the master server for the zone and start a timer;
 when the timer expires, the master will be polled to see if it contains
 new data.
 
+% ZONEMGR_STARTED zonemgr started
+This informational message is output by zonemgr when all initialization
+has been completed and it is entering its main loop.
+
 % ZONEMGR_RECEIVE_SHUTDOWN received SHUTDOWN command
 This is a debug message indicating that the zone manager has received
 a SHUTDOWN command over the command channel from the Boss process.
@@ -128,9 +132,11 @@ a bug report.
 
 % ZONEMGR_UNKNOWN_ZONE_FAIL zone %1 (class %2) is not known to the zone manager
 An XFRIN operation has failed but the zone that was the subject of the
-operation is not being managed by the zone manager.  This may indicate
-an error in the program (as the operation should not have been initiated
-if this were the case).  Please submit a bug report.
+operation is not being managed by the zone manager. This can be either the
+result of a bindctl command to transfer in a currently unknown (or mistyped)
+zone, or, if this error appears without the administrator giving transfer
+commands, it can indicate an error in the program, as it should not have
+initiated transfers of unknown zones on its own.
 
 % ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager
 A NOTIFY was received but the zone that was the subject of the operation
diff --git a/src/cppcheck-suppress.lst b/src/cppcheck-suppress.lst
index 1020ffe..ff4a79a 100644
--- a/src/cppcheck-suppress.lst
+++ b/src/cppcheck-suppress.lst
@@ -2,10 +2,12 @@
 // the following two will suppress, depending on the cppcheck version
 debug
 missingInclude
-// This is a template, and should be excluded from the check
-unreadVariable:src/lib/dns/rdata/template.cc:61
-// Intentional self assignment tests.  Suppress warning about them.
-selfAssignment:src/lib/dns/tests/name_unittest.cc:293
-selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
-selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:137
-selfAssignment:src/lib/dns/tests/rdata_txt_like_unittest.cc:222
+
+// Please don't add any suppressions here. We now use inline
+// suppressions (in the .cc files) so that we don't have to
+// maintain line numbers in this file.
+//
+// See the cppcheck manual for syntax. It is something like:
+//
+//    // cppcheck-suppress duplicateExpression
+//    EXPECT_FALSE(small_name < small_name);
diff --git a/src/lib/acl/ip_check.cc b/src/lib/acl/ip_check.cc
index 76aacca..7192064 100644
--- a/src/lib/acl/ip_check.cc
+++ b/src/lib/acl/ip_check.cc
@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <sys/types.h>
 #include <sys/socket.h>
 
 #include <exceptions/exceptions.h>
diff --git a/src/lib/acl/tests/.gitignore b/src/lib/acl/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/acl/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiodns/.gitignore b/src/lib/asiodns/.gitignore
new file mode 100644
index 0000000..dedf17e
--- /dev/null
+++ b/src/lib/asiodns/.gitignore
@@ -0,0 +1,2 @@
+/asiodns_messages.cc
+/asiodns_messages.h
diff --git a/src/lib/asiodns/Makefile.am b/src/lib/asiodns/Makefile.am
index b5d030d..a03f147 100644
--- a/src/lib/asiodns/Makefile.am
+++ b/src/lib/asiodns/Makefile.am
@@ -24,6 +24,7 @@ libasiodns_la_SOURCES += dns_server.h
 libasiodns_la_SOURCES += dns_service.cc dns_service.h
 libasiodns_la_SOURCES += tcp_server.cc tcp_server.h
 libasiodns_la_SOURCES += udp_server.cc udp_server.h
+libasiodns_la_SOURCES += sync_udp_server.cc sync_udp_server.h
 libasiodns_la_SOURCES += io_fetch.cc io_fetch.h
 libasiodns_la_SOURCES += logger.h logger.cc
 
diff --git a/src/lib/asiodns/dns_server.h b/src/lib/asiodns/dns_server.h
index d3a8528..119aa66 100644
--- a/src/lib/asiodns/dns_server.h
+++ b/src/lib/asiodns/dns_server.h
@@ -88,22 +88,6 @@ public:
     ///             to return.
     virtual void resume(const bool done) { self_->resume(done); }
 
-    /// \brief Indicate whether the server is able to send an answer
-    /// to a query.
-    ///
-    /// This is presently used only for testing purposes.
-    virtual bool hasAnswer() { return (self_->hasAnswer()); }
-
-    /// \brief Returns the current value of the 'coroutine' object
-    ///
-    /// This is a temporary method, intended to be used for debugging
-    /// purposes during development and removed later.  It allows
-    /// callers from outside the coroutine object to retrieve information
-    /// about its current state.
-    ///
-    /// \return The value of the 'coroutine' object
-    virtual int value() { return (self_->value()); }
-
     /// \brief Returns a pointer to a clone of this DNSServer object.
     ///
     /// When a \c DNSServer object is copied or assigned, the result will
diff --git a/src/lib/asiodns/dns_service.cc b/src/lib/asiodns/dns_service.cc
index 967ce15..2cfdea5 100644
--- a/src/lib/asiodns/dns_service.cc
+++ b/src/lib/asiodns/dns_service.cc
@@ -14,28 +14,19 @@
 
 #include <config.h>
 
-#include <netinet/in.h>
-#include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
+#include <exceptions/exceptions.h>
 
-#include <boost/lexical_cast.hpp>
-
-#include <log/dummylog.h>
-
-#include <asio.hpp>
 #include <dns_service.h>
+
 #include <asiolink/io_service.h>
-#include <asiolink/io_service.h>
+
+#include <asio.hpp> // xxx_server.h requires this to be included first
 #include <tcp_server.h>
 #include <udp_server.h>
+#include <sync_udp_server.h>
 
-#include <log/dummylog.h>
-
-#include <boost/lexical_cast.hpp>
 #include <boost/foreach.hpp>
 
-using isc::log::dlog;
-
 using namespace isc::asiolink;
 
 namespace isc {
@@ -44,39 +35,24 @@ namespace asiodns {
 class DNSLookup;
 class DNSAnswer;
 
-namespace {
-
-asio::ip::address
-convertAddr(const std::string& address) {
-    asio::error_code err;
-    asio::ip::address addr = asio::ip::address::from_string(address, err);
-    if (err) {
-        isc_throw(IOError, "Invalid IP address '" << &address << "': "
-            << err.message());
-    }
-    return (addr);
-}
-
-}
-
-
 class DNSServiceImpl {
 public:
-    DNSServiceImpl(IOService& io_service, const char& port,
-                  const asio::ip::address* v4addr,
-                  const asio::ip::address* v6addr,
-                  SimpleCallback* checkin, DNSLookup* lookup,
-                  DNSAnswer* answer);
+    DNSServiceImpl(IOService& io_service, SimpleCallback* checkin,
+                   DNSLookup* lookup, DNSAnswer* answer) :
+            io_service_(io_service), checkin_(checkin), lookup_(lookup),
+            answer_(answer)
+    {}
 
     IOService& io_service_;
 
     typedef boost::shared_ptr<UDPServer> UDPServerPtr;
+    typedef boost::shared_ptr<SyncUDPServer> SyncUDPServerPtr;
     typedef boost::shared_ptr<TCPServer> TCPServerPtr;
     typedef boost::shared_ptr<DNSServer> DNSServerPtr;
     std::vector<DNSServerPtr> servers_;
-    SimpleCallback *checkin_;
-    DNSLookup *lookup_;
-    DNSAnswer *answer_;
+    SimpleCallback* checkin_;
+    DNSLookup* lookup_;
+    DNSAnswer* answer_;
 
     template<class Ptr, class Server> void addServerFromFD(int fd, int af) {
         Ptr server(new Server(io_service_.get_io_service(), fd, af, checkin_,
@@ -84,101 +60,12 @@ public:
         (*server)();
         servers_.push_back(server);
     }
-
-    void addServer(uint16_t port, const asio::ip::address& address) {
-        try {
-            dlog(std::string("Initialize TCP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
-            TCPServerPtr tcpServer(new TCPServer(io_service_.get_io_service(),
-                address, port, checkin_, lookup_, answer_));
-            (*tcpServer)();
-            servers_.push_back(tcpServer);
-            dlog(std::string("Initialize UDP server at ") + address.to_string() + ":" + boost::lexical_cast<std::string>(port));
-            UDPServerPtr udpServer(new UDPServer(io_service_.get_io_service(),
-                address, port, checkin_, lookup_, answer_));
-            (*udpServer)();
-            servers_.push_back(udpServer);
-        }
-        catch (const asio::system_error& err) {
-            // We need to catch and convert any ASIO level exceptions.
-            // This can happen for unavailable address, binding a privilege port
-            // without the privilege, etc.
-            isc_throw(IOError, "Failed to initialize network servers: " <<
-                      err.what());
-        }
-    }
-    void addServer(const char& port, const asio::ip::address& address) {
-        uint16_t portnum;
-        try {
-            // XXX: SunStudio with stlport4 doesn't reject some invalid
-            // representation such as "-1" by lexical_cast<uint16_t>, so
-            // we convert it into a signed integer of a larger size and perform
-            // range check ourselves.
-            const int32_t portnum32 = boost::lexical_cast<int32_t>(&port);
-            if (portnum32 < 0 || portnum32 > 65535) {
-                isc_throw(IOError, "Invalid port number '" << &port);
-            }
-            portnum = portnum32;
-        } catch (const boost::bad_lexical_cast& ex) {
-            isc_throw(IOError, "Invalid port number '" << &port << "': " <<
-                      ex.what());
-        }
-        addServer(portnum, address);
-    }
 };
 
-DNSServiceImpl::DNSServiceImpl(IOService& io_service,
-                               const char& port,
-                               const asio::ip::address* const v4addr,
-                               const asio::ip::address* const v6addr,
-                               SimpleCallback* checkin,
-                               DNSLookup* lookup,
-                               DNSAnswer* answer) :
-    io_service_(io_service),
-    checkin_(checkin),
-    lookup_(lookup),
-    answer_(answer)
-{
-
-    if (v4addr) {
-        addServer(port, *v4addr);
-    }
-    if (v6addr) {
-        addServer(port, *v6addr);
-    }
-}
-
-DNSService::DNSService(IOService& io_service,
-                       const char& port, const char& address,
-                       SimpleCallback* checkin,
-                       DNSLookup* lookup,
-                       DNSAnswer* answer) :
-    impl_(new DNSServiceImpl(io_service, port, NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
-{
-    addServer(port, &address);
-}
-
-DNSService::DNSService(IOService& io_service,
-                       const char& port,
-                       const bool use_ipv4, const bool use_ipv6,
-                       SimpleCallback* checkin,
-                       DNSLookup* lookup,
-                       DNSAnswer* answer) :
-    impl_(NULL), io_service_(io_service)
-{
-    const asio::ip::address v4addr_any =
-        asio::ip::address(asio::ip::address_v4::any());
-    const asio::ip::address* const v4addrp = use_ipv4 ? &v4addr_any : NULL; 
-    const asio::ip::address v6addr_any =
-        asio::ip::address(asio::ip::address_v6::any());
-    const asio::ip::address* const v6addrp = use_ipv6 ? &v6addr_any : NULL;
-    impl_ = new DNSServiceImpl(io_service, port, v4addrp, v6addrp, checkin, lookup, answer);
-}
-
 DNSService::DNSService(IOService& io_service, SimpleCallback* checkin,
-    DNSLookup* lookup, DNSAnswer *answer) :
-    impl_(new DNSServiceImpl(io_service, *"0", NULL, NULL, checkin, lookup,
-        answer)), io_service_(io_service)
+                       DNSLookup* lookup, DNSAnswer *answer) :
+    impl_(new DNSServiceImpl(io_service, checkin, lookup, answer)),
+    io_service_(io_service)
 {
 }
 
@@ -186,22 +73,22 @@ DNSService::~DNSService() {
     delete impl_;
 }
 
-void
-DNSService::addServer(const char& port, const std::string& address) {
-    impl_->addServer(port, convertAddr(address));
-}
-
-void
-DNSService::addServer(uint16_t port, const std::string& address) {
-    impl_->addServer(port, convertAddr(address));
-}
-
 void DNSService::addServerTCPFromFD(int fd, int af) {
     impl_->addServerFromFD<DNSServiceImpl::TCPServerPtr, TCPServer>(fd, af);
 }
 
-void DNSService::addServerUDPFromFD(int fd, int af) {
-    impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(fd, af);
+void DNSService::addServerUDPFromFD(int fd, int af, ServerFlag options) {
+    if ((~SERVER_DEFINED_FLAGS & static_cast<unsigned int>(options)) != 0) {
+        isc_throw(isc::InvalidParameter, "Invalid DNS/UDP server option: "
+                  << options);
+    }
+    if ((options & SERVER_SYNC_OK) != 0) {
+        impl_->addServerFromFD<DNSServiceImpl::SyncUDPServerPtr,
+            SyncUDPServer>(fd, af);
+    } else {
+        impl_->addServerFromFD<DNSServiceImpl::UDPServerPtr, UDPServer>(
+            fd, af);
+    }
 }
 
 void
diff --git a/src/lib/asiodns/dns_service.h b/src/lib/asiodns/dns_service.h
index 66f8d33..8f2f6d7 100644
--- a/src/lib/asiodns/dns_service.h
+++ b/src/lib/asiodns/dns_service.h
@@ -27,6 +27,66 @@ class DNSLookup;
 class DNSAnswer;
 class DNSServiceImpl;
 
+/// \brief A base class for common \c DNSService interfaces.
+///
+/// This class is defined mainly for test code so it can use a faked/mock
+/// version of a derived class and test scenarios that would involve
+/// \c DNSService without actually instantiating the real service class.
+///
+/// It doesn't intend to be a customization for other purposes - we generally
+/// expect non test code only use \c DNSService directly.
+/// For this reason most of the detailed description are given in the
+/// \c DNSService class.  See that for further details of specific methods
+/// and class behaviors.
+class DNSServiceBase {
+protected:
+    /// \brief Default constructor.
+    ///
+    /// This is protected so this class couldn't be accidentally instantiated
+    /// directly, even if there were no pure virtual functions.
+    DNSServiceBase() {}
+
+public:
+    /// \brief Flags for optional server properties.
+    ///
+    /// The values of this enumerable type are intended to be used to specify
+    /// a particular property of the server created via the \c addServer
+    /// variants.  As we see need for more such properties, a compound
+    /// form of flags (i.e., a single value generated by bitwise OR'ed
+    /// multiple flag values) will be allowed.
+    ///
+    /// Note: the description is given here because it's used in the method
+    /// signature.  It essentially belongs to the derived \c DNSService
+    /// class.
+    enum ServerFlag {
+        SERVER_DEFAULT = 0, ///< The default flag (no particular property)
+        SERVER_SYNC_OK = 1 ///< The server can act in the "synchronous" mode.
+                           ///< In this mode, the client ensures that the
+                           ///< lookup provider always completes the query
+                           ///< process and it immediately releases the
+                           ///< ownership of the given buffer.  This allows
+                           ///< the server implementation to introduce some
+                           ///< optimization such as omitting unnecessary
+                           ///< operation or reusing internal resources.
+                           ///< Note that in functionality the non
+                           ///< "synchronous" mode is compatible with the
+                           ///< synchronous mode; it's up to the server
+                           ///< implementation whether it exploits the
+                           ///< information given by the client.
+    };
+
+public:
+    /// \brief The destructor.
+    virtual ~DNSServiceBase() {}
+
+    virtual void addServerTCPFromFD(int fd, int af) = 0;
+    virtual void addServerUDPFromFD(int fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT) = 0;
+    virtual void clearServers() = 0;
+
+    virtual asiolink::IOService& getIOService() = 0;
+};
+
 /// \brief Handle DNS Queries
 ///
 /// DNSService is the service that handles DNS queries and answers with
@@ -34,7 +94,7 @@ class DNSServiceImpl;
 /// logic that is shared between the authoritative and the recursive
 /// server implementations. As such, it handles asio, including config
 /// updates (through the 'Checkinprovider'), and listening sockets.
-class DNSService {
+class DNSService : public DNSServiceBase {
     ///
     /// \name Constructors and Destructor
     ///
@@ -45,50 +105,29 @@ private:
     DNSService(const DNSService& source);
     DNSService& operator=(const DNSService& source);
 
+private:
+    // Bit or'ed all defined \c ServerFlag values.  Used internally for
+    // compatibility check.  Note that this doesn't have to be used by
+    // applications, and doesn't have to be defined in the "base" class.
+    static const unsigned int SERVER_DEFINED_FLAGS = 1;
+
 public:
-    /// \brief The constructor with a specific IP address and port on which
-    /// the services listen on.
-    ///
-    /// \param io_service The IOService to work with
-    /// \param port the port to listen on
-    /// \param address the IP address to listen on
-    /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
-    /// \param lookup The lookup provider (see \c DNSLookup)
-    /// \param answer The answer provider (see \c DNSAnswer)
-    DNSService(asiolink::IOService& io_service, const char& port,
-               const char& address, isc::asiolink::SimpleCallback* checkin,
-               DNSLookup* lookup, DNSAnswer* answer);
-    /// \brief The constructor with a specific port on which the services
-    /// listen on.
+    /// \brief The constructor without any servers.
     ///
-    /// It effectively listens on "any" IPv4 and/or IPv6 addresses.
-    /// IPv4/IPv6 services will be available if and only if \c use_ipv4
-    /// or \c use_ipv6 is \c true, respectively.
+    /// Use addServerTCPFromFD() or addServerUDPFromFD() to add some servers.
     ///
     /// \param io_service The IOService to work with
-    /// \param port the port to listen on
-    /// \param use_ipv4 If true, listen on ipv4 'any'
-    /// \param use_ipv6 If true, listen on ipv6 'any'
     /// \param checkin Provider for cc-channel events (see \c SimpleCallback)
     /// \param lookup The lookup provider (see \c DNSLookup)
     /// \param answer The answer provider (see \c DNSAnswer)
-    DNSService(asiolink::IOService& io_service, const char& port,
-               const bool use_ipv4, const bool use_ipv6,
-               isc::asiolink::SimpleCallback* checkin, DNSLookup* lookup,
-               DNSAnswer* answer);
-    /// \brief The constructor without any servers.
-    ///
-    /// Use addServer() to add some servers.
-    DNSService(asiolink::IOService& io_service, isc::asiolink::SimpleCallback* checkin,
+    DNSService(asiolink::IOService& io_service,
+               isc::asiolink::SimpleCallback* checkin,
                DNSLookup* lookup, DNSAnswer* answer);
+
     /// \brief The destructor.
-    ~DNSService();
+    virtual ~DNSService();
     //@}
 
-    /// \brief Add another server to the service
-    void addServer(uint16_t port, const std::string &address);
-    void addServer(const char &port, const std::string &address);
-
     /// \brief Add another TCP server/listener to the service from already
     /// opened file descriptor
     ///
@@ -99,13 +138,17 @@ public:
     /// specific address) and is ready for listening to new connection
     /// requests but has not actually started listening.
     ///
+    /// At the moment, TCP servers don't support any optional properties;
+    /// so unlike the UDP version of the method it doesn't have an \c options
+    /// argument.
+    ///
     /// \param fd the file descriptor to be used.
     /// \param af the address family of the file descriptor. Must be either
     ///     AF_INET or AF_INET6.
     /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6.
     /// \throw isc::asiolink::IOError when a low-level error happens, like the
     ///     fd is not a valid descriptor or it can't be listened on.
-    void addServerTCPFromFD(int fd, int af);
+    virtual void addServerTCPFromFD(int fd, int af);
 
     /// \brief Add another UDP server to the service from already opened
     ///    file descriptor
@@ -119,10 +162,14 @@ public:
     /// \param fd the file descriptor to be used.
     /// \param af the address family of the file descriptor. Must be either
     ///     AF_INET or AF_INET6.
-    /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6.
+    /// \param options Optional properties of the server (see ServerFlag).
+    ///
+    /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6,
+    ///     or the given \c options include an unsupported or invalid value.
     /// \throw isc::asiolink::IOError when a low-level error happens, like the
     ///     fd is not a valid descriptor or it can't be listened on.
-    void addServerUDPFromFD(int fd, int af);
+    virtual void addServerUDPFromFD(int fd, int af,
+                                    ServerFlag options = SERVER_DEFAULT);
 
     /// \brief Remove all servers from the service
     void clearServers();
@@ -138,7 +185,7 @@ public:
     /// \brief Return the IO Service Object
     ///
     /// \return IOService object for this DNS service.
-    asiolink::IOService& getIOService() { return (io_service_);}
+    virtual asiolink::IOService& getIOService() { return (io_service_);}
 
 private:
     DNSServiceImpl* impl_;
@@ -148,3 +195,7 @@ private:
 } // namespace asiodns
 } // namespace isc
 #endif // __ASIOLINK_DNS_SERVICE_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc
index b22d067..eed5fdf 100644
--- a/src/lib/asiodns/io_fetch.cc
+++ b/src/lib/asiodns/io_fetch.cc
@@ -14,10 +14,10 @@
 
 #include <config.h>
 
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <stdint.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 
 #include <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
@@ -205,7 +205,8 @@ IOFetch::IOFetch(Protocol protocol, IOService& service,
 }
 
 void
-IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& service,
+IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol,
+                     IOService& service,
                      const isc::dns::Question& question,
                      const IOAddress& address, uint16_t port,
                      OutputBufferPtr& buff, Callback* cb, int wait, bool edns)
@@ -225,8 +226,10 @@ IOFetch::initIOFetch(MessagePtr& query_msg, Protocol protocol, IOService& servic
         query_msg->setEDNS(edns_query);
     }
 
-    MessageRenderer renderer(*data_->msgbuf);
+    MessageRenderer renderer;
+    renderer.setBuffer(data_->msgbuf.get());
     query_msg->toWire(renderer);
+    renderer.setBuffer(NULL);
 }
 
 // Return protocol in use.
diff --git a/src/lib/asiodns/sync_udp_server.cc b/src/lib/asiodns/sync_udp_server.cc
new file mode 100644
index 0000000..a31301d
--- /dev/null
+++ b/src/lib/asiodns/sync_udp_server.cc
@@ -0,0 +1,193 @@
+// 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 <asio.hpp>
+#include <asio/error.hpp>
+
+#include "sync_udp_server.h"
+#include "logger.h"
+
+#include <asiolink/dummy_io_cb.h>
+#include <asiolink/udp_endpoint.h>
+#include <asiolink/udp_socket.h>
+
+#include <boost/bind.hpp>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>             // for some IPC/network system calls
+#include <errno.h>
+
+using namespace std;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace asiodns {
+
+SyncUDPServer::SyncUDPServer(asio::io_service& io_service, const int fd,
+                             const int af, asiolink::SimpleCallback* checkin,
+                             DNSLookup* lookup, DNSAnswer* answer) :
+    output_buffer_(new isc::util::OutputBuffer(0)),
+    query_(new isc::dns::Message(isc::dns::Message::PARSE)),
+    answer_(new isc::dns::Message(isc::dns::Message::RENDER)),
+    io_(io_service), checkin_callback_(checkin), lookup_callback_(lookup),
+    answer_callback_(answer), stopped_(false)
+{
+    if (af != AF_INET && af != AF_INET6) {
+        isc_throw(InvalidParameter, "Address family must be either AF_INET "
+                  "or AF_INET6, not " << af);
+    }
+    LOG_DEBUG(logger, DBGLVL_TRACE_BASIC, ASIODNS_FD_ADD_UDP).arg(fd);
+    try {
+        socket_.reset(new asio::ip::udp::socket(io_service));
+        socket_->assign(af == AF_INET6 ? asio::ip::udp::v6() :
+                        asio::ip::udp::v4(), fd);
+    } catch (const std::exception& exception) {
+        // Whatever the thing throws, it is something from ASIO and we
+        // convert it
+        isc_throw(IOError, exception.what());
+    }
+}
+
+void
+SyncUDPServer::scheduleRead() {
+    socket_->async_receive_from(asio::buffer(data_, MAX_LENGTH), sender_,
+                                boost::bind(&SyncUDPServer::handleRead, this,
+                                            _1, _2));
+}
+
+void
+SyncUDPServer::handleRead(const asio::error_code& ec, const size_t length) {
+    // Abort on fatal errors
+    if (ec) {
+        using namespace asio::error;
+        if (ec.value() != would_block && ec.value() != try_again &&
+            ec.value() != interrupted) {
+            return;
+        }
+    }
+    // Some kind of interrupt, spurious wakeup, or like that. Just try reading
+    // again.
+    if (ec || length == 0) {
+        scheduleRead();
+        return;
+    }
+    // OK, we have a real packet of data. Let's dig into it!
+
+    // XXX: This is taken (and ported) from UDPSocket class. What the hell does
+    // it really mean?
+
+    // The UDP socket class has been extended with asynchronous functions
+    // and takes as a template parameter a completion callback class.  As
+    // UDPServer does not use these extended functions (only those defined
+    // in the IOSocket base class) - but needs a UDPSocket to get hold of
+    // the underlying Boost UDP socket - DummyIOCallback is used.  This
+    // provides the appropriate operator() but is otherwise functionless.
+    UDPSocket<DummyIOCallback> socket(*socket_);
+    UDPEndpoint endpoint(sender_);
+    IOMessage message(data_, length, socket, endpoint);
+    if (checkin_callback_ != NULL) {
+        (*checkin_callback_)(message);
+        if (stopped_) {
+            return;
+        }
+    }
+
+    // If we don't have a DNS Lookup provider, there's no point in
+    // continuing; we exit the coroutine permanently.
+    if (lookup_callback_ == NULL) {
+        scheduleRead();
+        return;
+    }
+
+    // Make sure the buffers are fresh
+    output_buffer_->clear();
+    query_->clear(isc::dns::Message::PARSE);
+    answer_->clear(isc::dns::Message::RENDER);
+
+    // Mark that we don't have an answer yet.
+    done_ = false;
+    resume_called_ = false;
+
+    // Call the actual lookup
+    (*lookup_callback_)(message, query_, answer_, output_buffer_, this);
+
+    if (!resume_called_) {
+        isc_throw(isc::Unexpected,
+                  "No resume called from the lookup callback");
+    }
+
+    if (stopped_) {
+        return;
+    }
+
+    if (done_) {
+        // Good, there's an answer.
+        // Call the answer callback to render it.
+        (*answer_callback_)(message, query_, answer_, output_buffer_);
+
+        if (stopped_) {
+            return;
+        }
+
+        socket_->send_to(asio::buffer(output_buffer_->getData(),
+                                      output_buffer_->getLength()),
+                         sender_);
+    }
+
+    // And schedule handling another socket.
+    scheduleRead();
+}
+
+void
+SyncUDPServer::operator()(asio::error_code, size_t) {
+    // To start the server, we just schedule reading of data when they
+    // arrive.
+    scheduleRead();
+}
+
+/// Stop the UDPServer
+void
+SyncUDPServer::stop() {
+    /// Using close instead of cancel, because cancel
+    /// will only cancel the asynchornized event already submitted
+    /// to io service, the events post to io service after
+    /// cancel still can be scheduled by io service, if
+    /// the socket is cloesed, all the asynchronized event
+    /// for it won't be scheduled by io service not matter it is
+    /// submit to io serice before or after close call. And we will
+    //. get bad_descriptor error
+    socket_->close();
+    stopped_ = true;
+}
+
+/// Post this coroutine on the ASIO service queue so that it will
+/// resume processing where it left off.  The 'done' parameter indicates
+/// whether there is an answer to return to the client.
+void
+SyncUDPServer::resume(const bool done) {
+    resume_called_ = true;
+    done_ = done;
+}
+
+bool
+SyncUDPServer::hasAnswer() {
+    return (done_);
+}
+
+} // namespace asiodns
+} // namespace isc
diff --git a/src/lib/asiodns/sync_udp_server.h b/src/lib/asiodns/sync_udp_server.h
new file mode 100644
index 0000000..9718422
--- /dev/null
+++ b/src/lib/asiodns/sync_udp_server.h
@@ -0,0 +1,148 @@
+// 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 __SYNC_UDP_SERVER_H
+#define __SYNC_UDP_SERVER_H 1
+
+#ifndef ASIO_HPP
+#error "asio.hpp must be included before including this, see asiolink.h as to why"
+#endif
+
+#include "dns_answer.h"
+#include "dns_lookup.h"
+#include "dns_server.h"
+
+#include <dns/message.h>
+#include <asiolink/simple_callback.h>
+#include <util/buffer.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <stdint.h>
+
+namespace isc {
+namespace asiodns {
+
+/// \brief An UDP server that doesn't asynchronous lookup handlers.
+///
+/// That means, the lookup handler must provide the answer right away.
+/// This allows for implementation with less overhead, compared with
+/// the UDPClass.
+class SyncUDPServer : public DNSServer, public boost::noncopyable {
+public:
+    /// \brief Constructor
+    /// \param io_service the asio::io_service to work with
+    /// \param fd the file descriptor of opened UDP socket
+    /// \param af address family, either AF_INET or AF_INET6
+    /// \param checkin the callbackprovider for non-DNS events
+    /// \param lookup the callbackprovider for DNS lookup events
+    /// \param answer the callbackprovider for DNS answer events
+    /// \throw isc::InvalidParameter if af is neither AF_INET nor AF_INET6
+    /// \throw isc::asiolink::IOError when a low-level error happens, like the
+    ///     fd is not a valid descriptor.
+    SyncUDPServer(asio::io_service& io_service, const int fd, const int af,
+                  isc::asiolink::SimpleCallback* checkin = NULL,
+                  DNSLookup* lookup = NULL, DNSAnswer* answer = NULL);
+
+    /// \brief Start the SyncUDPServer.
+    ///
+    /// This is the function operator to keep interface with other server
+    /// classes. They need that because they're coroutines.
+    virtual void operator()(asio::error_code ec = asio::error_code(),
+                    size_t length = 0);
+
+    /// \brief Calls the lookup callback
+    virtual void asyncLookup() {
+        isc_throw(Unexpected,
+                  "SyncUDPServer doesn't support asyncLookup by design, use "
+                  "UDPServer if you need it.");
+    }
+
+    /// \brief Stop the running server
+    /// \note once the server stopped, it can't restart
+    virtual void stop();
+
+    /// \brief Resume operation
+    ///
+    /// Note that unlike other servers, this one expects it to be called
+    /// directly from the lookup callback. If it isn't, the server will
+    /// throw an Unexpected exception (probably to the event loop, which
+    /// would usually lead to termination of the program, but that's OK,
+    /// as it would be serious programmer error).
+    ///
+    /// \param done Set this to true if the lookup action is done and
+    ///        we have an answer
+    virtual void resume(const bool done);
+
+    /// \brief Check if we have an answer
+    ///
+    /// \return true if we have an answer
+    virtual bool hasAnswer();
+
+    /// \brief Clones the object
+    ///
+    /// Since cloning is for the use of coroutines, the synchronous UDP server
+    /// does not need to be cloned. Therefore supporting it would be needless
+    /// work, and trying to clone it would be a programmer error anyway, this
+    /// throws Unexpected.
+    ///
+    /// \return a newly allocated copy of this object
+    virtual DNSServer* clone() {
+        isc_throw(Unexpected, "SyncUDPServer can't be cloned.");
+    }
+private:
+    // Internal state & buffers. We don't use the PIMPL idiom, as this class
+    // isn't usually used directly anyway.
+
+    // Maximum size of incoming UDP packet
+    static const size_t MAX_LENGTH = 4096;
+    // Buffer for incoming data
+    uint8_t data_[MAX_LENGTH];
+    // The buffer to render the output to and send it.
+    // If it was OK to have just a buffer, not the wrapper class,
+    // we could reuse the data_
+    isc::util::OutputBufferPtr output_buffer_;
+    // Objects to hold the query message and the answer
+    isc::dns::MessagePtr query_, answer_;
+    // The socket used for the communication
+    std::auto_ptr<asio::ip::udp::socket> socket_;
+    // The event loop we use
+    asio::io_service& io_;
+    // Place the socket puts the sender of a packet when it is received
+    asio::ip::udp::endpoint sender_;
+    // Callbacks
+    const asiolink::SimpleCallback* checkin_callback_;
+    const DNSLookup* lookup_callback_;
+    const DNSAnswer* answer_callback_;
+    // Answers from the lookup callback (not sent directly, but signalled
+    // through resume()
+    bool resume_called_, done_;
+    // This turns true when the server stops. Allows for not sending the
+    // answer after we closed the socket.
+    bool stopped_;
+
+    // Auxiliary functions
+
+    // Schedule next read on the socket. Just a wrapper around
+    // socket_->async_read_from with the correct parameters.
+    void scheduleRead();
+    // Callback from the socket's read call (called when there's an error or
+    // when a new packet comes).
+    void handleRead(const asio::error_code& ec, const size_t length);
+};
+
+} // namespace asiodns
+} // namespace isc
+#endif // __SYNC_UDP_SERVER_H
diff --git a/src/lib/asiodns/tcp_server.cc b/src/lib/asiodns/tcp_server.cc
index d116bdb..8e4b4d6 100644
--- a/src/lib/asiodns/tcp_server.cc
+++ b/src/lib/asiodns/tcp_server.cc
@@ -14,9 +14,9 @@
 
 #include <config.h>
 
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 #include <errno.h>
 
 #include <boost/shared_array.hpp>
@@ -47,28 +47,6 @@ namespace asiodns {
 /// The following functions implement the \c TCPServer class.
 ///
 /// The constructor
-TCPServer::TCPServer(io_service& io_service,
-                     const ip::address& addr, const uint16_t port, 
-                     const SimpleCallback* checkin,
-                     const DNSLookup* lookup,
-                     const DNSAnswer* answer) :
-    io_(io_service), done_(false),
-    checkin_callback_(checkin), lookup_callback_(lookup),
-    answer_callback_(answer)
-{
-    tcp::endpoint endpoint(addr, port);
-    acceptor_.reset(new tcp::acceptor(io_service));
-    acceptor_->open(endpoint.protocol());
-    // Set v6-only (we use a separate instantiation for v4,
-    // otherwise asio will bind to both v4 and v6
-    if (addr.is_v6()) {
-        acceptor_->set_option(ip::v6_only(true));
-    }
-    acceptor_->set_option(tcp::acceptor::reuse_address(true));
-    acceptor_->bind(endpoint);
-    acceptor_->listen();
-}
-
 TCPServer::TCPServer(io_service& io_service, int fd, int af,
                      const SimpleCallback* checkin,
                      const DNSLookup* lookup,
diff --git a/src/lib/asiodns/tcp_server.h b/src/lib/asiodns/tcp_server.h
index d079e97..01695e4 100644
--- a/src/lib/asiodns/tcp_server.h
+++ b/src/lib/asiodns/tcp_server.h
@@ -37,12 +37,6 @@ namespace asiodns {
 /// defined in coroutine.h. 
 class TCPServer : public virtual DNSServer, public virtual coroutine {
 public:
-    explicit TCPServer(asio::io_service& io_service,
-                       const asio::ip::address& addr, const uint16_t port, 
-                       const isc::asiolink::SimpleCallback* checkin = NULL,
-                       const DNSLookup* lookup = NULL,
-                       const DNSAnswer* answer = NULL);
-
     /// \brief Constructor
     /// \param io_service the asio::io_service to work with
     /// \param fd the file descriptor of opened TCP socket
@@ -62,9 +56,6 @@ public:
     void asyncLookup();
     void stop();
     void resume(const bool done);
-    bool hasAnswer() { return (done_); }
-    int value() { return (get_value()); }
-
     DNSServer* clone() {
         TCPServer* s = new TCPServer(*this);
         return (s);
diff --git a/src/lib/asiodns/tests/.gitignore b/src/lib/asiodns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiodns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/asiodns/tests/Makefile.am b/src/lib/asiodns/tests/Makefile.am
index f49d485..95094f0 100644
--- a/src/lib/asiodns/tests/Makefile.am
+++ b/src/lib/asiodns/tests/Makefile.am
@@ -18,7 +18,7 @@ TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
-run_unittests_SOURCES += io_service_unittest.cc
+run_unittests_SOURCES += dns_service_unittest.cc
 run_unittests_SOURCES += dns_server_unittest.cc
 run_unittests_SOURCES += io_fetch_unittest.cc
 
diff --git a/src/lib/asiodns/tests/dns_server_unittest.cc b/src/lib/asiodns/tests/dns_server_unittest.cc
index c9bbe7c..a5e83c7 100644
--- a/src/lib/asiodns/tests/dns_server_unittest.cc
+++ b/src/lib/asiodns/tests/dns_server_unittest.cc
@@ -19,6 +19,7 @@
 #include <asiolink/io_endpoint.h>
 #include <asiolink/io_error.h>
 #include <asiodns/udp_server.h>
+#include <asiodns/sync_udp_server.h>
 #include <asiodns/tcp_server.h>
 #include <asiodns/dns_answer.h>
 #include <asiodns/dns_lookup.h>
@@ -112,15 +113,22 @@ class DummyChecker : public SimpleCallback, public ServerStopper {
 
 // \brief no lookup logic at all,just provide a checkpoint to stop the server
 class DummyLookup : public DNSLookup, public ServerStopper {
-    public:
-        void operator()(const IOMessage& io_message,
-                isc::dns::MessagePtr message,
-                isc::dns::MessagePtr answer_message,
-                isc::util::OutputBufferPtr buffer,
-                DNSServer* server) const {
-            stopServer();
+public:
+    DummyLookup() :
+        allow_resume_(true)
+    { }
+    void operator()(const IOMessage& io_message,
+            isc::dns::MessagePtr message,
+            isc::dns::MessagePtr answer_message,
+            isc::util::OutputBufferPtr buffer,
+            DNSServer* server) const {
+        stopServer();
+        if (allow_resume_) {
             server->resume(true);
         }
+    }
+    // If you want it not to call resume, set this to false
+    bool allow_resume_;
 };
 
 // \brief copy the data received from user to the answer part
@@ -314,10 +322,11 @@ class TCPClient : public SimpleClient {
 // two servers, UDP client will only communicate with UDP server, same for TCP
 // client
 //
-// This is only the active part of the test. We run the test case twice, once
+// This is only the active part of the test. We run the test case four times, once
 // for each type of initialization (once when giving it the address and port,
-// once when giving the file descriptor), to ensure it works both ways exactly
-// the same.
+// once when giving the file descriptor) multiplied by once for each type of UDP
+// server (UDPServer and SyncUDPServer), to ensure it works exactly the same.
+template<class UDPServerClass>
 class DNSServerTestBase : public::testing::Test {
     protected:
         DNSServerTestBase() :
@@ -396,7 +405,7 @@ class DNSServerTestBase : public::testing::Test {
         SimpleAnswer* const answer_;
         UDPClient*    const udp_client_;
         TCPClient*    const tcp_client_;
-        UDPServer*    udp_server_;
+        UDPServerClass* udp_server_;
         TCPServer*    tcp_server_;
 
         // To access them in signal handle function, the following
@@ -405,19 +414,9 @@ class DNSServerTestBase : public::testing::Test {
         static bool io_service_is_time_out;
 };
 
-// Initialization with name and port
-class AddrPortInit : public DNSServerTestBase {
-protected:
-    AddrPortInit() {
-        udp_server_ = new UDPServer(service, server_address_, server_port,
-                                    checker_, lookup_, answer_);
-        tcp_server_ = new TCPServer(service, server_address_, server_port,
-                                    checker_, lookup_, answer_);
-    }
-};
-
-// Initialization by the file descriptor
-class FdInit : public DNSServerTestBase {
+// Initialization (by the file descriptor)
+template<class UDPServerClass>
+class FdInit : public DNSServerTestBase<UDPServerClass> {
 private:
     // Opens the file descriptor for us
     // It uses the low-level C api, as it seems to be the easiest way to get
@@ -465,12 +464,14 @@ protected:
     void SetUp() {
         const int fdUDP(getFd(SOCK_DGRAM));
         ASSERT_NE(-1, fdUDP) << strerror(errno);
-        udp_server_ = new UDPServer(service, fdUDP, AF_INET6, checker_,
-                                    lookup_, answer_);
+        this->udp_server_ = new UDPServerClass(this->service, fdUDP, AF_INET6,
+                                               this->checker_, this->lookup_,
+                                               this->answer_);
         const int fdTCP(getFd(SOCK_STREAM));
         ASSERT_NE(-1, fdTCP) << strerror(errno);
-        tcp_server_ = new TCPServer(service, fdTCP, AF_INET6, checker_,
-                                    lookup_, answer_);
+        this->tcp_server_ = new TCPServer(this->service, fdTCP, AF_INET6,
+                                          this->checker_, this->lookup_,
+                                          this->answer_);
     }
 };
 
@@ -478,11 +479,17 @@ protected:
 template<class Parent>
 class DNSServerTest : public Parent { };
 
-typedef ::testing::Types<AddrPortInit, FdInit> ServerTypes;
+typedef ::testing::Types<FdInit<UDPServer>, FdInit<SyncUDPServer> >
+    ServerTypes;
 TYPED_TEST_CASE(DNSServerTest, ServerTypes);
 
-bool DNSServerTestBase::io_service_is_time_out = false;
-asio::io_service* DNSServerTestBase::current_service(NULL);
+typedef ::testing::Types<UDPServer, SyncUDPServer> UDPServerTypes;
+TYPED_TEST_CASE(DNSServerTestBase, UDPServerTypes);
+
+template<class UDPServerClass>
+bool DNSServerTestBase<UDPServerClass>::io_service_is_time_out = false;
+template<class UDPServerClass>
+asio::io_service* DNSServerTestBase<UDPServerClass>::current_service(NULL);
 
 // Test whether server stopped successfully after client get response
 // client will send query and start to wait for response, once client
@@ -529,7 +536,8 @@ TYPED_TEST(DNSServerTest, stopUDPServerDuringPrepareAnswer) {
     EXPECT_TRUE(this->serverStopSucceed());
 }
 
-static void stopServerManyTimes(DNSServer *server, unsigned int times) {
+void
+stopServerManyTimes(DNSServer *server, unsigned int times) {
     for (unsigned int i = 0; i < times; ++i) {
         server->stop();
     }
@@ -608,17 +616,20 @@ TYPED_TEST(DNSServerTest, stopTCPServeMoreThanOnce) {
 }
 
 // It raises an exception when invalid address family is passed
-TEST_F(DNSServerTestBase, invalidFamily) {
+// The parameter here doesn't mean anything
+TYPED_TEST(DNSServerTestBase, invalidFamily) {
     // We abuse DNSServerTestBase for this test, as we don't need the
     // initialization.
-    EXPECT_THROW(UDPServer(service, 0, AF_UNIX, checker_, lookup_,
-                           answer_), isc::InvalidParameter);
-    EXPECT_THROW(TCPServer(service, 0, AF_UNIX, checker_, lookup_,
-                           answer_), isc::InvalidParameter);
+    EXPECT_THROW(TypeParam(this->service, 0, AF_UNIX, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::InvalidParameter);
+    EXPECT_THROW(TCPServer(this->service, 0, AF_UNIX, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::InvalidParameter);
 }
 
 // It raises an exception when invalid address family is passed
-TEST_F(DNSServerTestBase, invalidTCPFD) {
+TYPED_TEST(DNSServerTestBase, invalidTCPFD) {
     // We abuse DNSServerTestBase for this test, as we don't need the
     // initialization.
     /*
@@ -630,11 +641,12 @@ TEST_F(DNSServerTestBase, invalidTCPFD) {
     EXPECT_THROW(UDPServer(service, -1, AF_INET, checker_, lookup_,
                            answer_), isc::asiolink::IOError);
     */
-    EXPECT_THROW(TCPServer(service, -1, AF_INET, checker_, lookup_,
-                           answer_), isc::asiolink::IOError);
+    EXPECT_THROW(TCPServer(this->service, -1, AF_INET, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::asiolink::IOError);
 }
 
-TEST_F(DNSServerTestBase, DISABLED_invalidUDPFD) {
+TYPED_TEST(DNSServerTestBase, DISABLED_invalidUDPFD) {
     /*
      FIXME: The UDP server doesn't fail reliably with an invalid FD.
      We need to find a way to trigger it reliably (it seems epoll
@@ -642,8 +654,25 @@ TEST_F(DNSServerTestBase, DISABLED_invalidUDPFD) {
      not the others, maybe we could make it run this at least on epoll-based
      systems).
     */
-    EXPECT_THROW(UDPServer(service, -1, AF_INET, checker_, lookup_,
-                           answer_), isc::asiolink::IOError);
+    EXPECT_THROW(TypeParam(this->service, -1, AF_INET, this->checker_,
+                           this->lookup_, this->answer_),
+                 isc::asiolink::IOError);
+}
+
+// A specialized test type for SyncUDPServer.
+typedef FdInit<SyncUDPServer> SyncServerTest;
+
+// Check it rejects some of the unsupported operations
+TEST_F(SyncServerTest, unsupportedOps) {
+    EXPECT_THROW(udp_server_->clone(), isc::Unexpected);
+    EXPECT_THROW(udp_server_->asyncLookup(), isc::Unexpected);
+}
+
+// Check it rejects forgotten resume (eg. insists that it is synchronous)
+TEST_F(SyncServerTest, mustResume) {
+    lookup_->allow_resume_ = false;
+    ASSERT_THROW(testStopServerByStopper(udp_server_, udp_client_, lookup_),
+                 isc::Unexpected);
 }
 
 }
diff --git a/src/lib/asiodns/tests/dns_service_unittest.cc b/src/lib/asiodns/tests/dns_service_unittest.cc
new file mode 100644
index 0000000..ce8eee9
--- /dev/null
+++ b/src/lib/asiodns/tests/dns_service_unittest.cc
@@ -0,0 +1,232 @@
+// 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>
+#include <gtest/gtest.h>
+
+#include <exceptions/exceptions.h>
+
+#include <asio.hpp>
+#include <asiolink/asiolink.h>
+#include <asiodns/asiodns.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <csignal>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <unistd.h>
+#include <netdb.h>
+
+using namespace isc::asiolink;
+using namespace isc::asiodns;
+using boost::scoped_ptr;
+using boost::lexical_cast;
+
+namespace {
+const char* const TEST_SERVER_PORT = "53535";
+const char* const TEST_IPV6_ADDR = "::1";
+
+// A simple lookup callback for DNS services.  It records the pointer value of
+// to given output buffer each time the callback is called (up to two times)
+// for the main tests.  At the end of the second callback it stops the server.
+// The sender of the data doesn't expect to get a response, so it simply
+// discards any received data.
+class TestLookup : public DNSLookup {
+public:
+    TestLookup(isc::util::OutputBuffer** b1, isc::util::OutputBuffer** b2,
+               IOService& io_service) :
+        first_buffer_(b1), second_buffer_(b2), io_service_(io_service)
+    {}
+    void operator()(const IOMessage&, isc::dns::MessagePtr,
+                    isc::dns::MessagePtr, isc::util::OutputBufferPtr buffer,
+                    DNSServer* server) const
+    {
+        server->resume(false);
+        if (*first_buffer_ == NULL) {
+            *first_buffer_ = buffer.get();
+        } else {
+            assert(*second_buffer_ == NULL);
+            *second_buffer_ = buffer.get();
+            server->stop();
+            io_service_.stop();
+        }
+    }
+    isc::util::OutputBuffer** first_buffer_;
+    isc::util::OutputBuffer** second_buffer_;
+    IOService& io_service_;
+};
+
+// A test fixture to check creation of UDP servers from a socket FD, changing
+// options.
+class UDPDNSServiceTest : public::testing::Test {
+private:
+    static const unsigned int IO_SERVICE_TIME_OUT = 5;
+
+protected:
+    UDPDNSServiceTest() :
+        first_buffer_(NULL), second_buffer_(NULL),
+        lookup(&first_buffer_, &second_buffer_, io_service),
+        dns_service(io_service, NULL, &lookup, NULL),
+        client_socket(io_service.get_io_service(), asio::ip::udp::v6()),
+        server_ep(asio::ip::address::from_string(TEST_IPV6_ADDR),
+                  lexical_cast<uint16_t>(TEST_SERVER_PORT)),
+        asio_service(io_service.get_io_service())
+    {
+        current_service = &io_service;
+        // Content shouldn't matter for the tests, but initialize anyway
+        memset(data, 1, sizeof(data));
+    }
+
+    ~UDPDNSServiceTest() {
+        current_service = NULL;
+    }
+
+    void runService() {
+        io_service_is_time_out = false;
+
+        // Send two UDP packets, which will be passed to the TestLookup
+        // callback.  They are not expected to be responded, so it simply
+        // closes the socket right after that.
+        client_socket.send_to(asio::buffer(data, sizeof(data)), server_ep);
+        client_socket.send_to(asio::buffer(data, sizeof(data)), server_ep);
+        client_socket.close();
+
+        // set a signal-based alarm to prevent the test from hanging up
+        // due to a bug.
+        void (*prev_handler)(int) =
+            std::signal(SIGALRM, UDPDNSServiceTest::stopIOService);
+        current_service = &io_service;
+        alarm(IO_SERVICE_TIME_OUT);
+        io_service.run();
+        io_service.get_io_service().reset();
+        //cancel scheduled alarm
+        alarm(0);
+        std::signal(SIGALRM, prev_handler);
+    }
+
+    // last resort service stopper by signal
+    static void stopIOService(int) {
+        io_service_is_time_out = true;
+        if (current_service != NULL) {
+            current_service->stop();
+        }
+    }
+
+    bool serverStopSucceed() const {
+        return (!io_service_is_time_out);
+    }
+
+    isc::util::OutputBuffer* first_buffer_;
+    isc::util::OutputBuffer* second_buffer_;
+    IOService io_service;
+    TestLookup lookup;
+    DNSService dns_service;
+private:
+    asio::ip::udp::socket client_socket;
+    const asio::ip::udp::endpoint server_ep;
+    char data[4];
+
+    // To access them in signal handle function, the following
+    // variables have to be static.
+    static IOService* current_service;
+    static bool io_service_is_time_out;
+
+    asio::io_service& asio_service;
+};
+
+// Need to define the non-const static members outside of the class
+// declaration
+IOService* UDPDNSServiceTest::current_service;
+bool UDPDNSServiceTest::io_service_is_time_out;
+
+// A helper socket FD creator for given address and port.  It's generally
+// expected to succeed; on failure it simply throws an exception to make
+// the test fail.
+int
+getSocketFD(int family, const char* const address, const char* const port) {
+    struct addrinfo hints, *res = NULL;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = family;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_protocol = IPPROTO_UDP;
+    hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+    int s = -1;
+    int error = getaddrinfo(address, port, &hints, &res);
+    if (error == 0) {
+        // If getaddrinfo returns 0, res should be set to a non NULL valid
+        // pointer, but some variants of cppcheck reportedly complains about
+        // it, so we satisfy them here.
+        if (res != NULL) {
+            s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+            if (s >= 0) {
+                error = bind(s, res->ai_addr, res->ai_addrlen);
+            }
+            freeaddrinfo(res);
+        }
+    }
+    if (error != 0) {
+        if (s >= 0) {
+            close(s);
+        }
+        isc_throw(isc::Unexpected, "failed to open test socket");
+    }
+    return (s);
+}
+
+TEST_F(UDPDNSServiceTest, defaultUDPServerFromFD) {
+    // If no option is explicitly specified, an asynchronous server should be
+    // created.  So the two output buffers should be different.
+    dns_service.addServerUDPFromFD(getSocketFD(AF_INET6, TEST_IPV6_ADDR,
+                                               TEST_SERVER_PORT), AF_INET6);
+    runService();
+    EXPECT_TRUE(serverStopSucceed());
+    EXPECT_NE(first_buffer_, second_buffer_);
+}
+
+TEST_F(UDPDNSServiceTest, explicitDefaultUDPServerFromFD) {
+    // If "default" option is explicitly specified, the effect should be the
+    // same as the previous case.
+    dns_service.addServerUDPFromFD(getSocketFD(AF_INET6, TEST_IPV6_ADDR,
+                                               TEST_SERVER_PORT),
+                                   AF_INET6, DNSService::SERVER_DEFAULT);
+    runService();
+    EXPECT_TRUE(serverStopSucceed());
+    EXPECT_NE(first_buffer_, second_buffer_);
+}
+
+TEST_F(UDPDNSServiceTest, syncUDPServerFromFD) {
+    // If "SYNC_OK" option is specified, a synchronous server should be
+    // created.  It will reuse the output buffer, so the recorded two pointer
+    // should be identical.
+    dns_service.addServerUDPFromFD(getSocketFD(AF_INET6, TEST_IPV6_ADDR,
+                                               TEST_SERVER_PORT),
+                                   AF_INET6, DNSService::SERVER_SYNC_OK);
+    runService();
+    EXPECT_TRUE(serverStopSucceed());
+    EXPECT_EQ(first_buffer_, second_buffer_);
+}
+
+TEST_F(UDPDNSServiceTest, addUDPServerFromFDWithUnknownOption) {
+    // Use of undefined/incompatible options should result in an exception.
+    EXPECT_THROW(dns_service.addServerUDPFromFD(
+                     getSocketFD(AF_INET6, TEST_IPV6_ADDR, TEST_SERVER_PORT),
+                     AF_INET6, static_cast<DNSService::ServerFlag>(2)),
+                 isc::InvalidParameter);
+}
+
+} // unnamed namespace
diff --git a/src/lib/asiodns/tests/io_fetch_unittest.cc b/src/lib/asiodns/tests/io_fetch_unittest.cc
index 936c6c7..acb184c 100644
--- a/src/lib/asiodns/tests/io_fetch_unittest.cc
+++ b/src/lib/asiodns/tests/io_fetch_unittest.cc
@@ -133,10 +133,15 @@ public:
         EDNSPtr msg_edns(new EDNS());
         msg_edns->setUDPSize(Message::DEFAULT_MAX_EDNS0_UDPSIZE);
         msg.setEDNS(msg_edns);
-        MessageRenderer renderer(*msgbuf_);
+
+        MessageRenderer renderer;
+        renderer.setBuffer(msgbuf_.get());
+        msg.toWire(renderer);
+        renderer.setBuffer(NULL);
+
+        renderer.setBuffer(expected_buffer_.get());
         msg.toWire(renderer);
-        MessageRenderer renderer2(*expected_buffer_);
-        msg.toWire(renderer2);
+        renderer.setBuffer(NULL);
 
         // Initialize the test data to be returned: tests will return a
         // substring of this data. (It's convenient to have this as a member of
@@ -581,20 +586,22 @@ public:
         return_data_ = "Message returned to the client";
 
         udp::endpoint remote;
-        socket.async_receive_from(asio::buffer(receive_buffer_, sizeof(receive_buffer_)),
-            remote,
-            boost::bind(&IOFetchTest::udpReceiveHandler, this, &remote, &socket,
-                        _1, _2, bad_qid, second_send));
+        socket.async_receive_from(asio::buffer(receive_buffer_,
+                                               sizeof(receive_buffer_)),
+                                  remote,
+                                  boost::bind(&IOFetchTest::udpReceiveHandler,
+                                              this, &remote, &socket,
+                                              _1, _2, bad_qid, second_send));
         service_.get_io_service().post(udp_fetch_);
         if (debug_) {
-            cout << "udpSendReceive: async_receive_from posted, waiting for callback" <<
-                    endl;
+            cout << "udpSendReceive: async_receive_from posted,"
+                "waiting for callback" << endl;
         }
         service_.run();
 
         socket.close();
 
-        EXPECT_TRUE(run_);;
+        EXPECT_TRUE(run_);
     }
 };
 
diff --git a/src/lib/asiodns/tests/io_service_unittest.cc b/src/lib/asiodns/tests/io_service_unittest.cc
deleted file mode 100644
index cc64022..0000000
--- a/src/lib/asiodns/tests/io_service_unittest.cc
+++ /dev/null
@@ -1,118 +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>
-#include <gtest/gtest.h>
-
-#include <asio.hpp>
-#include <asiolink/asiolink.h>
-#include <asiodns/asiodns.h>
-
-using namespace isc::asiolink;
-using namespace isc::asiodns;
-
-const char* const TEST_SERVER_PORT = "53535";
-const char* const TEST_CLIENT_PORT = "53536";
-const char* const TEST_IPV6_ADDR = "::1";
-const char* const TEST_IPV4_ADDR = "127.0.0.1";
-
-TEST(IOServiceTest, badPort) {
-    IOService io_service;
-    EXPECT_THROW(DNSService(io_service, *"65536", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"53210.0", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"-1", true, false, NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *"domain", true, false, NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, badAddress) {
-    IOService io_service;
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.1.1", NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"2001:db8:::1", NULL, NULL, NULL), IOError);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"localhost", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, unavailableAddress) {
-    IOService io_service;
-    // These addresses should generally be unavailable as a valid local
-    // address, although there's no guarantee in theory.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"192.0.2.0", NULL, NULL, NULL), IOError);
-
-    // Some OSes would simply reject binding attempt for an AF_INET6 socket
-    // to an IPv4-mapped IPv6 address.  Even if those that allow it, since
-    // the corresponding IPv4 address is the same as the one used in the
-    // AF_INET socket case above, it should at least show the same result
-    // as the previous one.
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:192.0.2.0", NULL, NULL, NULL), IOError);
-}
-
-TEST(IOServiceTest, duplicateBind_v6) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv6, "any" address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, false, true, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v6_address) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv6, specific address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV6_ADDR, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv4, "any" address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, true, false, NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-}
-
-TEST(IOServiceTest, duplicateBind_v4_address) {
-    // In each sub test case, second attempt should fail due to duplicate bind
-    IOService io_service;
-
-    // IPv4, specific address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *TEST_IPV4_ADDR, NULL, NULL, NULL), IOError);
-    delete dns_service;
-}
-
-// Disabled because IPv4-mapped addresses don't seem to be working with
-// the IOService constructor
-TEST(IOServiceTest, DISABLED_IPv4MappedDuplicateBind) {
-    IOService io_service;
-    // Duplicate bind on IPv4-mapped IPv6 address
-    DNSService* dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL), IOError);
-    delete dns_service;
-
-    // XXX:
-    // Currently, this throws an "invalid argument" exception.  I have
-    // not been able to get IPv4-mapped addresses to work.
-    dns_service = new DNSService(io_service, *TEST_SERVER_PORT, *"::ffff:127.0.0.1", NULL, NULL, NULL);
-    EXPECT_THROW(DNSService(io_service, *TEST_SERVER_PORT, *"127.0.0.1", NULL, NULL, NULL), IOError);
-    delete dns_service;
-}
-
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
index 820db4c..0f5456b 100644
--- a/src/lib/asiodns/udp_server.cc
+++ b/src/lib/asiodns/udp_server.cc
@@ -12,9 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 #include <errno.h>
 
 #include <boost/shared_array.hpp>
@@ -182,12 +182,6 @@ struct UDPServer::Data {
 ///
 /// The constructor. It just creates new internal state object
 /// and lets it handle the initialization.
-UDPServer::UDPServer(io_service& io_service, const ip::address& addr,
-                     const uint16_t port, SimpleCallback* checkin,
-                     DNSLookup* lookup, DNSAnswer* answer) :
-    data_(new Data(io_service, addr, port, checkin, lookup, answer))
-{ }
-
 UDPServer::UDPServer(io_service& io_service, int fd, int af,
                      SimpleCallback* checkin, DNSLookup* lookup,
                      DNSAnswer* answer) :
@@ -343,10 +337,5 @@ UDPServer::resume(const bool done) {
     data_->io_.post(*this);
 }
 
-bool
-UDPServer::hasAnswer() {
-    return (data_->done_);
-}
-
 } // namespace asiodns
 } // namespace isc
diff --git a/src/lib/asiodns/udp_server.h b/src/lib/asiodns/udp_server.h
index c82b78c..b32c06c 100644
--- a/src/lib/asiodns/udp_server.h
+++ b/src/lib/asiodns/udp_server.h
@@ -41,19 +41,6 @@ class UDPServer : public virtual DNSServer, public virtual coroutine {
 public:
     /// \brief Constructor
     /// \param io_service the asio::io_service to work with
-    /// \param addr the IP address to listen for queries on
-    /// \param port the port to listen for queries on
-    /// \param checkin the callbackprovider for non-DNS events
-    /// \param lookup the callbackprovider for DNS lookup events
-    /// \param answer the callbackprovider for DNS answer events
-    explicit UDPServer(asio::io_service& io_service,
-                       const asio::ip::address& addr, const uint16_t port,
-                       isc::asiolink::SimpleCallback* checkin = NULL,
-                       DNSLookup* lookup = NULL,
-                       DNSAnswer* answer = NULL);
-
-    /// \brief Constructor
-    /// \param io_service the asio::io_service to work with
     /// \param fd the file descriptor of opened UDP socket
     /// \param af address family, either AF_INET or AF_INET6
     /// \param checkin the callbackprovider for non-DNS events
@@ -83,16 +70,6 @@ public:
     ///        we have an answer
     void resume(const bool done);
 
-    /// \brief Check if we have an answer
-    ///
-    /// \return true if we have an answer
-    bool hasAnswer();
-
-    /// \brief Returns the coroutine state value
-    ///
-    /// \return the coroutine state value
-    int value() { return (get_value()); }
-
     /// \brief Clones the object
     ///
     /// \return a newly allocated copy of this object
diff --git a/src/lib/asiolink/io_address.cc b/src/lib/asiolink/io_address.cc
index 370e8f5..832452c 100644
--- a/src/lib/asiolink/io_address.cc
+++ b/src/lib/asiolink/io_address.cc
@@ -37,7 +37,7 @@ namespace asiolink {
 
 // XXX: we cannot simply construct the address in the initialization list,
 // because we'd like to throw our own exception on failure.
-IOAddress::IOAddress(const string& address_str) {
+IOAddress::IOAddress(const std::string& address_str) {
     asio::error_code err;
     asio_address_ = ip::address::from_string(address_str, err);
     if (err) {
@@ -46,7 +46,7 @@ IOAddress::IOAddress(const string& address_str) {
     }
 }
 
-IOAddress::IOAddress(const ip::address& asio_address) :
+IOAddress::IOAddress(const asio::ip::address& asio_address) :
     asio_address_(asio_address)
 {}
 
diff --git a/src/lib/asiolink/io_service.cc b/src/lib/asiolink/io_service.cc
index 76d5ee1..15fad0c 100644
--- a/src/lib/asiolink/io_service.cc
+++ b/src/lib/asiolink/io_service.cc
@@ -14,9 +14,9 @@
 
 #include <config.h>
 
+#include <unistd.h>             // for some IPC/network system calls
 #include <netinet/in.h>
 #include <sys/socket.h>
-#include <unistd.h>             // for some IPC/network system calls
 
 #include <asio.hpp>
 #include <asiolink/io_service.h>
diff --git a/src/lib/asiolink/tests/.gitignore b/src/lib/asiolink/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/asiolink/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/bench/benchmark.h b/src/lib/bench/benchmark.h
index 7f77aa1..a5c6fd4 100644
--- a/src/lib/bench/benchmark.h
+++ b/src/lib/bench/benchmark.h
@@ -17,6 +17,7 @@
 
 #include <sys/time.h>
 
+#include <cassert>
 #include <iostream>
 #include <ios>
 
@@ -210,9 +211,9 @@ public:
     /// \param target The templated class object that
     /// implements the code to be benchmarked.
     BenchMark(const int iterations, T target) :
-        iterations_(iterations), sub_iterations_(0), target_(target)
+        iterations_(iterations), sub_iterations_(0), target_(NULL)
     {
-        initialize(true);
+        initialize(target, true);
     }
 
     /// \brief Constructor for finer-grained control.
@@ -230,9 +231,9 @@ public:
     /// \param immediate If \c true the benchmark will be performed within
     /// the constructor; otherwise it only does initialization.
     BenchMark(const int iterations, T& target, const bool immediate) :
-        iterations_(iterations), sub_iterations_(0), target_(target)
+        iterations_(iterations), sub_iterations_(0), target_(&target)
     {
-        initialize(immediate);
+        initialize(target, immediate);
     }
     //@}
 
@@ -240,15 +241,17 @@ public:
     ///
     /// This method will be called from \c run() before starting the benchmark.
     /// By default it's empty, but can be customized via template
-    /// specialization.
-    void setUp() {}
+    /// specialization.  When specialized, a reference to the target object
+    /// given to the constructor will be passed to the implementation.
+    void setUp(T&) {}
 
     /// \brief Hook to be called after benchmark.
     ///
     /// This method will be called from \c run() when the benchmark completes.
     /// By default it's empty, but can be customized via template
-    /// specialization.
-    void tearDown() {}
+    /// specialization.  When specialized, a reference to the target object
+    /// given to the constructor will be passed to the implementation.
+    void tearDown(T&) {}
 
     /// \brief Perform benchmark.
     ///
@@ -257,17 +260,8 @@ public:
     /// of times specified on construction, and records the time on completion.
     /// Finally, it calls \c tearDown().
     void run() {
-        setUp();
-
-        struct timeval beg, end;
-        gettimeofday(&beg, NULL);
-        for (unsigned int i = 0; i < iterations_; ++i) {
-            sub_iterations_ += target_.run();
-        }
-        gettimeofday(&end, NULL);
-        tv_diff_ = tv_subtract(end, beg);
-
-        tearDown();
+        assert(target_ != NULL);
+        run(*target_);
     }
 
     /// \brief Print the benchmark result.
@@ -361,9 +355,23 @@ public:
     /// performed implicitly.
     static const int ITERATION_FAILURE = -1;
 private:
-    void initialize(const bool immediate) {
+    void run(T& target) {
+        setUp(target);
+
+        struct timeval beg, end;
+        gettimeofday(&beg, NULL);
+        for (unsigned int i = 0; i < iterations_; ++i) {
+            sub_iterations_ += target.run();
+        }
+        gettimeofday(&end, NULL);
+        tv_diff_ = tv_subtract(end, beg);
+
+        tearDown(target);
+    }
+
+    void initialize(T& target, const bool immediate) {
         if (immediate) {
-            run();
+            run(target);
             printResult();
         }
     }
@@ -388,7 +396,7 @@ private:
     static const int ONE_MILLION = 1000000;
     const unsigned int iterations_;
     unsigned int sub_iterations_;
-    T& target_;
+    T* target_;
     struct timeval tv_diff_;
 };
 
diff --git a/src/lib/bench/benchmark_util.cc b/src/lib/bench/benchmark_util.cc
index 9cf3b26..34356c8 100644
--- a/src/lib/bench/benchmark_util.cc
+++ b/src/lib/bench/benchmark_util.cc
@@ -61,8 +61,7 @@ loadQueryData(istream& input, BenchQueries& queries, const RRClass& qclass,
     string line;
     unsigned int linenum = 0;
     Message query_message(Message::RENDER);
-    OutputBuffer buffer(128); // this should be sufficiently large
-    MessageRenderer renderer(buffer);
+    MessageRenderer renderer;
     while (getline(input, line), !input.eof()) {
         ++linenum;
         if (input.bad() || input.fail()) {
@@ -99,9 +98,9 @@ loadQueryData(istream& input, BenchQueries& queries, const RRClass& qclass,
             renderer.clear();
             query_message.toWire(renderer);
             vector<unsigned char> query_data(
-                static_cast<const unsigned char*>(buffer.getData()),
-                static_cast<const unsigned char*>(buffer.getData()) +
-                buffer.getLength());
+                static_cast<const unsigned char*>(renderer.getData()),
+                static_cast<const unsigned char*>(renderer.getData()) +
+                renderer.getLength());
             queries.push_back(query_data);
         } catch (const Exception&) {
             if (strict) {
diff --git a/src/lib/bench/example/.gitignore b/src/lib/bench/example/.gitignore
new file mode 100644
index 0000000..eb3877d
--- /dev/null
+++ b/src/lib/bench/example/.gitignore
@@ -0,0 +1 @@
+/search_bench
diff --git a/src/lib/bench/example/search_bench.cc b/src/lib/bench/example/search_bench.cc
index 851d815..84f95d9 100644
--- a/src/lib/bench/example/search_bench.cc
+++ b/src/lib/bench/example/search_bench.cc
@@ -79,9 +79,9 @@ namespace isc {
 namespace bench {
 template<>
 void
-BenchMark<SetSearchBenchMark>::setUp() {
+BenchMark<SetSearchBenchMark>::setUp(SetSearchBenchMark& target) {
     cout << "Benchmark for searching std::set (size="
-         << target_.data_.size() << ")" << endl;    
+         << target.data_.size() << ")" << endl;
 }
 }
 }
diff --git a/src/lib/bench/tests/.gitignore b/src/lib/bench/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/bench/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/bench/tests/benchmark_unittest.cc b/src/lib/bench/tests/benchmark_unittest.cc
index 7bb8a60..dfe7df9 100644
--- a/src/lib/bench/tests/benchmark_unittest.cc
+++ b/src/lib/bench/tests/benchmark_unittest.cc
@@ -46,14 +46,14 @@ namespace isc {
 namespace bench {
 template <>
 void
-BenchMark<TestBenchMark>::setUp() {
-    target_.setup_completed_ = true;
+BenchMark<TestBenchMark>::setUp(TestBenchMark& target) {
+    target.setup_completed_ = true;
 };
 
 template <>
 void
-BenchMark<TestBenchMark>::tearDown() {
-    target_.teardown_completed_ = true;
+BenchMark<TestBenchMark>::tearDown(TestBenchMark& target) {
+    target.teardown_completed_ = true;
 };
 
 // XXX: some compilers cannot find class static constants used in
@@ -70,9 +70,9 @@ TEST(BenchMarkTest, run) {
     const int sleep_time = 50000; // will sleep for 50ms
     const struct timespec sleep_timespec = { 0, sleep_time * 1000 };
     // we cannot expect particular accuracy on the measured duration, so
-    // we'll include some conservative margin (25%) and perform range
+    // we'll include some conservative margin (50%) and perform range
     // comparison below.
-    const int duration_margin = 12500; // 12.5ms
+    const int duration_margin = 25000; // 25ms
     const int ONE_MILLION = 1000000;
 
     // Prerequisite check: since the tests in this case may depend on subtle
@@ -80,6 +80,8 @@ TEST(BenchMarkTest, run) {
     // where sleeping doesn't work as this test expects.  So we check the
     // conditions before the tests, and if it fails skip the tests at the
     // risk of overlooking possible bugs.
+    // We do this with a tighter margin than the checks themselves
+    const int duration_soft_margin = 12500; // 12.5ms
     struct timeval check_begin, check_end;
     gettimeofday(&check_begin, NULL);
     nanosleep(&sleep_timespec, 0);
@@ -93,8 +95,8 @@ TEST(BenchMarkTest, run) {
         --check_end.tv_sec;
     }
     if (check_end.tv_sec != 0 ||
-        sleep_time - duration_margin > check_end.tv_usec ||
-        sleep_time + duration_margin < check_end.tv_usec) {
+        sleep_time - duration_soft_margin > check_end.tv_usec ||
+        sleep_time + duration_soft_margin < check_end.tv_usec) {
         cerr << "Prerequisite check failed.  skipping test" << endl;
         return;
     }
diff --git a/src/lib/cache/.gitignore b/src/lib/cache/.gitignore
new file mode 100644
index 0000000..a33f3f0
--- /dev/null
+++ b/src/lib/cache/.gitignore
@@ -0,0 +1,2 @@
+/cache_messages.cc
+/cache_messages.h
diff --git a/src/lib/cache/tests/.gitignore b/src/lib/cache/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/cache/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/cache/tests/Makefile.am b/src/lib/cache/tests/Makefile.am
index a215c56..b638f55 100644
--- a/src/lib/cache/tests/Makefile.am
+++ b/src/lib/cache/tests/Makefile.am
@@ -47,11 +47,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 run_unittests_LDADD    = $(GTEST_LDADD)
 
-# NOTE: we may have to clean up this hack later (see the note in configure.ac)
-if NEED_LIBBOOST_THREAD
-run_unittests_LDADD += -lboost_thread
-endif
-
 run_unittests_LDADD += $(top_builddir)/src/lib/cache/libcache.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
diff --git a/src/lib/cache/tests/negative_cache_unittest.cc b/src/lib/cache/tests/negative_cache_unittest.cc
index 56d777d..4935e4a 100644
--- a/src/lib/cache/tests/negative_cache_unittest.cc
+++ b/src/lib/cache/tests/negative_cache_unittest.cc
@@ -52,14 +52,17 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
     msg_nxdomain.makeResponse();
 
     Name non_exist_qname("nonexist.example.com.");
-    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(), msg_nxdomain));
+    EXPECT_TRUE(cache->lookup(non_exist_qname, RRType::A(), RRClass::IN(),
+                msg_nxdomain));
 
     RRsetIterator iter = msg_nxdomain.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record
     const RRTTL& nxdomain_ttl1 = rrset_ptr->getTTL();
-    EXPECT_EQ(nxdomain_ttl1.getValue(), 86400);
+    // May have already crossed seconds boundary
+    EXPECT_GE(nxdomain_ttl1.getValue(), 86399);
+    EXPECT_LE(nxdomain_ttl1.getValue(), 86400);
 
     // SOA response for example.com
     Message msg_example_com_soa(Message::PARSE);
@@ -68,16 +71,22 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
 
     msg_example_com_soa.makeResponse();
     Name soa_qname("example.com.");
-    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(),
+                msg_example_com_soa));
 
     iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
     rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record in answer section
     const RRTTL& soa_ttl = rrset_ptr->getTTL();
-    EXPECT_EQ(soa_ttl.getValue(), 172800);
+    // May have already crossed seconds boundary
+    EXPECT_GE(soa_ttl.getValue(), 172799);
+    EXPECT_LE(soa_ttl.getValue(), 172800);
 
-    sleep(1);
+    // Sleep for 2 seconds. 2 seconds to make sure the final range check
+    // does not overlap with the original ones (in which case this test
+    // would erroneously pass if the ttl value is not changed)
+    sleep(2);
 
     // Query nonexist.example.com again
     Message msg_nxdomain2(Message::PARSE);
@@ -90,7 +99,8 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
 
     // The TTL should equal to the TTL of negative response SOA record
     const RRTTL& nxdomain_ttl2 = rrset_ptr->getTTL();
-    EXPECT_TRUE(86398 <= nxdomain_ttl2.getValue() && nxdomain_ttl2.getValue() <= 86399);
+    EXPECT_GE(nxdomain_ttl2.getValue(), 86397);
+    EXPECT_LE(nxdomain_ttl2.getValue(), 86398);
     // No RRset in ANSWER section
     EXPECT_TRUE(msg_nxdomain2.getRRCount(Message::SECTION_ANSWER) == 0);
     // Check that only one SOA record exist in AUTHORITY section
@@ -103,13 +113,15 @@ TEST_F(NegativeCacheTest, testNXDOMAIN){
     Message msg_example_com_soa2(Message::PARSE);
     messageFromFile(msg_example_com_soa2, "message_example_com_soa.wire");
     msg_example_com_soa2.makeResponse();
-    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa2));
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(),
+                              msg_example_com_soa2));
 
     iter = msg_example_com_soa2.beginSection(Message::SECTION_ANSWER);
     rrset_ptr = *iter;
     const RRTTL& soa_ttl2 = rrset_ptr->getTTL();
     // The TTL should equal to the TTL of SOA record in answer section
-    EXPECT_TRUE(172798 <= soa_ttl2.getValue() && soa_ttl2.getValue() <= 172799);
+    EXPECT_GE(soa_ttl2.getValue(), 172797);
+    EXPECT_LE(soa_ttl2.getValue(), 172798);
 }
 
 TEST_F(NegativeCacheTest, testNXDOMAINWithoutSOA){
@@ -166,15 +178,16 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
     msg_nodata.makeResponse();
 
     Name example_dot_com("example.com.");
-    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata));
+    EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(),
+                msg_nodata));
 
     RRsetIterator iter = msg_nodata.beginSection(Message::SECTION_AUTHORITY);
     RRsetPtr rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record
     const RRTTL& nodata_ttl1 = rrset_ptr->getTTL();
-    EXPECT_EQ(nodata_ttl1.getValue(), 86400);
-
+    EXPECT_GE(nodata_ttl1.getValue(), 86399);
+    EXPECT_LE(nodata_ttl1.getValue(), 86400);
 
     // Normal SOA response for example.com
     Message msg_example_com_soa(Message::PARSE);
@@ -183,21 +196,26 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
 
     msg_example_com_soa.makeResponse();
     Name soa_qname("example.com.");
-    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(), msg_example_com_soa));
+    EXPECT_TRUE(cache->lookup(soa_qname, RRType::SOA(), RRClass::IN(),
+                msg_example_com_soa));
 
     iter = msg_example_com_soa.beginSection(Message::SECTION_ANSWER);
     rrset_ptr = *iter;
 
     // The TTL should equal to the TTL of SOA record in answer section
     const RRTTL& soa_ttl = rrset_ptr->getTTL();
-    EXPECT_EQ(soa_ttl.getValue(), 172800);
+    EXPECT_GE(soa_ttl.getValue(), 172799);
+    EXPECT_LE(soa_ttl.getValue(), 172800);
 
     // Query MX record of example.com again
     Message msg_nodata2(Message::PARSE);
     messageFromFile(msg_nodata2, "message_nodata_with_soa.wire");
     msg_nodata2.makeResponse();
 
-    sleep(1);
+    // Sleep for 2 seconds. 2 seconds to make sure the final range check
+    // does not overlap with the original ones (in which case this test
+    // would erroneously pass if the ttl value is not changed)
+    sleep(2);
 
     EXPECT_TRUE(cache->lookup(example_dot_com, RRType::MX(), RRClass::IN(), msg_nodata2));
 
@@ -209,9 +227,11 @@ TEST_F(NegativeCacheTest, testNoerrorNodata){
     iter = msg_nodata2.beginSection(Message::SECTION_AUTHORITY);
     rrset_ptr = *iter;
 
-    // The TTL should equal to the TTL of negative response SOA record and counted down
+    // The TTL should equal to the TTL of negative response SOA record
+    // and counted down
     const RRTTL& nodata_ttl2 = rrset_ptr->getTTL();
-    EXPECT_TRUE(86398 <= nodata_ttl2.getValue() && nodata_ttl2.getValue() <= 86399);
+    EXPECT_GE(nodata_ttl2.getValue(), 86397);
+    EXPECT_LE(nodata_ttl2.getValue(), 86398);
 }
 
 TEST_F(NegativeCacheTest, testReferralResponse){
diff --git a/src/lib/cc/.gitignore b/src/lib/cc/.gitignore
new file mode 100644
index 0000000..cb2800f
--- /dev/null
+++ b/src/lib/cc/.gitignore
@@ -0,0 +1,4 @@
+/cc_messages.cc
+/cc_messages.h
+/session_config.h
+/session_config.h.pre
diff --git a/src/lib/cc/cc_messages.mes b/src/lib/cc/cc_messages.mes
index 8370cdd..94b955a 100644
--- a/src/lib/cc/cc_messages.mes
+++ b/src/lib/cc/cc_messages.mes
@@ -14,7 +14,7 @@
 
 $NAMESPACE isc::cc
 
-% CC_ASYNC_READ_FAILED asynchronous read failed
+% CC_ASYNC_READ_FAILED asynchronous read failed (error code = %1)
 This marks a low level error, we tried to read data from the message queue
 daemon asynchronously, but the ASIO library returned an error.
 
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 77f948a..6ec243a 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -30,6 +30,10 @@
 
 using namespace std;
 
+namespace {
+const char* WHITESPACE = " \b\f\n\r\t";
+} // end anonymous namespace
+
 namespace isc {
 namespace data {
 
@@ -314,15 +318,49 @@ str_from_stringstream(std::istream &in, const std::string& file, const int line,
     } else {
         throwJSONError("String expected", file, line, pos);
     }
+
     while (c != EOF && c != '"') {
-        ss << c;
-        if (c == '\\' && in.peek() == '"') {
-            ss << in.get();
+        if (c == '\\') {
+            // see the spec for allowed escape characters
+            switch (in.peek()) {
+            case '"':
+                c = '"';
+                break;
+            case '/':
+                c = '/';
+                break;
+            case '\\':
+                c = '\\';
+                break;
+            case 'b':
+                c = '\b';
+                break;
+            case 'f':
+                c = '\f';
+                break;
+            case 'n':
+                c = '\n';
+                break;
+            case 'r':
+                c = '\r';
+                break;
+            case 't':
+                c = '\t';
+                break;
+            default:
+                throwJSONError("Bad escape", file, line, pos);
+            }
+            // drop the escaped char
+            in.get();
             ++pos;
         }
+        ss << c;
         c = in.get();
         ++pos;
     }
+    if (c == EOF) {
+        throwJSONError("Unterminated string", file, line, pos);
+    }
     return (ss.str());
 }
 
@@ -427,12 +465,12 @@ from_stringstream_list(std::istream &in, const std::string& file, int& line,
     ElementPtr list = Element::createList();
     ConstElementPtr cur_list_element;
 
-    skip_chars(in, " \t\n", line, pos);
+    skip_chars(in, WHITESPACE, line, pos);
     while (c != EOF && c != ']') {
         if (in.peek() != ']') {
             cur_list_element = Element::fromJSON(in, file, line, pos);
             list->add(cur_list_element);
-            skip_to(in, file, line, pos, ",]", " \t\n");
+            skip_to(in, file, line, pos, ",]", WHITESPACE);
         }
         c = in.get();
         pos++;
@@ -445,7 +483,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
                       int& pos)
 {
     ElementPtr map = Element::createMap();
-    skip_chars(in, " \t\n", line, pos);
+    skip_chars(in, WHITESPACE, line, pos);
     char c = in.peek();
     if (c == EOF) {
         throwJSONError(std::string("Unterminated map, <string> or } expected"), file, line, pos);
@@ -456,7 +494,7 @@ from_stringstream_map(std::istream &in, const std::string& file, int& line,
         while (c != EOF && c != '}') {
             std::string key = str_from_stringstream(in, file, line, pos);
 
-            skip_to(in, file, line, pos, ":", " \t\n");
+            skip_to(in, file, line, pos, ":", WHITESPACE);
             // skip the :
             in.get();
             pos++;
@@ -464,7 +502,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, ",}", " \t\n");
+            skip_to(in, file, line, pos, ",}", WHITESPACE);
             c = in.get();
             pos++;
         }
@@ -543,7 +581,7 @@ Element::fromJSON(std::istream &in, const std::string& file, int& line,
     char c = 0;
     ElementPtr element;
     bool el_read = false;
-    skip_chars(in, " \n\t", line, pos);
+    skip_chars(in, WHITESPACE, line, pos);
     while (c != EOF && !el_read) {
         c = in.get();
         pos++;
@@ -610,7 +648,14 @@ ElementPtr
 Element::fromJSON(const std::string &in) {
     std::stringstream ss;
     ss << in;
-    return (fromJSON(ss, "<string>"));
+    int line = 1, pos = 1;
+    ElementPtr result(fromJSON(ss, "<string>", line, pos));
+    skip_chars(ss, WHITESPACE, line, pos);
+    // ss must now be at end
+    if (ss.peek() != EOF) {
+        throwJSONError("Extra data", "<string>", line, pos);
+    }
+    return result;
 }
 
 // to JSON format
@@ -642,7 +687,39 @@ NullElement::toJSON(std::ostream& ss) const {
 void
 StringElement::toJSON(std::ostream& ss) const {
     ss << "\"";
-    ss << stringValue();
+    char c;
+    const std::string& str = stringValue();
+    for (size_t i = 0; i < str.size(); ++i) {
+        c = str[i];
+        // Escape characters as defined in JSON spec
+        // Note that we do not escape forward slash; this
+        // is allowed, but not mandatory.
+        switch (c) {
+        case '"':
+            ss << '\\' << c;
+            break;
+        case '\\':
+            ss << '\\' << c;
+            break;
+        case '\b':
+            ss << '\\' << 'b';
+            break;
+        case '\f':
+            ss << '\\' << 'f';
+            break;
+        case '\n':
+            ss << '\\' << 'n';
+            break;
+        case '\r':
+            ss << '\\' << 'r';
+            break;
+        case '\t':
+            ss << '\\' << 't';
+            break;
+        default:
+            ss << c;
+        }
+    }
     ss << "\"";
 }
 
diff --git a/src/lib/cc/session.cc b/src/lib/cc/session.cc
index 1b21d21..40ab86d 100644
--- a/src/lib/cc/session.cc
+++ b/src/lib/cc/session.cc
@@ -249,7 +249,7 @@ SessionImpl::internalRead(const asio::error_code& error,
         }
         user_handler_();
     } else {
-        LOG_ERROR(logger, CC_ASYNC_READ_FAILED);
+        LOG_ERROR(logger, CC_ASYNC_READ_FAILED).arg(error.value());
         isc_throw(SessionError, "asynchronous read failed");
     }
 }
diff --git a/src/lib/cc/tests/.gitignore b/src/lib/cc/tests/.gitignore
new file mode 100644
index 0000000..f10451c
--- /dev/null
+++ b/src/lib/cc/tests/.gitignore
@@ -0,0 +1,2 @@
+/run_unittests
+/session_unittests_config.h
diff --git a/src/lib/cc/tests/Makefile.am b/src/lib/cc/tests/Makefile.am
index 4760855..08b7f33 100644
--- a/src/lib/cc/tests/Makefile.am
+++ b/src/lib/cc/tests/Makefile.am
@@ -24,11 +24,14 @@ run_unittests_SOURCES = data_unittests.cc session_unittests.cc run_unittests.cc
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 
-run_unittests_LDADD = $(GTEST_LDADD)
-run_unittests_LDADD +=  $(top_builddir)/src/lib/cc/libcc.la
+# We need to put our libs first, in case gtest (or any dependency, really)
+# is installed in the same location as a different version of bind10
+# Otherwise the linker may not use the source tree libs 
+run_unittests_LDADD =  $(top_builddir)/src/lib/cc/libcc.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/log/liblog.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD +=  $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(GTEST_LDADD)
 
 endif
 
diff --git a/src/lib/cc/tests/data_unittests.cc b/src/lib/cc/tests/data_unittests.cc
index d8624cb..87d92f6 100644
--- a/src/lib/cc/tests/data_unittests.cc
+++ b/src/lib/cc/tests/data_unittests.cc
@@ -20,6 +20,7 @@
 
 using namespace isc::data;
 
+#include <sstream>
 #include <iostream>
 using std::oct;
 #include <iomanip>
@@ -90,7 +91,7 @@ TEST(Element, from_and_to_json) {
     sv.push_back("-1");
     sv.push_back("-1.234");
     sv.push_back("-123.456");
-    
+
     BOOST_FOREACH(const std::string& s, sv) {
         // test << operator, which uses Element::str()
         std::ostringstream stream;
@@ -122,8 +123,16 @@ TEST(Element, from_and_to_json) {
     sv.push_back("{ \"a\": None}");
     sv.push_back("");
     sv.push_back("nul");
+    sv.push_back("hello\"foobar\"");
+    sv.push_back("\"foobar\"hello");
+    sv.push_back("[]hello");
+    sv.push_back("{}hello");
+    // String not delimited correctly
+    sv.push_back("\"hello");
+    sv.push_back("hello\"");
+
+
     BOOST_FOREACH(std::string s, sv) {
-        
         EXPECT_THROW(el = Element::fromJSON(s), isc::data::JSONError);
     }
 
@@ -150,6 +159,9 @@ TEST(Element, from_and_to_json) {
     EXPECT_EQ("false", Element::fromJSON("FALSE")->str());
     EXPECT_EQ("true", Element::fromJSON("True")->str());
     EXPECT_EQ("true", Element::fromJSON("TRUE")->str());
+    EXPECT_EQ("\"\"", Element::fromJSON("  \n \t \r \f \b \"\" \n \f \t \r \b")->str());
+    EXPECT_EQ("{  }", Element::fromJSON("{  \n  \r \t  \b \f }")->str());
+    EXPECT_EQ("[  ]", Element::fromJSON("[  \n  \r \f \t  \b  ]")->str());
 
     // number overflows
     EXPECT_THROW(Element::fromJSON("12345678901234567890")->str(), JSONError);
@@ -299,6 +311,43 @@ TEST(Element, create_and_value_throws) {
 
 }
 
+// Helper for escape check; it puts the given string in a StringElement,
+// then checks for the following conditions:
+// stringValue() must be same as input
+// toJSON() output must be escaped
+// fromJSON() on the previous output must result in original input
+void
+escapeHelper(const std::string& input, const std::string& expected) {
+    StringElement str_element = StringElement(input);
+    EXPECT_EQ(input, str_element.stringValue());
+    std::stringstream os;
+    str_element.toJSON(os);
+    EXPECT_EQ(expected, os.str());
+    ElementPtr str_element2 = Element::fromJSON(os.str());
+    EXPECT_EQ(str_element.stringValue(), str_element2->stringValue());
+}
+
+TEST(Element, escape) {
+    // Test whether quotes are escaped correctly when creating direct
+    // String elements.
+    escapeHelper("foo\"bar", "\"foo\\\"bar\"");
+    escapeHelper("foo\\bar", "\"foo\\\\bar\"");
+    escapeHelper("foo\bbar", "\"foo\\bbar\"");
+    escapeHelper("foo\fbar", "\"foo\\fbar\"");
+    escapeHelper("foo\nbar", "\"foo\\nbar\"");
+    escapeHelper("foo\rbar", "\"foo\\rbar\"");
+    escapeHelper("foo\tbar", "\"foo\\tbar\"");
+    // Bad escapes
+    EXPECT_THROW(Element::fromJSON("\\a"), JSONError);
+    EXPECT_THROW(Element::fromJSON("\\"), JSONError);
+    // Can't have escaped quotes outside strings
+    EXPECT_THROW(Element::fromJSON("\\\"\\\""), JSONError);
+    // Inside strings is OK
+    EXPECT_NO_THROW(Element::fromJSON("\"\\\"\\\"\""));
+    // A whitespace test
+    EXPECT_NO_THROW(Element::fromJSON("\"  \n  \r \t \f  \n \n    \t\""));
+}
+
 TEST(Element, ListElement) {
     // this function checks the specific functions for ListElements
     ElementPtr el = Element::fromJSON("[ 1, \"bar\", 3 ]");
diff --git a/src/lib/config/.gitignore b/src/lib/config/.gitignore
new file mode 100644
index 0000000..c7ec9d3
--- /dev/null
+++ b/src/lib/config/.gitignore
@@ -0,0 +1,2 @@
+/config_messages.cc
+/config_messages.h
diff --git a/src/lib/config/config_data.h b/src/lib/config/config_data.h
index 197d319..3fdbc25 100644
--- a/src/lib/config/config_data.h
+++ b/src/lib/config/config_data.h
@@ -32,7 +32,7 @@ public:
     DataNotFoundError(const char* file, size_t line, const std::string& what) :
         isc::Exception(file, line, what) {}
 };
-    
+
 class ConfigData {
 public:
     /// Constructs a ConfigData option with no specification and an
diff --git a/src/lib/config/module_spec.cc b/src/lib/config/module_spec.cc
index bebe695..a931070 100644
--- a/src/lib/config/module_spec.cc
+++ b/src/lib/config/module_spec.cc
@@ -40,13 +40,14 @@ check_leaf_item(ConstElementPtr spec, const std::string& name,
         if (spec->get(name)->getType() == type) {
             return;
         } else {
-            throw ModuleSpecError(name + " not of type " + Element::typeToName(type));
+            isc_throw(ModuleSpecError,
+                      name + " not of type " + Element::typeToName(type));
         }
     } else if (mandatory) {
         // todo: want parent item name, and perhaps some info about location
         // in list? or just catch and throw new...
         // or make this part non-throwing and check return value...
-        throw ModuleSpecError(name + " missing in " + spec->str());
+        isc_throw(ModuleSpecError, name + " missing in " + spec->str());
     }
 }
 
@@ -80,7 +81,7 @@ check_config_item(ConstElementPtr spec) {
 void
 check_config_item_list(ConstElementPtr spec) {
     if (spec->getType() != Element::list) {
-        throw ModuleSpecError("config_data is not a list of elements");
+        isc_throw(ModuleSpecError, "config_data is not a list of elements");
     }
     BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
         check_config_item(item);
@@ -122,7 +123,7 @@ void check_statistics_item_list(ConstElementPtr spec);
 void
 check_statistics_item_list(ConstElementPtr spec) {
     if (spec->getType() != Element::list) {
-        throw ModuleSpecError("statistics is not a list of elements");
+        isc_throw(ModuleSpecError, "statistics is not a list of elements");
     }
     BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
         check_config_item(item);
@@ -135,7 +136,7 @@ check_statistics_item_list(ConstElementPtr spec) {
             && item->contains("item_default")) {
             if(!check_format(item->get("item_default"),
                              item->get("item_format"))) {
-                throw ModuleSpecError(
+                isc_throw(ModuleSpecError, 
                     "item_default not valid type of item_format");
             }
         }
@@ -152,7 +153,7 @@ check_command(ConstElementPtr spec) {
 void
 check_command_list(ConstElementPtr spec) {
     if (spec->getType() != Element::list) {
-        throw ModuleSpecError("commands is not a list of elements");
+        isc_throw(ModuleSpecError, "commands is not a list of elements");
     }
     BOOST_FOREACH(ConstElementPtr item, spec->listValue()) {
         check_command(item);
@@ -183,7 +184,7 @@ check_module_specification(ConstElementPtr def) {
     try {
         check_data_specification(def);
     } catch (const TypeError& te) {
-        throw ModuleSpecError(te.what());
+        isc_throw(ModuleSpecError, te.what());
     }
 }
 }
@@ -314,14 +315,14 @@ moduleSpecFromFile(const std::string& file_name, const bool check)
     if (!file) {
         std::stringstream errs;
         errs << "Error opening " << file_name << ": " << strerror(errno);
-        throw ModuleSpecError(errs.str());
+        isc_throw(ModuleSpecError, errs.str());
     }
 
     ConstElementPtr module_spec_element = Element::fromJSON(file, file_name);
     if (module_spec_element->contains("module_spec")) {
         return (ModuleSpec(module_spec_element->get("module_spec"), check));
     } else {
-        throw ModuleSpecError("No module_spec in specification");
+        isc_throw(ModuleSpecError, "No module_spec in specification");
     }
 }
 
@@ -333,7 +334,7 @@ moduleSpecFromFile(std::ifstream& in, const bool check)
     if (module_spec_element->contains("module_spec")) {
         return (ModuleSpec(module_spec_element->get("module_spec"), check));
     } else {
-        throw ModuleSpecError("No module_spec in specification");
+        isc_throw(ModuleSpecError, "No module_spec in specification");
     }
 }
 
@@ -466,7 +467,6 @@ ModuleSpec::validateSpecList(ConstElementPtr spec, ConstElementPtr data,
                                const bool full, ElementPtr errors) const
 {
     bool validated = true;
-    std::string cur_item_name;
     BOOST_FOREACH(ConstElementPtr cur_spec_el, spec->listValue()) {
         if (!validateSpec(cur_spec_el, data, full, errors)) {
             validated = false;
diff --git a/src/lib/config/module_spec.h b/src/lib/config/module_spec.h
index ce3762f..27dcfe3 100644
--- a/src/lib/config/module_spec.h
+++ b/src/lib/config/module_spec.h
@@ -26,15 +26,11 @@ namespace isc { namespace config {
     /// A standard ModuleSpec exception that is thrown when a
     /// specification is not in the correct form.
     ///
-    /// TODO: use jinmei's exception class as a base and not c_str in
-    /// what() there
-    class ModuleSpecError : public std::exception {
+    class ModuleSpecError : public isc::Exception {
     public:
-        ModuleSpecError(std::string m = "Module specification is invalid") : msg(m) {}
-        ~ModuleSpecError() throw() {}
-        const char* what() const throw() { return (msg.c_str()); }
-    private:
-        std::string msg;
+        ModuleSpecError(const char* file, size_t line,
+                        const char* what = "Module specification is invalid") :
+            isc::Exception(file, line, what) {}
     };
 
     ///
diff --git a/src/lib/config/tests/.gitignore b/src/lib/config/tests/.gitignore
new file mode 100644
index 0000000..abdfa8a
--- /dev/null
+++ b/src/lib/config/tests/.gitignore
@@ -0,0 +1,2 @@
+/data_def_unittests_config.h
+/run_unittests
diff --git a/src/lib/config/tests/testdata/.gitignore b/src/lib/config/tests/testdata/.gitignore
new file mode 100644
index 0000000..1c67281
--- /dev/null
+++ b/src/lib/config/tests/testdata/.gitignore
@@ -0,0 +1 @@
+/b10-config.db
diff --git a/src/lib/cryptolink/tests/.gitignore b/src/lib/cryptolink/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/cryptolink/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/datasrc/.gitignore b/src/lib/datasrc/.gitignore
new file mode 100644
index 0000000..05c761e
--- /dev/null
+++ b/src/lib/datasrc/.gitignore
@@ -0,0 +1,4 @@
+/datasrc_messages.cc
+/datasrc_messages.h
+/datasrc_config.h
+/datasrc_config.h.pre
diff --git a/src/lib/datasrc/Makefile.am b/src/lib/datasrc/Makefile.am
index b6c314c..2cdb8ea 100644
--- a/src/lib/datasrc/Makefile.am
+++ b/src/lib/datasrc/Makefile.am
@@ -7,10 +7,10 @@ AM_CPPFLAGS += $(SQLITE_CFLAGS)
 
 AM_CXXFLAGS = $(B10_CXXFLAGS)
 
-pkglibexecdir = $(libexecdir)/@PACKAGE@/backends
+pkglibdir = $(libexecdir)/@PACKAGE@/backends
 
 datasrc_config.h: datasrc_config.h.pre
-	$(SED) -e "s|@@PKGLIBEXECDIR@@|$(pkglibexecdir)|" datasrc_config.h.pre >$@
+	$(SED) -e "s|@@PKGLIBDIR@@|$(pkglibdir)|" datasrc_config.h.pre >$@
 
 CLEANFILES = *.gcno *.gcda datasrc_messages.h datasrc_messages.cc
 CLEANFILES += datasrc_config.h
@@ -21,27 +21,31 @@ libdatasrc_la_SOURCES += static_datasrc.h static_datasrc.cc
 libdatasrc_la_SOURCES += sqlite3_datasrc.h sqlite3_datasrc.cc
 libdatasrc_la_SOURCES += query.h query.cc
 libdatasrc_la_SOURCES += cache.h cache.cc
+libdatasrc_la_SOURCES += rbnode_rrset.h
 libdatasrc_la_SOURCES += rbtree.h
 libdatasrc_la_SOURCES += zonetable.h zonetable.cc
-libdatasrc_la_SOURCES += zone.h
+libdatasrc_la_SOURCES += zone.h zone_finder_context.cc
 libdatasrc_la_SOURCES += result.h
 libdatasrc_la_SOURCES += logger.h logger.cc
 libdatasrc_la_SOURCES += client.h iterator.h
 libdatasrc_la_SOURCES += database.h database.cc
 libdatasrc_la_SOURCES += factory.h factory.cc
 nodist_libdatasrc_la_SOURCES = datasrc_messages.h datasrc_messages.cc
+libdatasrc_la_LDFLAGS = -no-undefined -version-info 1:0:1
 
-pkglibexec_LTLIBRARIES =  sqlite3_ds.la memory_ds.la
+pkglib_LTLIBRARIES =  sqlite3_ds.la memory_ds.la
 
 sqlite3_ds_la_SOURCES = sqlite3_accessor.h sqlite3_accessor.cc
-sqlite3_ds_la_LDFLAGS = -module
+sqlite3_ds_la_SOURCES += sqlite3_accessor_link.cc
+sqlite3_ds_la_LDFLAGS = -module -avoid-version
 sqlite3_ds_la_LDFLAGS += -no-undefined -version-info 1:0:0
 sqlite3_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 sqlite3_ds_la_LIBADD += libdatasrc.la
 sqlite3_ds_la_LIBADD += $(SQLITE_LIBS)
 
 memory_ds_la_SOURCES = memory_datasrc.h memory_datasrc.cc
-memory_ds_la_LDFLAGS = -module
+memory_ds_la_SOURCES += memory_datasrc_link.cc
+memory_ds_la_LDFLAGS = -module -avoid-version
 memory_ds_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 memory_ds_la_LIBADD += libdatasrc.la
 
diff --git a/src/lib/datasrc/data_source.cc b/src/lib/datasrc/data_source.cc
index 94dec89..a713b82 100644
--- a/src/lib/datasrc/data_source.cc
+++ b/src/lib/datasrc/data_source.cc
@@ -384,7 +384,8 @@ doQueryTask(QueryTask& task, ZoneInfo& zoneinfo, RRsetList& target) {
     const Name* const zonename = zoneinfo.getEnclosingZone();
     if (ds == NULL) {
         task.flags |= DataSrc::NO_SUCH_ZONE;
-        logger.info(DATASRC_QUERY_NO_ZONE).arg(task.qname).arg(task.qclass);
+        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_QUERY_NO_ZONE).
+            arg(task.qname).arg(task.qclass);
         return (DataSrc::SUCCESS);
     }
 
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index 139576a..7b271f1 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -27,15 +27,18 @@
 #include <dns/rrset.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/nsec3hash.h>
 
 #include <datasrc/data_source.h>
 #include <datasrc/logger.h>
 
 #include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
 
 using namespace isc::dns;
 using namespace std;
 using namespace isc::dns::rdata;
+using namespace boost;
 
 namespace isc {
 namespace datasrc {
@@ -177,15 +180,17 @@ private:
 DatabaseClient::Finder::FoundRRsets
 DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
                                   bool check_ns, const string* construct_name,
-                                  bool any)
+                                  bool any,
+                                  DatabaseAccessor::IteratorContextPtr context)
 {
     RRsigStore sig_store;
     bool records_found = false;
     std::map<RRType, RRsetPtr> result;
 
-    // Request the context
-    DatabaseAccessor::IteratorContextPtr
-        context(accessor_->getRecords(name, zone_id_));
+    // Request the context in case we didn't get one
+    if (!context) {
+        context = accessor_->getRecords(name, zone_id_);
+    }
     // It must not return NULL, that's a bug of the implementation
     if (!context) {
         isc_throw(isc::Unexpected, "Iterator context null at " + name);
@@ -286,13 +291,11 @@ DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
          i != result.end(); ++ i) {
         sig_store.appendSignatures(i->second);
     }
-
     if (records_found && any) {
         result[RRType::ANY()] = RRsetPtr();
         // These will be sitting on the other RRsets.
         result.erase(RRType::RRSIG());
     }
-
     return (FoundRRsets(records_found, result));
 }
 
@@ -318,6 +321,30 @@ namespace {
 typedef std::set<RRType> WantedTypes;
 
 const WantedTypes&
+NSEC3_TYPES() {
+    static bool initialized(false);
+    static WantedTypes result;
+
+    if (!initialized) {
+        result.insert(RRType::NSEC3());
+        initialized = true;
+    }
+    return (result);
+}
+
+const WantedTypes&
+NSEC3PARAM_TYPES() {
+    static bool initialized(false);
+    static WantedTypes result;
+
+    if (!initialized) {
+        result.insert(RRType::NSEC3PARAM());
+        initialized = true;
+    }
+    return (result);
+}
+
+const WantedTypes&
 NSEC_TYPES() {
     static bool initialized(false);
     static WantedTypes result;
@@ -355,56 +382,20 @@ FINAL_TYPES() {
     }
     return (result);
 }
-
-}
-
-ConstRRsetPtr
-DatabaseClient::Finder::findNSECCover(const Name& name) {
-    try {
-        // Which one should contain the NSEC record?
-        const Name coverName(findPreviousName(name));
-        // Get the record and copy it out
-        const FoundRRsets found = getRRsets(coverName.toText(), NSEC_TYPES(),
-                                            coverName != getOrigin());
-        const FoundIterator
-            nci(found.second.find(RRType::NSEC()));
-        if (nci != found.second.end()) {
-            return (nci->second);
-        } else {
-            // The previous doesn't contain NSEC.
-            // Badly signed zone or a bug?
-
-            // FIXME: Currently, if the zone is not signed, we could get
-            // here. In that case we can't really throw, but for now, we can't
-            // recognize it. So we don't throw at all, enable it once
-            // we have a is_signed flag or something.
-#if 0
-            isc_throw(DataSourceError, "No NSEC in " +
-                      coverName.toText() + ", but it was "
-                      "returned as previous - "
-                      "accessor error? Badly signed zone?");
-#endif
-        }
-    }
-    catch (const isc::NotImplemented&) {
-        // Well, they want DNSSEC, but there is no available.
-        // So we don't provide anything.
-        LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
-            arg(accessor_->getDBName()).arg(name);
-    }
-    // We didn't find it, return nothing
-    return (ConstRRsetPtr());
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 DatabaseClient::Finder::findAll(const isc::dns::Name& name,
                                 std::vector<isc::dns::ConstRRsetPtr>& target,
                                 const FindOptions options)
 {
-    return (findInternal(name, RRType::ANY(), &target, options));
+    return (ZoneFinderContextPtr(new Context(*this, options,
+                                             findInternal(name, RRType::ANY(),
+                                                          &target, options),
+                                             target)));
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 DatabaseClient::Finder::find(const isc::dns::Name& name,
                              const isc::dns::RRType& type,
                              const FindOptions options)
@@ -412,7 +403,9 @@ DatabaseClient::Finder::find(const isc::dns::Name& name,
     if (type == RRType::ANY()) {
         isc_throw(isc::Unexpected, "Use findAll to answer ANY");
     }
-    return (findInternal(name, type, NULL, options));
+    return (ZoneFinderContextPtr(new Context(*this, options,
+                                             findInternal(name, type, NULL,
+                                                          options))));
 }
 
 DatabaseClient::Finder::DelegationSearchResult
@@ -573,11 +566,11 @@ DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
 // covering NSEC record.
 //
 // If none of the above applies in any level, the search fails with NXDOMAIN.
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findWildcardMatch(
-    const isc::dns::Name& name, const isc::dns::RRType& type,
-    const FindOptions options, const DelegationSearchResult& dresult,
-    std::vector<isc::dns::ConstRRsetPtr>* target)
+    const Name& name, const RRType& type, const FindOptions options,
+    const DelegationSearchResult& dresult, vector<ConstRRsetPtr>* target,
+    FindDNSSECContext& dnssec_ctx)
 {
     // Note that during the search we are going to search not only for the
     // requested type, but also for types that indicate a delegation -
@@ -616,13 +609,12 @@ DatabaseClient::Finder::findWildcardMatch(
                           DATASRC_DATABASE_WILDCARD_CANCEL_NS).
                     arg(accessor_->getDBName()).arg(wildcard).
                     arg(dresult.first_ns->getName());
-                return (FindResult(DELEGATION, dresult.first_ns));
-
+                return (ResultContext(DELEGATION, dresult.first_ns));
             } else if (!hasSubdomains(name.split(i - 1).toText())) {
                 // The wildcard match is the best one, find the final result
                 // at it.  Note that wildcard should never be the zone origin.
-                return (findOnNameResult(name, type, options, false,
-                                         found, &wildcard, target));
+                return (findOnNameResult(name, type, options, false, found,
+                                         &wildcard, target, dnssec_ctx));
             } else {
 
                 // more specified match found, cancel wildcard match
@@ -630,7 +622,7 @@ DatabaseClient::Finder::findWildcardMatch(
                           DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
                     arg(accessor_->getDBName()).arg(wildcard).
                     arg(name).arg(superdomain);
-                return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+                return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
             }
 
         } else if (hasSubdomains(wildcard)) {
@@ -638,22 +630,19 @@ DatabaseClient::Finder::findWildcardMatch(
             LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                       DATASRC_DATABASE_WILDCARD_EMPTY).
                 arg(accessor_->getDBName()).arg(wildcard).arg(name);
-            if ((options & FIND_DNSSEC) != 0) {
-                ConstRRsetPtr nsec = findNSECCover(Name(wildcard));
-                if (nsec) {
-                    return (FindResult(NXRRSET, nsec,
-                                       RESULT_WILDCARD | RESULT_NSEC_SIGNED));
-                }
-            }
-            return (FindResult(NXRRSET, ConstRRsetPtr(), RESULT_WILDCARD));
+            const FindResultFlags flags = (RESULT_WILDCARD |
+                                           dnssec_ctx.getResultFlags());
+            return (ResultContext(NXRRSET,
+                                  dnssec_ctx.getDNSSECRRset(Name(wildcard),
+                                                            true), flags));
         }
     }
 
     // Nothing found at any level.
-    return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+    return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::logAndCreateResult(
     const Name& name, const string* wildname, const RRType& type,
     ZoneFinder::Result code, ConstRRsetPtr rrset,
@@ -680,10 +669,125 @@ DatabaseClient::Finder::logAndCreateResult(
                 arg(getClass()).arg(*wildname);
         }
     }
-    return (ZoneFinder::FindResult(code, rrset, flags));
+    return (ResultContext(code, rrset, flags));
+}
+
+DatabaseClient::Finder::FindDNSSECContext::FindDNSSECContext(
+    DatabaseClient::Finder& finder,
+    const FindOptions options) :
+    finder_(finder),
+    need_dnssec_((options & FIND_DNSSEC) != 0),
+    is_nsec3_(false),
+    is_nsec_(false),
+    probed_(false)
+{}
+
+void
+DatabaseClient::Finder::FindDNSSECContext::probe() {
+    if (!probed_) {
+        probed_ = true;
+        if (need_dnssec_) {
+            // If an NSEC3PARAM RR exists at the zone apex, it's quite likely
+            // that the zone is signed with NSEC3.  (If not the zone is more
+            // or less broken, but it's caller's responsibility how to handle
+            // such cases).
+            const string origin = finder_.getOrigin().toText();
+            const FoundRRsets nsec3_found =
+                finder_.getRRsets(origin, NSEC3PARAM_TYPES(), false);
+            const FoundIterator nfi=
+                nsec3_found.second.find(RRType::NSEC3PARAM());
+            is_nsec3_ = (nfi != nsec3_found.second.end());
+
+            // Likewise for NSEC, depending on the apex has an NSEC RR.
+            // If we know the zone is NSEC3-signed, however, we don't bother
+            // to check that.  This is aligned with the transition guideline
+            // described in Section 10.4 of RFC 5155.
+            if (!is_nsec3_) {
+                const FoundRRsets nsec_found =
+                    finder_.getRRsets(origin, NSEC_TYPES(), false);
+                const FoundIterator nfi =
+                    nsec_found.second.find(RRType::NSEC());
+                is_nsec_ = (nfi != nsec_found.second.end());
+            }
+        }
+    }
+}
+
+bool
+DatabaseClient::Finder::FindDNSSECContext::isNSEC3() {
+    if (!probed_) {
+        probe();
+    }
+    return (is_nsec3_);
+}
+
+bool
+DatabaseClient::Finder::FindDNSSECContext::isNSEC() {
+    if (!probed_) {
+        probe();
+    }
+    return (is_nsec_);
+}
+
+isc::dns::ConstRRsetPtr
+DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(
+    const FoundRRsets& found_set)
+{
+    if (!isNSEC()) {
+        return (ConstRRsetPtr());
+    }
+
+    const FoundIterator nci = found_set.second.find(RRType::NSEC());
+    if (nci != found_set.second.end()) {
+        return (nci->second);
+    } else {
+        return (ConstRRsetPtr());
+    }
+}
+
+isc::dns::ConstRRsetPtr
+DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
+                                                          bool covering)
+{
+    if (!isNSEC()) {
+        return (ConstRRsetPtr());
+    }
+
+    try {
+        const Name& nsec_name =
+            covering ? finder_.findPreviousName(name) : name;
+        const bool need_nscheck = (nsec_name != finder_.getOrigin());
+        const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
+                                                    NSEC_TYPES(),
+                                                    need_nscheck);
+        const FoundIterator nci = found.second.find(RRType::NSEC());
+        if (nci != found.second.end()) {
+            return (nci->second);
+        }
+    } catch (const isc::NotImplemented&) {
+        // This happens when the underlying database accessor doesn't support
+        // findPreviousName() (it probably doesn't support DNSSEC at all) but
+        // there is somehow an NSEC RR at the zone apex.  We log the fact but
+        // otherwise let the caller decide what to do (so, for example, a
+        // higher level query processing won't completely fail but can return
+        // anything it can get).
+        LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
+            arg(finder_.accessor_->getDBName()).arg(name);
+    }
+    return (ConstRRsetPtr());
 }
 
-ZoneFinder::FindResult
+ZoneFinder::FindResultFlags
+DatabaseClient::Finder::FindDNSSECContext::getResultFlags() {
+    if (isNSEC3()) {
+        return (RESULT_NSEC3_SIGNED);
+    } else if (isNSEC()) {
+        return (RESULT_NSEC_SIGNED);
+    }
+    return (RESULT_DEFAULT);
+}
+
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findOnNameResult(const Name& name,
                                          const RRType& type,
                                          const FindOptions options,
@@ -691,28 +795,22 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                          const FoundRRsets& found,
                                          const string* wildname,
                                          std::vector<isc::dns::ConstRRsetPtr>*
-                                         target)
+                                         target, FindDNSSECContext& dnssec_ctx)
 {
     const bool wild = (wildname != NULL);
-    FindResultFlags flags = wild ? RESULT_WILDCARD : RESULT_DEFAULT;
+    // For wildcard case with DNSSEC required, the caller would need to
+    // know whether it's NSEC or NSEC3 signed.  getResultFlags returns
+    // appropriate flag based on the query context and zone status.
+    const FindResultFlags flags =
+        wild ? (RESULT_WILDCARD | dnssec_ctx.getResultFlags()) : RESULT_DEFAULT;
 
     // Get iterators for the different types of records we are interested in -
     // CNAME, NS and Wanted types.
     const FoundIterator nsi(found.second.find(RRType::NS()));
     const FoundIterator cni(found.second.find(RRType::CNAME()));
     const FoundIterator wti(found.second.find(type));
-    // For wildcard case with DNSSEC required, the caller would need to know
-    // whether it's NSEC or NSEC3 signed.  So we need to do an additional
-    // search here, even though the NSEC RR may not be returned.
-    // TODO: this part should be revised when we support NSEC3; ideally we
-    // should use more effective and efficient way to identify (whether and)
-    // in which way the zone is signed.
-    if (wild && (options & FIND_DNSSEC) != 0 &&
-        found.second.find(RRType::NSEC()) != found.second.end()) {
-        flags = flags | RESULT_NSEC_SIGNED;
-    }
-
-    if (!is_origin && ((options & FIND_GLUE_OK) == 0) &&
+
+    if (!is_origin && (options & FIND_GLUE_OK) == 0 &&
         nsi != found.second.end()) {
         // A NS RRset was found at the domain we were searching for.  As it is
         // not at the origin of the zone, it is a delegation and indicates that
@@ -739,7 +837,6 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
                                    wild ? DATASRC_DATABASE_WILDCARD_CNAME :
                                    DATASRC_DATABASE_FOUND_CNAME,
                                    flags));
-
     } else if (wti != found.second.end()) {
         bool any(type == RRType::ANY());
         isc::log::MessageID lid(wild ? DATASRC_DATABASE_WILDCARD_MATCH :
@@ -771,43 +868,29 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
     // provide the NSEC records.  If it's for wildcard, we need to get the
     // NSEC records in the name of the wildcard, not the substituted one,
     // so we need to search the tree again.
-    ConstRRsetPtr nsec_rrset;   // possibly used with DNSSEC, otherwise NULL
-    if ((options & FIND_DNSSEC) != 0) {
-        if (wild) {
-            const FoundRRsets wfound = getRRsets(*wildname, NSEC_TYPES(),
-                                                 true);
-            const FoundIterator nci = wfound.second.find(RRType::NSEC());
-            if (nci != wfound.second.end()) {
-                nsec_rrset = nci->second;
-            }
-        } else {
-            const FoundIterator nci = found.second.find(RRType::NSEC());
-            if (nci != found.second.end()) {
-                nsec_rrset = nci->second;
-            }
-        }
-    }
-    if (nsec_rrset) {
+    const ConstRRsetPtr dnssec_rrset =
+        wild ? dnssec_ctx.getDNSSECRRset(Name(*wildname), false) :
+        dnssec_ctx.getDNSSECRRset(found);
+    if (dnssec_rrset) {
         // This log message covers both normal and wildcard cases, so we pass
         // NULL for 'wildname'.
-        return (logAndCreateResult(name, NULL, type, NXRRSET, nsec_rrset,
+        return (logAndCreateResult(name, NULL, type, NXRRSET, dnssec_rrset,
                                    DATASRC_DATABASE_FOUND_NXRRSET_NSEC,
                                    flags | RESULT_NSEC_SIGNED));
     }
-    return (logAndCreateResult(name, wildname, type, NXRRSET, nsec_rrset,
+    return (logAndCreateResult(name, wildname, type, NXRRSET, dnssec_rrset,
                                wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
-                               DATASRC_DATABASE_FOUND_NXRRSET, flags));
+                               DATASRC_DATABASE_FOUND_NXRRSET,
+                               flags | dnssec_ctx.getResultFlags()));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
                                          FindOptions options,
                                          const DelegationSearchResult& dresult,
                                          std::vector<isc::dns::ConstRRsetPtr>*
-                                         target)
+                                         target, FindDNSSECContext& dnssec_ctx)
 {
-    const bool dnssec_data = ((options & FIND_DNSSEC) != 0);
-
     // On entry to this method, we know that the database doesn't have any
     // entry for this name.  Before returning NXDOMAIN, we need to check
     // for special cases.
@@ -819,19 +902,18 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
         LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                   DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
             arg(accessor_->getDBName()).arg(name);
-        const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
-            ConstRRsetPtr();
-        return (FindResult(NXRRSET, nsec,
-                           nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+        return (ResultContext(NXRRSET, dnssec_ctx.getDNSSECRRset(name, true),
+                              dnssec_ctx.getResultFlags()));
     } else if ((options & NO_WILDCARD) == 0) {
         // It's not an empty non-terminal and wildcard matching is not
         // disabled, so check for wildcards. If there is a wildcard match
         // (i.e. all results except NXDOMAIN) return it; otherwise fall
         // through to the NXDOMAIN case below.
-        const ZoneFinder::FindResult wresult =
-            findWildcardMatch(name, type, options, dresult, target);
-        if (wresult.code != NXDOMAIN) {
-            return (wresult);
+        const ResultContext wcontext =
+            findWildcardMatch(name, type, options, dresult, target,
+                              dnssec_ctx);
+        if (wcontext.code != NXDOMAIN) {
+            return (wcontext);
         }
     }
 
@@ -839,13 +921,11 @@ DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
     // NSEC records if requested).
     LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
               arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
-    const ConstRRsetPtr nsec = dnssec_data ? findNSECCover(name) :
-        ConstRRsetPtr();
-    return (FindResult(NXDOMAIN, nsec,
-                       nsec ? RESULT_NSEC_SIGNED : RESULT_DEFAULT));
+    return (ResultContext(NXDOMAIN, dnssec_ctx.getDNSSECRRset(name, true),
+                          dnssec_ctx.getResultFlags()));
 }
 
-ZoneFinder::FindResult
+ZoneFinder::ResultContext
 DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
                                      std::vector<ConstRRsetPtr>* target,
                                      const FindOptions options)
@@ -860,7 +940,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
         name.compare(getOrigin()).getRelation();
     if (reln != NameComparisonResult::SUBDOMAIN &&
         reln != NameComparisonResult::EQUAL) {
-        return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+        isc_throw(OutOfZone, name.toText() << " not in " << getOrigin());
     }
 
     // First, go through all superdomains from the origin down, searching for
@@ -877,7 +957,7 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     const DelegationSearchResult dresult = findDelegationPoint(name, options);
     if (dresult.rrset) {
         // In this case no special flags are needed.
-        return (FindResult(dresult.code, dresult.rrset));
+        return (ResultContext(dresult.code, dresult.rrset));
     }
 
     // If there is no delegation, look for the exact match to the request
@@ -892,23 +972,134 @@ DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
     const FoundRRsets found = getRRsets(name.toText(), final_types,
                                         !is_origin, NULL,
                                         type == RRType::ANY());
-
+    FindDNSSECContext dnssec_ctx(*this, options);
     if (found.first) {
         // Something found at the domain name.  Look into it further to get
         // the final result.
         return (findOnNameResult(name, type, options, is_origin, found, NULL,
-                                 target));
+                                 target, dnssec_ctx));
     } else {
         // Did not find anything at all at the domain name, so check for
         // subdomains or wildcards.
-        return (findNoNameResult(name, type, options, dresult, target));
+        return (findNoNameResult(name, type, options, dresult, target,
+                                 dnssec_ctx));
     }
 }
 
+// The behaviour is inspired by the one in the in-memory implementation.
 ZoneFinder::FindNSEC3Result
-DatabaseClient::Finder::findNSEC3(const Name&, bool) {
-    isc_throw(NotImplemented, "findNSEC3 is not yet implemented for database "
-              "data source");
+DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3).arg(name).
+        arg(recursive ? "recursive" : "non-recursive");
+
+    // First, validate the input
+    const NameComparisonResult cmp_result(name.compare(getOrigin()));
+    if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
+        cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
+        isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: " <<
+                  name << ", zone: " << getOrigin() << "/" << getClass());
+    }
+
+    // Now, we need to get the NSEC3 params from the apex and create the hash
+    // creator for it.
+    const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
+                                 NSEC3PARAM_TYPES(), false));
+    const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
+    if (!nsec3param.first || param == nsec3param.second.end()) {
+        // No NSEC3 params? :-(
+        isc_throw(DataSourceError, "findNSEC3 attempt for non NSEC3 signed " <<
+                  "zone: " << getOrigin() << "/" << getClass());
+    }
+    // This takes the RRset received from the find method, takes the first RR
+    // in it, casts it to NSEC3PARAM (as it should be that one) and then creates
+    // the hash calculator class from it.
+    const scoped_ptr<NSEC3Hash> calculator(NSEC3Hash::create(
+        dynamic_cast<const generic::NSEC3PARAM&>(
+            param->second->getRdataIterator()->getCurrent())));
+
+    // Few shortcut variables
+    const unsigned olabels(getOrigin().getLabelCount());
+    const unsigned qlabels(name.getLabelCount());
+    const string otext(getOrigin().toText());
+
+    // This will be set to the one covering the query name
+    ConstRRsetPtr covering_proof;
+
+    // We keep stripping the leftmost label until we find something.
+    // In case it is recursive, we'll exit the loop at the first iteration.
+    for (unsigned labels(qlabels); labels >= olabels; -- labels) {
+        const string hash(calculator->calculate(labels == qlabels ? name :
+                                                name.split(qlabels - labels,
+                                                           labels)));
+        // Get the exact match for the name.
+        LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3_TRYHASH).
+            arg(name).arg(labels).arg(hash);
+
+        DatabaseAccessor::IteratorContextPtr
+            context(accessor_->getNSEC3Records(hash, zone_id_));
+
+        if (!context) {
+            isc_throw(Unexpected, "Iterator context null for hash " + hash);
+        }
+
+        const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
+                                          false, NULL, false, context));
+
+        if (nsec3.first) {
+            // We found an exact match against the current label.
+            const FoundIterator it(nsec3.second.find(RRType::NSEC3()));
+            if (it == nsec3.second.end()) {
+                isc_throw(DataSourceError, "Hash " + hash +
+                          "exists, but no NSEC3 there");
+            }
+
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_DATABASE_FINDNSEC3_MATCH).arg(name).arg(labels).
+                arg(*it->second);
+            // Yes, we win
+            return (FindNSEC3Result(true, labels, it->second, covering_proof));
+        } else {
+            // There's no exact match. We try a previous one. We must find it
+            // (if the zone is properly signed).
+            const string prevHash(accessor_->findPreviousNSEC3Hash(zone_id_,
+                                                                   hash));
+            LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                      DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV).arg(name).
+                arg(labels).arg(prevHash);
+            context = accessor_->getNSEC3Records(prevHash, zone_id_);
+            const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
+                                                   NSEC3_TYPES(), false, NULL,
+                                                   false, context));
+
+            if (!prev_nsec3.first) {
+                isc_throw(DataSourceError, "Hash " + prevHash + " returned "
+                          "from findPreviousNSEC3Hash, but it is empty");
+            }
+            const FoundIterator
+                prev_it(prev_nsec3.second.find(RRType::NSEC3()));
+            if (prev_it == prev_nsec3.second.end()) {
+                isc_throw(DataSourceError, "The previous hash " + prevHash +
+                          "exists, but does not contain the NSEC3");
+            }
+
+            covering_proof = prev_it->second;
+            // In case it is recursive, we try to get an exact match a level
+            // up. If it is not recursive, the caller is ok with a covering
+            // one, so we just return it.
+            if (!recursive) {
+                LOG_DEBUG(logger, DBG_TRACE_BASIC,
+                          DATASRC_DATABASE_FINDNSEC3_COVER).arg(name).
+                    arg(labels).arg(*covering_proof);
+                return (FindNSEC3Result(false, labels, covering_proof,
+                                        ConstRRsetPtr()));
+            }
+        }
+    }
+
+    // The zone must contain at least the apex and that one should match
+    // exactly. If that doesn't happen, we have a problem.
+    isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely a "
+              "broken NSEC3 zone: " << otext << "/" << getClass());
 }
 
 Name
@@ -918,16 +1109,9 @@ DatabaseClient::Finder::findPreviousName(const Name& name) const {
     try {
         return (Name(str));
     }
-
-    // To avoid having the same code many times, we just catch all the
-    // exceptions and handle them in a common code below
-    catch (const isc::dns::EmptyLabel&) {}
-    catch (const isc::dns::TooLongLabel&) {}
-    catch (const isc::dns::BadLabelType&) {}
-    catch (const isc::dns::BadEscape&) {}
-    catch (const isc::dns::TooLongName&) {}
-    catch (const isc::dns::IncompleteName&) {}
-    isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
+    catch (const isc::dns::NameParserException&) {
+        isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
+    }
 }
 
 Name
@@ -975,7 +1159,7 @@ public:
         // Find the SOA of the zone (may or may not succeed).  Note that
         // this must be done before starting the iteration context.
         soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
-            find(zone_name, RRType::SOA()).rrset;
+            find(zone_name, RRType::SOA())->rrset;
 
         // Request the context
         context_ = accessor_->getAllRecords(zone.second);
@@ -1007,28 +1191,44 @@ public:
             // At the end of zone
             accessor_->commit();
             ready_ = false;
-            LOG_DEBUG(logger, DBG_TRACE_DETAILED,
-                      DATASRC_DATABASE_ITERATE_END);
+            LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_END);
             return (ConstRRsetPtr());
         }
-        const string name_str(name_), rtype_str(rtype_), ttl(ttl_);
-        const Name name(name_str);
-        const RRType rtype(rtype_str);
-        RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl)));
-        while (data_ready_ && name_ == name_str && rtype_str == rtype_) {
-            if (ttl_ != ttl) {
-                if (ttl < ttl_) {
-                    ttl_ = ttl;
-                    rrset->setTTL(RRTTL(ttl));
-                }
-                LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH).
-                    arg(name_).arg(class_).arg(rtype_).arg(rrset->getTTL());
-            }
-            rrset->addRdata(rdata::createRdata(rtype, class_, rdata_));
+        const RRType rtype(rtype_txt_);
+        RRsetPtr rrset(new RRset(Name(name_txt_), class_, rtype,
+                                 RRTTL(ttl_txt_)));
+        // Remember the first RDATA of the RRset for comparison:
+        const ConstRdataPtr rdata_base = rdata_;
+        while (true) {
+            // Extend the RRset with the new RDATA.
+            rrset->addRdata(rdata_);
+
+            // Retrieve the next record from the database.  If we reach the
+            // end of the zone, done; if we were requested to separate all RRs,
+            // just remember this record and return the single RR.
             getData();
-            if (separate_rrs_) {
+            if (separate_rrs_ || !data_ready_) {
+                break;
+            }
+
+            // Check if the next record belongs to the same RRset.  If not,
+            // we are done.  The next RDATA has been stored in rdata_, which
+            // is used within this loop (if it belongs to the same RRset) or
+            // in the next call.
+            if (Name(name_txt_) != rrset->getName() ||
+                !isSameType(rtype, rdata_base, RRType(rtype_txt_), rdata_)) {
                 break;
             }
+
+            // Adjust TTL if necessary
+            const RRTTL next_ttl(ttl_txt_);
+            if (next_ttl != rrset->getTTL()) {
+                if (next_ttl < rrset->getTTL()) {
+                    rrset->setTTL(next_ttl);
+                }
+                LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH).
+                    arg(name_txt_).arg(class_).arg(rtype).arg(rrset->getTTL());
+            }
         }
         LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
             arg(rrset->getName()).arg(rrset->getType());
@@ -1036,14 +1236,34 @@ public:
     }
 
 private:
+    // Check two RDATA types are equivalent.  Basically it's a trivial
+    // comparison, but if both are of RRSIG, we should also compare the types
+    // covered.
+    static bool isSameType(RRType type1, ConstRdataPtr rdata1,
+                           RRType type2, ConstRdataPtr rdata2)
+    {
+        if (type1 != type2) {
+            return (false);
+        }
+        if (type1 == RRType::RRSIG()) {
+            return (dynamic_cast<const generic::RRSIG&>(*rdata1).typeCovered()
+                    == dynamic_cast<const generic::RRSIG&>(*rdata2).
+                    typeCovered());
+        }
+        return (true);
+    }
+
     // Load next row of data
     void getData() {
         string data[DatabaseAccessor::COLUMN_COUNT];
         data_ready_ = context_->getNext(data);
-        name_ = data[DatabaseAccessor::NAME_COLUMN];
-        rtype_ = data[DatabaseAccessor::TYPE_COLUMN];
-        ttl_ = data[DatabaseAccessor::TTL_COLUMN];
-        rdata_ = data[DatabaseAccessor::RDATA_COLUMN];
+        if (data_ready_) {
+            name_txt_ = data[DatabaseAccessor::NAME_COLUMN];
+            rtype_txt_ = data[DatabaseAccessor::TYPE_COLUMN];
+            ttl_txt_ = data[DatabaseAccessor::TTL_COLUMN];
+            rdata_ = rdata::createRdata(RRType(rtype_txt_), class_,
+                                        data[DatabaseAccessor::RDATA_COLUMN]);
+        }
     }
 
     // The dedicated accessor
@@ -1057,10 +1277,12 @@ private:
     // Status
     bool ready_, data_ready_;
     // Data of the next row
-    string name_, rtype_, rdata_, ttl_;
+    string name_txt_, rtype_txt_, ttl_txt_;
+    // RDATA of the next row
+    ConstRdataPtr rdata_;
     // Whether to modify differing TTL values, or treat a different TTL as
     // a different RRset
-    bool separate_rrs_;
+    const bool separate_rrs_;
 };
 
 }
@@ -1193,49 +1415,132 @@ DatabaseUpdater::validateAddOrDelete(const char* const op_str,
     }
 }
 
+// This is a helper class used in adding/deleting RRsets to/from a database.
+// The purpose of this class is to provide conversion interface from various
+// parameters of the RRset to corresponding textual representations that the
+// underlying database interface expects.  The necessary parameters and how
+// to convert them depend on several things, such as whether it's NSEC3 related
+// or not, or whether journaling is requested.  In order to avoid unnecessary
+// conversion, this class also performs the conversion in a lazy manner.
+// Also, in order to avoid redundant conversion when the conversion is
+// requested for the same parameter multiple times, it remembers the
+// conversion result first time, and reuses it for subsequent requests
+// (this implicitly assumes copying std::string objects is not very expensive;
+// this is often the case in some common implementations that have
+// copy-on-write semantics for the string class).
+class RRParameterConverter {
+public:
+    RRParameterConverter(const AbstractRRset& rrset) : rrset_(rrset)
+    {}
+    const string& getName() {
+        if (name_.empty()) {
+            name_ = rrset_.getName().toText();
+        }
+        return (name_);
+    }
+    const string& getNSEC3Name() {
+        if (nsec3_name_.empty()) {
+            nsec3_name_ = rrset_.getName().split(0, 1).toText(true);
+        }
+        return (nsec3_name_);
+    }
+    const string& getRevName() {
+        if (revname_.empty()) {
+            revname_ = rrset_.getName().reverse().toText();
+        }
+        return (revname_);
+    }
+    const string& getTTL() {
+        if (ttl_.empty()) {
+            ttl_ = rrset_.getTTL().toText();
+        }
+        return (ttl_);
+    }
+    const string& getType() {
+        if (type_.empty()) {
+            type_ = rrset_.getType().toText();
+        }
+        return (type_);
+    }
+
+private:
+    string name_;
+    string nsec3_name_;
+    string revname_;
+    string ttl_;
+    string type_;
+    const AbstractRRset& rrset_;
+};
+
+namespace {
+// A shared shortcut to detect if the given type of RDATA is NSEC3 or
+// RRSIG covering NSEC3.  RRSIG for NSEC3 should go to the (conceptual)
+// separate namespace, so we need to check the covered type.
+// Note: in principle the type covered should be the same for
+// all RDATA, but the RRset interface doesn't ensure that condition.
+// So we explicitly check that for every RDATA below.
+bool
+isNSEC3KindType(RRType rrtype, const Rdata& rdata) {
+    if (rrtype == RRType::NSEC3()) {
+        return (true);
+    }
+    if (rrtype == RRType::RRSIG() &&
+        dynamic_cast<const generic::RRSIG&>(rdata).typeCovered() ==
+        RRType::NSEC3())
+    {
+        return (true);
+    }
+    return (false);
+}
+}
+
 void
 DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
     validateAddOrDelete("add", rrset, DELETE, ADD);
 
     // It's guaranteed rrset has at least one RDATA at this point.
     RdataIteratorPtr it = rrset.getRdataIterator();
-
-    string columns[Accessor::ADD_COLUMN_COUNT]; // initialized with ""
-    columns[Accessor::ADD_NAME] = rrset.getName().toText();
-    columns[Accessor::ADD_REV_NAME] = rrset.getName().reverse().toText();
-    columns[Accessor::ADD_TTL] = rrset.getTTL().toText();
-    columns[Accessor::ADD_TYPE] = rrset.getType().toText();
-    string journal[Accessor::DIFF_PARAM_COUNT];
     if (journaling_) {
-        journal[Accessor::DIFF_NAME] = columns[Accessor::ADD_NAME];
-        journal[Accessor::DIFF_TYPE] = columns[Accessor::ADD_TYPE];
-        journal[Accessor::DIFF_TTL] = columns[Accessor::ADD_TTL];
         diff_phase_ = ADD;
         if (rrset.getType() == RRType::SOA()) {
-            serial_ =
-                dynamic_cast<const generic::SOA&>(it->getCurrent()).
+            serial_ = dynamic_cast<const generic::SOA&>(it->getCurrent()).
                 getSerial();
         }
     }
+
+    RRParameterConverter cvtr(rrset);
     for (; !it->isLast(); it->next()) {
+        const Rdata& rdata = it->getCurrent();
+        const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
+
+        string sigtype;
         if (rrset.getType() == RRType::RRSIG()) {
             // XXX: the current interface (based on the current sqlite3
             // data source schema) requires a separate "sigtype" column,
             // even though it won't be used in a newer implementation.
             // We should eventually clean up the schema design and simplify
             // the interface, but until then we have to conform to the schema.
-            const generic::RRSIG& rrsig_rdata =
-                dynamic_cast<const generic::RRSIG&>(it->getCurrent());
-            columns[Accessor::ADD_SIGTYPE] =
-                rrsig_rdata.typeCovered().toText();
+            sigtype = dynamic_cast<const generic::RRSIG&>(rdata).
+                typeCovered().toText();
         }
-        columns[Accessor::ADD_RDATA] = it->getCurrent().toText();
+        const string& rdata_txt = rdata.toText();
         if (journaling_) {
-            journal[Accessor::DIFF_RDATA] = columns[Accessor::ADD_RDATA];
+            const string journal[Accessor::DIFF_PARAM_COUNT] =
+                { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
             accessor_->addRecordDiff(zone_id_, serial_.getValue(),
                                      Accessor::DIFF_ADD, journal);
         }
-        accessor_->addRecordToZone(columns);
+        if (nsec3_type) {
+            const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] =
+                { cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(),
+                  rdata_txt };
+            accessor_->addNSEC3RecordToZone(nsec3_columns);
+        } else {
+            const string columns[Accessor::ADD_COLUMN_COUNT] =
+                { cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(),
+                  cvtr.getType(), sigtype, rdata_txt };
+            accessor_->addRecordToZone(columns);
+        }
     }
 }
 
@@ -1250,15 +1555,7 @@ DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
     validateAddOrDelete("delete", rrset, ADD, DELETE);
 
     RdataIteratorPtr it = rrset.getRdataIterator();
-
-    string params[Accessor::DEL_PARAM_COUNT]; // initialized with ""
-    params[Accessor::DEL_NAME] = rrset.getName().toText();
-    params[Accessor::DEL_TYPE] = rrset.getType().toText();
-    string journal[Accessor::DIFF_PARAM_COUNT];
     if (journaling_) {
-        journal[Accessor::DIFF_NAME] = params[Accessor::DEL_NAME];
-        journal[Accessor::DIFF_TYPE] = params[Accessor::DEL_TYPE];
-        journal[Accessor::DIFF_TTL] = rrset.getTTL().toText();
         diff_phase_ = DELETE;
         if (rrset.getType() == RRType::SOA()) {
             serial_ =
@@ -1266,14 +1563,27 @@ DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
                 getSerial();
         }
     }
+
+    RRParameterConverter cvtr(rrset);
     for (; !it->isLast(); it->next()) {
-        params[Accessor::DEL_RDATA] = it->getCurrent().toText();
+        const Rdata& rdata = it->getCurrent();
+        const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
+        const string& rdata_txt = it->getCurrent().toText();
+
         if (journaling_) {
-            journal[Accessor::DIFF_RDATA] = params[Accessor::DEL_RDATA];
+            const string journal[Accessor::DIFF_PARAM_COUNT] =
+                { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
             accessor_->addRecordDiff(zone_id_, serial_.getValue(),
                                      Accessor::DIFF_DELETE, journal);
         }
-        accessor_->deleteRecordInZone(params);
+        const string params[Accessor::DEL_PARAM_COUNT] =
+            { nsec3_type ? cvtr.getNSEC3Name() : cvtr.getName(),
+              cvtr.getType(), rdata_txt };
+        if (nsec3_type) {
+            accessor_->deleteNSEC3RecordInZone(params);
+        } else {
+            accessor_->deleteRecordInZone(params);
+        }
     }
 }
 
diff --git a/src/lib/datasrc/database.h b/src/lib/datasrc/database.h
index 4d58401..8083322 100644
--- a/src/lib/datasrc/database.h
+++ b/src/lib/datasrc/database.h
@@ -26,7 +26,7 @@
 
 #include <datasrc/data_source.h>
 #include <datasrc/client.h>
-#include <datasrc/client.h>
+#include <datasrc/zone.h>
 #include <datasrc/logger.h>
 
 #include <dns/name.h>
@@ -95,13 +95,36 @@ public:
         ADD_COLUMN_COUNT = 6 ///< Number of columns
     };
 
+    /// \brief Definitions of the fields to be passed to addNSEC3RecordToZone()
+    ///
+    /// Each derived implementation of addNSEC3RecordToZone() should expect
+    /// the "columns" array to be filled with the values as described in this
+    /// enumeration, in this order.
+    ///
+    /// Note that there is no "reversed name" column.  Since the conceptual
+    /// separate namespace for NSEC3 is very simplified and essentially only
+    /// consists of a single-label names, there is no need for using reversed
+    /// names to identify the "previous hash".
+    enum AddNSEC3RecordColumns {
+        ADD_NSEC3_HASH = 0, ///< The hash (1st) label of the owner name,
+                            ///< excluding the dot character
+        ADD_NSEC3_TTL = 1,  ///< The TTL of the record (in numeric form)
+        ADD_NSEC3_TYPE = 2, ///< The RRType of the record (either NSEC3 or
+                            ///< RRSIG for NSEC3)
+        ADD_NSEC3_RDATA = 3, ///< Full text representation of the record's
+                             ///< RDATA
+        ADD_NSEC3_COLUMN_COUNT = 4 ///< Number of columns
+    };
+
     /// \brief Definitions of the fields to be passed to deleteRecordInZone()
+    /// and deleteNSEC3RecordInZone()
     ///
     /// Each derived implementation of deleteRecordInZone() should expect
     /// the "params" array to be filled with the values as described in this
     /// enumeration, in this order.
     enum DeleteRecordParams {
         DEL_NAME = 0, ///< The owner name of the record (a domain name)
+                      ///< or the hash label for deleteNSEC3RecordInZone()
         DEL_TYPE = 1, ///< The RRType of the record (A/NS/TXT etc.)
         DEL_RDATA = 2, ///< Full text representation of the record's RDATA
         DEL_PARAM_COUNT = 3 ///< Number of parameters
@@ -234,6 +257,41 @@ public:
                                           int id,
                                           bool subdomains = false) const = 0;
 
+    /// \brief Creates an iterator context for the records of NSEC3 namespace
+    ///     for the given hash
+    ///
+    /// Returns an Iteratorcontextptr that contains all the records of the given
+    /// hash in the NSEC3 namespace of the given zone.
+    ///
+    /// The implementation of the iterator that is returned may leave the
+    /// NAME_COLUMN column of the array passed to getNext() untouched,
+    /// as that name is easy to construct on the caller side (both the
+    /// hash and the name of the zone is known). The SIGTYPE_COLUMN can
+    /// be omitted as well, as it would be always empty for NSEC3 RRs or
+    /// contained "NSEC3" in case of RRSIG RRs.
+    ///
+    /// The iterator will contain both the NSEC3 records and the corresponding
+    /// RRSIGs, in arbitrary order.
+    ///
+    /// The iterator might be empty (containing no RRs) in case the zone is not
+    /// signed by NSEC3.
+    ///
+    /// \note In case there are multiple NSEC3 chains and they collide
+    ///     (unlikely, but it can happen), this can return multiple NSEC3
+    ///     records.
+    /// \exception any Since any implementaion can be used, the caller should
+    ///     expect any exception to be thrown.
+    /// \exception isc::NotImplemented in case the database does not support
+    ///     NSEC3
+    ///
+    /// \param hash The hash part of the NSEC3 name (eg. for a name of NSEC3
+    ///     RKBUCQT8T78GV6QBCGBHCHC019LG73SJ.example.com., we the hash would be
+    ///     RKBUCQT8T78GV6QBCGBHCHC019LG73SJ).
+    /// \param id The id of te zone, as returned from getZone().
+    /// \return Newly created iterator context. Must not be NULL.
+    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
+                                               int id) const = 0;
+
     /// \brief Creates an iterator context for the whole zone.
     ///
     /// Returns an IteratorContextPtr that contains all records of the
@@ -397,6 +455,46 @@ public:
     virtual void addRecordToZone(
         const std::string (&columns)[ADD_COLUMN_COUNT]) = 0;
 
+    /// \brief Add a single NSEC3-related record to the zone to be updated.
+    ///
+    /// This method is similar to \c addRecordToZone(), but is expected to
+    /// be only used for NSEC3 RRs or RRSIG RRs that cover NSEC3.  In terms
+    /// of the DNS protocol, these types of RRs reside in a separate space
+    /// of the zone.  While this interface does not mandate a specific way
+    /// of implementing the separate namespaces in the underlying database,
+    /// it would be more convenient for the underlying implementation if the
+    /// interfaces are separated; for example, the implementation does not
+    /// have to examine the given data to identify the appropriate namespace.
+    ///
+    /// An implementation may choose to skip providing this interface if the
+    /// zones managed by that data source are known to not support NSEC3.
+    /// In that case the implementation should throw the
+    /// \c isc::NotImplemented exception.
+    ///
+    /// Note that the \c ADD_NSEC3_HASH column of \c columns is expected to
+    /// store only the hash label, not the entire owner name.  This is similar
+    /// to the \c hash parameter of \c getNSEC3Records().
+    ///
+    /// The RRs to be added using this method are expected to be limited to
+    /// NSEC3 or RRSIG RRs that cover NSEC3, but it's generally assumed to
+    /// be the caller's responsibility to ensure that; the implementation
+    /// is not required to check that condition.  The result of adding
+    /// unexpected type of RRs (and the result of subsequent lookups) is
+    /// undefined.
+    ///
+    /// Other general notes for \c addRecordToZone() also apply to this
+    /// method.
+    ///
+    /// \exception DataSourceError Invalid call without starting a transaction,
+    /// or other internal database error.
+    /// \exception isc::NotImplemented in case the database does not support
+    ///     NSEC3
+    ///
+    /// \param columns An array of strings that defines a record to be added
+    /// to the NSEC3 namespace of the zone.
+    virtual void addNSEC3RecordToZone(
+        const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]) = 0;
+
     /// \brief Delete a single record from the zone to be updated.
     ///
     /// This method provides a simple interface to delete a record
@@ -434,6 +532,31 @@ public:
     virtual void deleteRecordInZone(
         const std::string (&params)[DEL_PARAM_COUNT]) = 0;
 
+    /// \brief Delete a single NSEC3-related record from the zone to be
+    /// updated.
+    ///
+    /// This method is similar to \c deleteRecordInZone(), but is expected to
+    /// be only used for NSEC3 RRs or RRSIG RRs that cover NSEC3.  The
+    /// relationship between these two methods is similar to that between
+    /// \c addRecordToZone() and \c addNSEC3RecordToZone(), and the same
+    /// notes apply to this method.
+    ///
+    /// This method uses the same set of parameters to specify the record
+    /// to be deleted as \c deleteRecordInZone(), but the \c DEL_NAME column
+    /// is expected to only store the hash label of the owner name.
+    /// This is the same as \c ADD_NSEC3_HASH column for
+    /// \c addNSEC3RecordToZone().
+    ///
+    /// \exception DataSourceError Invalid call without starting a transaction,
+    /// or other internal database error.
+    /// \exception isc::NotImplemented in case the database does not support
+    ///     NSEC3
+    ///
+    /// \param params An array of strings that defines a record to be deleted
+    /// from the NSEC3 namespace of the zone.
+    virtual void deleteNSEC3RecordInZone(
+        const std::string (&params)[DEL_PARAM_COUNT]) = 0;
+
     /// \brief Start a general transaction.
     ///
     /// Each derived class version of this method starts a database
@@ -643,6 +766,34 @@ public:
     ///     apex of the zone).
     virtual std::string findPreviousName(int zone_id,
                                          const std::string& rname) const = 0;
+
+    /// \brief It returns the previous hash in the NSEC3 chain.
+    ///
+    /// This is used to find previous NSEC3 hashes, to find covering NSEC3 in
+    /// case none match exactly.
+    ///
+    /// In case a hash before the lowest or the lowest is provided,
+    /// this should return the largest one in the zone (NSEC3 needs a
+    /// wrap-around semantics).
+    ///
+    /// \param zone_id Specifies the zone to look into, as returned by getZone.
+    /// \param hash The hash to look before.
+    /// \return The nearest smaller hash than the provided one, or the largest
+    ///     hash in the zone if something smaller or equal to the lowest one
+    ///     is provided.
+    /// \note If the zone contains multiple NSEC3 chains, you should check that
+    ///     the returned result contains the NSEC3 for correct parameters. If
+    ///     not, query again and get something smaller - this will eventually
+    ///     get to the correct one. This interface and semantics might change
+    ///     in future.
+    ///
+    /// \throw DataSourceError if there's a problem with the database or if
+    ///     this zone is not signed with NSEC3.
+    /// \throw NotImplemented if this database doesn't support NSEC3.
+    /// \throw anything else, as this might be any implementation.
+    virtual std::string findPreviousNSEC3Hash(int zone_id,
+                                              const std::string& hash)
+        const = 0;
 };
 
 /// \brief Concrete data source client oriented at database backends.
@@ -673,6 +824,7 @@ public:
     DatabaseClient(isc::dns::RRClass rrclass,
                    boost::shared_ptr<DatabaseAccessor> accessor);
 
+
     /// \brief Corresponding ZoneFinder implementation
     ///
     /// The zone finder implementation for database data sources. Similarly
@@ -738,17 +890,19 @@ public:
         /// \param type The RRType to find
         /// \param options Options about how to search.
         ///     See ZoneFinder::FindOptions.
-        virtual FindResult find(const isc::dns::Name& name,
-                                const isc::dns::RRType& type,
-                                const FindOptions options = FIND_DEFAULT);
+        virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+                                          const isc::dns::RRType& type,
+                                          const FindOptions options =
+                                          FIND_DEFAULT);
         /// \brief Implementation of the ZoneFinder::findAll method.
         ///
         /// In short, it is mostly the same thing as find, but it returns all
         /// RRsets in the named node through the target parameter in successful
         /// case. It acts the same in the unsuccessful one.
-        virtual FindResult findAll(const isc::dns::Name& name,
-                                   std::vector<isc::dns::ConstRRsetPtr>& target,
-                                   const FindOptions options = FIND_DEFAULT);
+        virtual ZoneFinderContextPtr findAll(
+            const isc::dns::Name& name,
+            std::vector<isc::dns::ConstRRsetPtr>& target,
+            const FindOptions options = FIND_DEFAULT);
 
         /// \brief Implementation of ZoneFinder::findPreviousName method.
         virtual isc::dns::Name findPreviousName(const isc::dns::Name& query)
@@ -780,11 +934,13 @@ public:
         boost::shared_ptr<DatabaseAccessor> accessor_;
         const int zone_id_;
         const isc::dns::Name origin_;
+
         /// \brief Shortcut name for the result of getRRsets
         typedef std::pair<bool, std::map<dns::RRType, dns::RRsetPtr> >
             FoundRRsets;
         /// \brief Just shortcut for set of types
         typedef std::set<dns::RRType> WantedTypes;
+
         /// \brief Internal logit of find and findAll methods.
         ///
         /// Most of their handling is in the "error" cases and delegations
@@ -794,10 +950,12 @@ public:
         /// Parameters and behaviour is like of those combined together.
         /// Unexpected parameters, like type != ANY and having the target, are
         /// just that - unexpected and not checked.
-        FindResult findInternal(const isc::dns::Name& name,
-                                const isc::dns::RRType& type,
-                                std::vector<isc::dns::ConstRRsetPtr>* target,
-                                const FindOptions options = FIND_DEFAULT);
+        ResultContext findInternal(const isc::dns::Name& name,
+                                   const isc::dns::RRType& type,
+                                   std::vector<isc::dns::ConstRRsetPtr>*
+                                   target,
+                                   const FindOptions options = FIND_DEFAULT);
+
         /// \brief Searches database for RRsets of one domain.
         ///
         /// This method scans RRs of single domain specified by name and
@@ -819,6 +977,9 @@ public:
         ///     ones requested by types. It also puts a NULL pointer under the
         ///     ANY type into the result, if it finds any RRs at all, to easy the
         ///     identification of success.
+        /// \param srcContext This can be set to non-NULL value to override the
+        ///     iterator context used for obtaining the data. This can be used,
+        ///     for example, to get data from the NSEC3 namespace.
         /// \return A pair, where the first element indicates if the domain
         ///     contains any RRs at all (not only the requested, it may happen
         ///     this is set to true, but the second part is empty). The second
@@ -830,7 +991,122 @@ public:
         FoundRRsets getRRsets(const std::string& name,
                               const WantedTypes& types, bool check_ns,
                               const std::string* construct_name = NULL,
-                              bool any = false);
+                              bool any = false,
+                              DatabaseAccessor::IteratorContextPtr srcContext =
+                              DatabaseAccessor::IteratorContextPtr());
+
+        /// \brief DNSSEC related context for ZoneFinder::findInternal.
+        ///
+        /// This class is a helper for the ZoneFinder::findInternal method,
+        /// encapsulating DNSSEC related information and processing logic.
+        /// Specifically, it tells the finder whether the zone under search
+        /// is DNSSEC signed or not, and if it is, whether it's with NSEC or
+        /// with NSEC3.  It also provides a RRset DNSSEC proof RRset for some
+        /// specific situations (in practice, this means an NSEC RRs for
+        /// negative proof when they are needed and expected).
+        ///
+        /// The purpose of this class is to keep the main finder implementation
+        /// unaware of DNSSEC related details.  It's also intended to help
+        /// avoid unnecessary lookup for DNSSEC proof RRsets; this class
+        /// doesn't look into the DB for these RRsets unless it's known to
+        /// be needed.  The same optimization could be implemented in the
+        /// main code, but it will result in duplicate similar code logic
+        /// and make the code more complicated.  By encapsulating and unifying
+        /// the logic in a single separate class, we can keep the main
+        /// search logic readable.
+        class FindDNSSECContext {
+        public:
+            /// \brief Constructor for FindDNSSECContext class.
+            ///
+            /// This constructor doesn't involve any expensive operation such
+            /// as database lookups.  It only initializes some internal
+            /// states (in a cheap way) and remembers if DNSSEC proof
+            /// is requested.
+            ///
+            /// \param finder The Finder for the findInternal that uses this
+            /// context.
+            /// \param options Find options given to the finder.
+            FindDNSSECContext(Finder& finder, const FindOptions options);
+
+            /// \brief Return DNSSEC related result flags for the context.
+            ///
+            /// This method returns a FindResultFlags value related to
+            /// DNSSEC, based on the context.  If DNSSEC proof is requested
+            /// and the zone is signed with NSEC/NSEC3, it returns
+            /// RESULT_NSEC_SIGNED/RESULT_NSEC3_SIGNED, respectively;
+            /// otherwise it returns RESULT_DEFAULT.  So the caller can simply
+            /// take a logical OR for the returned value of this method and
+            /// whatever other flags it's going to set, without knowing
+            /// DNSSEC specific information.
+            ///
+            /// If it's not yet identified whether and how the zone is DNSSEC
+            /// signed at the time of the call, it now detects that via
+            /// database lookups (if necessary).  (And this is because why
+            /// this method cannot be a const member function).
+            ZoneFinder::FindResultFlags getResultFlags();
+
+            /// \brief Get DNSSEC negative proof for a given name.
+            ///
+            /// If the zone is considered NSEC-signed and the context
+            /// requested DNSSEC proofs, this method tries to find NSEC RRs
+            /// for the give name.  If \c covering is true, it means a
+            /// "no name" proof is requested, so it calls findPreviousName on
+            /// the given name and extracts an NSEC record on the result;
+            /// otherwise it tries to get NSEC RRs for the given name.  If
+            /// the NSEC is found, this method returns it; otherwise it returns
+            /// NULL.
+            ///
+            /// In all other cases this method simply returns NULL.
+            ///
+            /// \param name The name which the NSEC RRset belong to.
+            /// \param covering true if a covering NSEC is required; false if
+            /// a matching NSEC is required.
+            /// \return Any found DNSSEC proof RRset or NULL
+            isc::dns::ConstRRsetPtr getDNSSECRRset(
+                const isc::dns::Name& name, bool covering);
+
+            /// \brief Get DNSSEC negative proof for a given name.
+            ///
+            /// If the zone is considered NSEC-signed and the context
+            /// requested DNSSEC proofs, this method tries to find NSEC RRset
+            /// from the given set (\c found_set) and returns it if found;
+            /// in other cases this method simply returns NULL.
+            ///
+            /// \param found_set The RRset which may contain an NSEC RRset.
+            /// \return Any found DNSSEC proof RRset or NULL
+            isc::dns::ConstRRsetPtr getDNSSECRRset(const FoundRRsets&
+                                                   found_set);
+
+        private:
+            /// \brief Returns whether the zone is signed with NSEC3.
+            ///
+            /// This method returns true if the zone for the finder that
+            /// uses this context is considered DNSSEC signed with NSEC3;
+            /// otherwise it returns false.  If it's not yet detected,
+            /// this method now detects that via database lookups (if
+            /// necessary).
+            bool isNSEC3();
+
+            /// \brief Returns whether the zone is signed with NSEC.
+            ///
+            /// This is similar to isNSEC3(), but works for NSEC.
+            bool isNSEC();
+
+            /// \brief Probe into the database to see if/how the zone is
+            /// signed.
+            ///
+            /// This is a subroutine of isNSEC3() and isNSEC(), and performs
+            /// delayed database probe to detect whether the zone used by
+            /// the finder is DNSSEC signed, and if it is, with NSEC or NSEC3.
+            void probe();
+
+            DatabaseClient::Finder& finder_;
+            const bool need_dnssec_;
+
+            bool is_nsec3_;
+            bool is_nsec_;
+            bool probed_;
+        };
 
         /// \brief Search result of \c findDelegationPoint().
         ///
@@ -934,7 +1210,8 @@ public:
         /// \param target If the type happens to be ANY, it will insert all
         ///        the RRsets of the found name (if any is found) here instead
         ///        of being returned by the result.
-        ///
+        /// \param dnssec_ctx The dnssec context, it is a DNSSEC wrapper for
+        ///        find function.
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
         ///         indicating the match type (e.g. CNAME at the wildcard
@@ -942,11 +1219,12 @@ public:
         ///         success due to an exact match).  Also returned if there
         ///         is no match is an indication as to whether there was an
         ///         NXDOMAIN or an NXRRSET.
-        FindResult findWildcardMatch(
-            const isc::dns::Name& name,
-            const isc::dns::RRType& type, const FindOptions options,
-            const DelegationSearchResult& dresult,
-            std::vector<isc::dns::ConstRRsetPtr>* target);
+        ResultContext findWildcardMatch(const isc::dns::Name& name,
+                                        const isc::dns::RRType& type,
+                                        const FindOptions options,
+                                        const DelegationSearchResult& dresult,
+                                        std::vector<isc::dns::ConstRRsetPtr>*
+                                        target, FindDNSSECContext& dnssec_ctx);
 
         /// \brief Handle matching results for name
         ///
@@ -979,20 +1257,23 @@ public:
         ///                 it's NULL in the case of non wildcard match.
         /// \param target When the query is any, this must be set to a vector
         ///    where the result will be stored.
-        ///
+        /// \param dnssec_ctx The dnssec context, it is a DNSSEC wrapper for
+        ///        find function.
+
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
         ///         indicating the match type (corresponding to the each of
         ///         the above 4 cases).  The return value is intended to be
         ///         usable as a return value of the caller of this helper
         ///         method.
-        FindResult findOnNameResult(const isc::dns::Name& name,
-				    const isc::dns::RRType& type,
-				    const FindOptions options,
-				    const bool is_origin,
-				    const FoundRRsets& found,
-				    const std::string* wildname,
-                    std::vector<isc::dns::ConstRRsetPtr>* target);
+        ResultContext findOnNameResult(const isc::dns::Name& name,
+                                       const isc::dns::RRType& type,
+                                       const FindOptions options,
+                                       const bool is_origin,
+                                       const FoundRRsets& found,
+                                       const std::string* wildname,
+                                       std::vector<isc::dns::ConstRRsetPtr>*
+                                       target, FindDNSSECContext& dnssec_ctx);
 
         /// \brief Handle no match for name
         ///
@@ -1017,18 +1298,19 @@ public:
         /// \param target If the query is for type ANY, the successfull result,
         ///        if there happens to be one, will be returned through the
         ///        parameter, as it doesn't fit into the result.
-        ///
+        /// \param dnssec_ctx The dnssec context, it is a DNSSEC wrapper for
+        ///        find function.
         /// \return Tuple holding the result of the search - the RRset of the
         ///         wildcard records matching the name, together with a status
         ///         indicating the match type (e.g. CNAME at the wildcard
         ///         match, no RRs of the requested type at the wildcard,
         ///         success due to an exact match).
-        FindResult findNoNameResult(const isc::dns::Name& name,
-                                    const isc::dns::RRType& type,
-                                    FindOptions options,
-                                    const DelegationSearchResult& dresult,
-                                    std::vector<isc::dns::ConstRRsetPtr>*
-                                    target);
+        ResultContext findNoNameResult(const isc::dns::Name& name,
+                                       const isc::dns::RRType& type,
+                                       FindOptions options,
+                                       const DelegationSearchResult& dresult,
+                                       std::vector<isc::dns::ConstRRsetPtr>*
+                                       target, FindDNSSECContext& dnssec_ctx);
 
         /// Logs condition and creates result
         ///
@@ -1051,13 +1333,13 @@ public:
         ///
         /// \return FindResult object constructed from the code and rrset
         ///         arguments.
-        FindResult logAndCreateResult(const isc::dns::Name& name,
-				      const std::string* wildname,
-                                      const isc::dns::RRType& type,
-                                      ZoneFinder::Result code,
-                                      isc::dns::ConstRRsetPtr rrset,
-                                      const isc::log::MessageID& log_id,
-                                      FindResultFlags flags) const;
+        ResultContext logAndCreateResult(const isc::dns::Name& name,
+                                         const std::string* wildname,
+                                         const isc::dns::RRType& type,
+                                         ZoneFinder::Result code,
+                                         isc::dns::ConstRRsetPtr rrset,
+                                         const isc::log::MessageID& log_id,
+                                         FindResultFlags flags) const;
 
         /// \brief Checks if something lives below this domain.
         ///
@@ -1069,13 +1351,6 @@ public:
         /// \return true if the name has subdomains, false if not.
         bool hasSubdomains(const std::string& name);
 
-        /// \brief Get the NSEC covering a name.
-        ///
-        /// This one calls findPreviousName on the given name and extracts an
-        /// NSEC record on the result. It handles various error cases. The
-        /// method exists to share code present at more than one location.
-        dns::ConstRRsetPtr findNSECCover(const dns::Name& name);
-
         /// \brief Convenience type shortcut.
         ///
         /// To find stuff in the result of getRRsets.
@@ -1150,3 +1425,7 @@ private:
 }
 
 #endif  // __DATABASE_DATASRC_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/datasrc_config.h.pre.in b/src/lib/datasrc/datasrc_config.h.pre.in
index ff99601..9074df6 100644
--- a/src/lib/datasrc/datasrc_config.h.pre.in
+++ b/src/lib/datasrc/datasrc_config.h.pre.in
@@ -23,7 +23,7 @@ namespace datasrc {
 /// such as memory_ds.so and sqlite3_ds.so are found. It is used by the
 /// DataSourceClient loader if no absolute path is used and
 /// B10_FROM_BUILD is not set in the environment.
-const char* const BACKEND_LIBRARY_PATH = "@@PKGLIBEXECDIR@@/";
+const char* const BACKEND_LIBRARY_PATH = "@@PKGLIBDIR@@/";
 
 } // end namespace datasrc
 } // end namespace isc
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index d8ad07b..a9870d6 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -75,6 +75,35 @@ The datasource tried to provide an NSEC proof that the named domain does not
 exist, but the database backend doesn't support DNSSEC. No proof is included
 in the answer as a result.
 
+% DATASRC_DATABASE_FINDNSEC3 Looking for NSEC3 for %1 in %2 mode
+Debug information. A search in an database data source for NSEC3 that
+matches or covers the given name is being started.
+
+% DATASRC_DATABASE_FINDNSEC3_COVER found a covering NSEC3 for %1: %2
+Debug information. An NSEC3 that covers the given name is found and
+being returned.  The found NSEC3 RRset is also displayed.
+
+% DATASRC_DATABASE_FINDNSEC3_MATCH found a matching NSEC3 for %1 at label count %2: %3
+Debug information. An NSEC3 that matches (a possibly superdomain of)
+the given name is found and being returned.  When the shown label
+count is smaller than that of the given name, the matching NSEC3 is
+for a superdomain of the given name (see DATASRC_DATABSE_FINDNSEC3_TRYHASH).
+The found NSEC3 RRset is also displayed.
+
+% DATASRC_DATABASE_FINDNSEC3_TRYHASH looking for NSEC3 for %1 at label count %2 (hash %3)
+Debug information. In an attempt of finding an NSEC3 for the give name,
+(a possibly superdomain of) the name is hashed and searched for in the
+NSEC3 name space.  When the shown label count is smaller than that of the
+shown name, the search tries the superdomain name that share the shown
+(higher) label count of the shown name (e.g., for
+www.example.com. with shown label count of 3, example.com. is being
+tried, as "." is 1 label long).
+
+% DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV looking for previous NSEC3 for %1 at label count %2 (hash %3)
+Debug information. An exact match on hash (see
+DATASRC_DATABASE_FINDNSEC3_TRYHASH) was unsuccessful. We get the previous hash
+to that one instead.
+
 % DATASRC_DATABASE_FIND_RECORDS looking in datasource %1 for record %2/%3/%4
 Debug information. The database data source is looking up records with the given
 name and type in the database.
@@ -145,10 +174,12 @@ While iterating through the zone, the program extracted next RRset from it.
 The name and RRtype of the RRset is indicated in the message.
 
 % DATASRC_DATABASE_ITERATE_TTL_MISMATCH TTL values differ for RRs of %1/%2/%3, setting to %4
-While iterating through the zone, the time to live for RRs of the given RRset
-were found to be different. This isn't allowed on the wire and is considered
-an error, so we set it to the lowest value we found (but we don't modify the
-database). The data in database should be checked and fixed.
+While iterating through the zone, the time to live for RRs of the
+given RRset were found to be different. Since an RRset cannot have
+multiple TTLs, we set it to the lowest value we found (but we don't
+modify the database). This is what the client would do when such RRs
+were given in a DNS response according to RFC2181. The data in
+database should be checked and fixed.
 
 % DATASRC_DATABASE_JOURNALREADER_END %1/%2 on %3 from %4 to %5
 This is a debug message indicating that the program (successfully)
@@ -569,8 +600,10 @@ An attempt to add a NSEC3 record into the message failed, because the zone does
 not have any DS record. This indicates problem with the provided data.
 
 % DATASRC_QUERY_NO_ZONE no zone containing '%1' in class '%2'
-Lookup of domain failed because the data have no zone that contain the
-domain. Maybe someone sent a query to the wrong server for some reason.
+Debug information. Lookup of domain failed because the datasource
+has no zone that contains the domain. Maybe someone sent a query
+to the wrong server for some reason. This may also happen when
+looking in the datasource for addresses for NS records.
 
 % DATASRC_QUERY_PROCESS processing query '%1/%2' in the '%3' class
 Debug information. A sure query is being processed now.
@@ -585,7 +618,7 @@ The underlying data source failed to answer the query for referral information.
 1 means some error, 2 is not implemented. The data source should have logged
 the specific error already.
 
-% DATASRC_QUERY_RRSIG unable to answer RRSIG query
+% DATASRC_QUERY_RRSIG unable to answer RRSIG query for %1
 The server is unable to answer a direct query for RRSIG type, but was asked
 to do so.
 
@@ -632,6 +665,17 @@ enough information for it.  The code is 1 for error, 2 for not implemented.
 % DATASRC_SQLITE_CLOSE closing SQLite database
 Debug information. The SQLite data source is closing the database file.
 
+% DATASRC_SQLITE_COMPATIBLE_VERSION database schema V%1.%2 not up to date (expecting V%3.%4) but is compatible
+The version of the SQLite3 database schema used to hold the zone data
+is not the latest one - the current version of BIND 10 was written
+with a later schema version in mind.  However, the database is
+compatible with the current version of BIND 10, and BIND 10 will run
+without any problems.
+
+Consult the release notes for your version of BIND 10.  Depending on
+the changes made to the database schema, it is possible that improved
+performance could result if the database were upgraded.
+
 % DATASRC_SQLITE_CONNCLOSE Closing sqlite database
 The database file is no longer needed and is being closed.
 
@@ -699,6 +743,14 @@ source.
 The SQLite data source was asked to provide a NSEC3 record for given zone.
 But it doesn't contain that zone.
 
+% DATASRC_SQLITE_INCOMPATIBLE_VERSION database schema V%1.%2 incompatible with version (V%3.%4) expected
+The version of the SQLite3 database schema used to hold the zone data
+is incompatible with the version expected by BIND 10.  As a result,
+BIND 10 is unable to run using the database file as the data source.
+
+The database should be updated using the means described in the BIND
+10 documentation.
+
 % DATASRC_SQLITE_NEWCONN SQLite3Database is being initialized
 A wrapper object to hold database connection is being initialized.
 
diff --git a/src/lib/datasrc/factory.h b/src/lib/datasrc/factory.h
index 9d0a762..f3ca397 100644
--- a/src/lib/datasrc/factory.h
+++ b/src/lib/datasrc/factory.h
@@ -163,7 +163,7 @@ public:
     ///
     /// \return Reference to the DataSourceClient instance contained in this
     ///         container.
-    DataSourceClient& getInstance() { return *instance_; }
+    DataSourceClient& getInstance() { return (*instance_); }
 
 private:
     DataSourceClient* instance_;
diff --git a/src/lib/datasrc/memory_datasrc.cc b/src/lib/datasrc/memory_datasrc.cc
index 5137727..ea35cfa 100644
--- a/src/lib/datasrc/memory_datasrc.cc
+++ b/src/lib/datasrc/memory_datasrc.cc
@@ -12,17 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <algorithm>
-#include <map>
-#include <utility>
-#include <cctype>
-#include <cassert>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
 #include <exceptions/exceptions.h>
 
 #include <dns/name.h>
@@ -34,24 +23,44 @@
 
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/rbtree.h>
+#include <datasrc/rbnode_rrset.h>
 #include <datasrc/logger.h>
 #include <datasrc/iterator.h>
 #include <datasrc/data_source.h>
 #include <datasrc/factory.h>
 
-#include <cc/data.h>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+
+#include <algorithm>
+#include <map>
+#include <utility>
+#include <cctype>
+#include <cassert>
 
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
-using namespace isc::data;
 using boost::scoped_ptr;
 
 namespace isc {
 namespace datasrc {
 
+using namespace internal;
+
 namespace {
 // Some type aliases
+
+// A functor type used for loading.
+typedef boost::function<void(ConstRRsetPtr)> LoadCallback;
+
+// RRset specified for this implementation
+typedef boost::shared_ptr<internal::RBNodeRRset> RBNodeRRsetPtr;
+typedef boost::shared_ptr<const internal::RBNodeRRset> ConstRBNodeRRsetPtr;
+
 /*
  * Each domain consists of some RRsets. They will be looked up by the
  * RRType.
@@ -63,25 +72,59 @@ namespace {
  * critical place and map has better interface for the lookups, so we use
  * that.
  */
-typedef map<RRType, ConstRRsetPtr> Domain;
+typedef map<RRType, ConstRBNodeRRsetPtr> Domain;
 typedef Domain::value_type DomainPair;
 typedef boost::shared_ptr<Domain> DomainPtr;
 // The tree stores domains
 typedef RBTree<Domain> DomainTree;
 typedef RBNode<Domain> DomainNode;
 
+// In the following dedicated namespace we define a few application-specific
+// RBNode flags.  We use a separate namespace so we can consolidate the
+// definition in a single place, which would hopefully reduce the risk of
+// collisions.
+// (Note: it's within an unnamed namespace, so effectively private.)
+namespace domain_flag {
+// This flag indicates the node is at a "wildcard level" (in short, it means
+// one of the node's immediate child is a wildcard).  See addWildcards()
+// for more details.
+const DomainNode::Flags WILD = DomainNode::FLAG_USER1;
+
+// This flag is used for additional record shortcut.  If a node has this
+// flag, it's under a zone cut for a delegation to a child zone.
+// Note: for a statically built zone this information is stable, but if we
+// change the implementation to be dynamically modifiable, it may not be
+// realistic to keep this flag update for all affected nodes, and we may
+// have to reconsider the mechanism.
+const DomainNode::Flags GLUE = DomainNode::FLAG_USER2;
+
+// This flag indicates the node is generated as a result of wildcard
+// expansion.  In this implementation, this flag can be set only in
+// the separate auxiliary tree of ZoneData (see the structure description).
+const DomainNode::Flags WILD_EXPANDED = DomainNode::FLAG_USER3;
+};
+
 // Separate storage for NSEC3 RRs (and their RRSIGs).  It's an STL map
 // from string to the NSEC3 RRset.  The map key is the first label
 // (upper cased) of the owner name of the corresponding NSEC3 (i.e., map
 // value).  We can use  the standard string comparison (if the comparison
 // target is also upper cased) due to the nature of NSEC3 owner names.
-typedef map<string, ConstRRsetPtr> NSEC3Map;
+//
+// Note: We maintain the RRsets in the form of RBNodeRRset even if they are
+// not stored in the RB tree.  The reason is because comparison can be
+// more efficient if we make sure all RRsets returned from this module are
+// of the same type.
+typedef map<string, ConstRBNodeRRsetPtr> NSEC3Map;
 typedef NSEC3Map::value_type NSEC3Pair;
 
 // Actual zone data: Essentially a set of zone's RRs.  This is defined as
 // a separate structure so that it'll be replaceable on reload.
 struct ZoneData {
-    ZoneData(const Name& origin) : domains_(true), origin_data_(NULL) {
+    ZoneData(const Name& origin) :
+        domains_(true),
+        origin_data_(NULL),
+        nsec_signed_(false)
+    {
         // We create the node for origin (it needs to exist anyway in future)
         domains_.insert(origin, &origin_data_);
         DomainPtr origin_domain(new Domain);
@@ -91,6 +134,31 @@ struct ZoneData {
     // The main data (name + RRsets)
     DomainTree domains_;
 
+    // An auxiliary tree for wildcard expanded data used in additional data
+    // processing.  It contains names like "ns.wild.example" in the following
+    // example:
+    // child.wild.example. NS ns.wild.example.
+    // *.wild.example IN AAAA 2001:db8::1234
+    // (and there's no exact ns.wild.example. in the zone).  This tree contains
+    // such names with a copy of the RRsets of the matching wildcard name
+    // with its owner name expanded, e.g.:
+    // ns.wild.example. IN AAAA 2001:db8::1234
+    // In theory, this tree could have many such wildcard-expandable names,
+    // each of which has a copy of the original list of RRsets.  In practice,
+    // however, it should be very rare that names for additional section
+    // processing are subject to wildcard expansion, so in most cases this tree
+    // should be even empty, and even if it has content it should be very
+    // small.
+private:
+    scoped_ptr<DomainTree> aux_wild_domains_;
+public:
+    DomainTree& getAuxWildDomains() {
+        if (!aux_wild_domains_) {
+            aux_wild_domains_.reset(new DomainTree);
+        }
+        return (*aux_wild_domains_);
+    }
+
     // Shortcut to the origin node, which should always exist
     DomainNode* origin_data_;
 
@@ -106,9 +174,586 @@ struct ZoneData {
         const scoped_ptr<NSEC3Hash> hash_; // hash parameter/calculator
     };
     scoped_ptr<NSEC3Data> nsec3_data_; // non NULL only when it's NSEC3 signed
+    bool nsec_signed_; // True if there's at least one NSEC record
+
+    // This templated structure encapsulates the find result of findNode()
+    // method (also templated) below.
+    // The template parameter is expected to be either 'const DomainNode' or
+    // 'DomainNode' (to avoid misuse the template definition itself is kept
+    // private - we only expose expected typedefs).  The former is expected
+    // to be used for lookups, and the latter is expected to be used for
+    // constructing the zone.
+private:
+    template <typename NodeType>
+    struct FindNodeResultBase {
+        // Bitwise flags to represent supplemental information of the
+        // search result:
+        // Search resulted in a wildcard match.
+        static const unsigned int FIND_WILDCARD = 1;
+        // Search encountered a zone cut due to NS but continued to look for
+        // a glue.
+        static const unsigned int FIND_ZONECUT = 2;
+
+        FindNodeResultBase(ZoneFinder::Result code_param,
+                           NodeType* node_param,
+                           ConstRBNodeRRsetPtr rrset_param,
+                           unsigned int flags_param = 0) :
+            code(code_param), node(node_param), rrset(rrset_param),
+            flags(flags_param)
+        {}
+        const ZoneFinder::Result code;
+        NodeType* const node;
+        ConstRBNodeRRsetPtr const rrset;
+        const unsigned int flags;
+    };
+public:
+    typedef FindNodeResultBase<const DomainNode> FindNodeResult;
+    typedef FindNodeResultBase<DomainNode> FindMutableNodeResult;
+
+    // Identify the RBTree node that best matches the given name.
+    // See implementation notes below.
+    template <typename ResultType>
+    ResultType findNode(const Name& name,
+                        ZoneFinder::FindOptions options) const;
+};
+
+/// Maintain intermediate data specific to the search context used in
+/// \c find().
+///
+/// It will be passed to \c cutCallback() (see below) and record a possible
+/// zone cut node and related RRset (normally NS or DNAME).
+struct FindState {
+    FindState(bool glue_ok) :
+        zonecut_node_(NULL),
+        dname_node_(NULL),
+        glue_ok_(glue_ok)
+    {}
+
+    // These will be set to a domain node of the highest delegation point,
+    // if any.  In fact, we could use a single variable instead of both.
+    // But then we would need to distinquish these two cases by something
+    // else and it seemed little more confusing when this was written.
+    const DomainNode* zonecut_node_;
+    const DomainNode* dname_node_;
+
+    // Delegation RRset (NS or DNAME), if found.
+    ConstRBNodeRRsetPtr rrset_;
+
+    // Whether to continue search below a delegation point.
+    // Set at construction time.
+    const bool glue_ok_;
+};
+
+// A callback called from possible zone cut nodes and nodes with DNAME.
+// This will be passed from findNode() to \c RBTree::find().
+bool cutCallback(const DomainNode& 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,
+    // the NS is authoritative, not delegation (corner case explicitly
+    // allowed by section 3 of 2672)
+    const Domain::const_iterator found_dname(node.getData()->find(
+                                                 RRType::DNAME()));
+    if (found_dname != node.getData()->end()) {
+        LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
+        state->dname_node_ = &node;
+        state->rrset_ = found_dname->second;
+        // No more processing below the DNAME (RFC 2672, section 3
+        // forbids anything to exist below it, so there's no need
+        // to actually search for it). This is strictly speaking
+        // a different way than described in 4.1 of that RFC,
+        // but because of the assumption in section 3, it has the
+        // same behaviour.
+        return (true);
+    }
+
+    // Look for NS
+    const Domain::const_iterator found_ns(node.getData()->find(RRType::NS()));
+    if (found_ns != node.getData()->end()) {
+        // We perform callback check only for the highest zone cut in the
+        // rare case of nested zone cuts.
+        if (state->zonecut_node_ != NULL) {
+            return (false);
+        }
+
+        LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
+
+        // BIND 9 checks if this node is not the origin.  That's probably
+        // because it can support multiple versions for dynamic updates
+        // and IXFR, and it's possible that the callback is called at
+        // the apex and the DNAME doesn't exist for a particular version.
+        // It cannot happen for us (at least for now), so we don't do
+        // that check.
+        state->zonecut_node_ = &node;
+        state->rrset_ = found_ns->second;
+
+        // Unless glue is allowed the search stops here, so we return
+        // false; otherwise return true to continue the search.
+        return (!state->glue_ok_);
+    }
+
+    // This case should not happen because we enable callback only
+    // when we add an RR searched for above.
+    assert(0);
+    // This is here to avoid warning (therefore compilation error)
+    // in case assert is turned off. Otherwise we could get "Control
+    // reached end of non-void function".
+    return (false);
+}
+
+// Implementation notes: this method identifies an RBT node that best matches
+// the give name in terms of DNS query handling.  In many cases,
+// DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that
+// the given name is generally expected to be contained in the zone, so
+// even if it doesn't exist, it should at least match the zone origin).
+// If it finds an exact match, that's obviously the best one.  The partial
+// match case is more complicated.
+//
+// We first need to consider the case where search hits a delegation point,
+// either due to NS or DNAME.  They are indicated as either dname_node_ or
+// zonecut_node_ being non NULL.  Usually at most one of them will be
+// something else than NULL (it might happen both are NULL, in which case we
+// consider it NOT FOUND). There's one corner case when both might be
+// something else than NULL and it is in case there's a DNAME under a zone
+// cut and we search in glue OK mode ‒ in that case we don't stop on the
+// domain with NS and ignore it for the answer, but it gets set anyway. Then
+// we find the DNAME and we need to act by it, therefore we first check for
+// DNAME and then for NS. In all other cases it doesn't matter, as at least
+// one of them is NULL.
+//
+// Next, we need to check if the RBTree search stopped at a node for a
+// subdomain of the search name (so the comparison result that stopped the
+// search is "SUPERDOMAIN"), it means the stopping node is an empty
+// non-terminal node.  In this case the search name is considered to exist
+// but no data should be found there.
+//
+// If none of above is the case, we then consider whether there's a matching
+// wildcard.  DomainTree::find() records the node if it encounters a
+// "wildcarding" node, i.e., the immediate ancestor of a wildcard name
+// (e.g., wild.example.com for *.wild.example.com), and returns it if it
+// doesn't find any node that better matches the query name.  In this case
+// we'll check if there's indeed a wildcard below the wildcarding node.
+//
+// Note, first, that the wildcard is checked after the empty
+// non-terminal domain case above, because if that one triggers, it
+// means we should not match according to 4.3.3 of RFC 1034 (the query
+// name is known to exist).
+//
+// Before we try to find a wildcard, we should check whether there's
+// an existing node that would cancel the wildcard match.  If
+// DomainTree::find() stopped at a node which has a common ancestor
+// with the query name, it might mean we are comparing with a
+// non-wildcard node. In that case, we check which part is common. If
+// we have something in common that lives below the node we got (the
+// one above *), then we should cancel the match according to section
+// 4.3.3 of RFC 1034 (as the name between the wildcard domain and the
+// query name is known to exist).
+//
+// If there's no node below the wildcarding node that shares a common ancestor
+// of the query name, we can conclude the wildcard is the best match.
+// We'll then identify the wildcard node via an incremental search.  Note that
+// there's no possibility that the query name is at an empty non terminal
+// node below the wildcarding node at this stage; that case should have been
+// caught above.
+//
+// If none of the above succeeds, we conclude the name doesn't exist in
+// the zone.
+template <typename ResultType>
+ResultType
+ZoneData::findNode(const Name& name, ZoneFinder::FindOptions options) const {
+    DomainNode* node = NULL;
+    RBTreeNodeChain<Domain> node_path;
+    FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
+
+    const DomainTree::Result result =
+        domains_.find(name, &node, node_path, cutCallback, &state);
+    const unsigned int zonecut_flag =
+        (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
+    if (result == DomainTree::EXACTMATCH) {
+        return (ResultType(ZoneFinder::SUCCESS, node, state.rrset_,
+                           zonecut_flag));
+    } else if (result == DomainTree::PARTIALMATCH) {
+        assert(node != NULL);
+        if (state.dname_node_ != NULL) { // DNAME
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
+                arg(state.rrset_->getName());
+            return (ResultType(ZoneFinder::DNAME, NULL, state.rrset_));
+        }
+        if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
+                arg(state.rrset_->getName());
+            return (ResultType(ZoneFinder::DELEGATION, NULL, state.rrset_));
+        }
+        if (node_path.getLastComparisonResult().getRelation() ==
+            NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
+            LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).arg(name);
+            return (ResultType(ZoneFinder::NXRRSET, node,
+                               ConstRBNodeRRsetPtr()));
+        }
+        if (node->getFlag(domain_flag::WILD)) { // maybe a wildcard
+            if (node_path.getLastComparisonResult().getRelation() ==
+                NameComparisonResult::COMMONANCESTOR &&
+                node_path.getLastComparisonResult().getCommonLabels() > 1) {
+                // Wildcard canceled.  Treat it as NXDOMAIN.
+                // Note: Because the way the tree stores relative names, we
+                // will have exactly one common label (the ".") in case we have
+                // nothing common under the node we got, and we will get
+                // more common labels otherwise (yes, this relies on the
+                // internal RBTree structure, which leaks out through this
+                // little bit).
+                LOG_DEBUG(logger, DBG_TRACE_DATA,
+                          DATASRC_MEM_WILDCARD_CANCEL).arg(name);
+                return (ResultType(ZoneFinder::NXDOMAIN, NULL,
+                                   ConstRBNodeRRsetPtr()));
+            }
+            // Now the wildcard should be the best match.
+            const Name wildcard(Name("*").concatenate(
+                                    node_path.getAbsoluteName()));
+            DomainTree::Result result = domains_.find(wildcard, &node);
+            // Otherwise, why would the domain_flag::WILD be there if
+            // there was no wildcard under it?
+            assert(result == DomainTree::EXACTMATCH);
+            return (ResultType(ZoneFinder::SUCCESS, node, state.rrset_,
+                               FindNodeResult::FIND_WILDCARD |
+                               zonecut_flag));
+        }
+        // Nothing really matched.
+        LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
+        return (ResultType(ZoneFinder::NXDOMAIN, node, state.rrset_));
+    } 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 " <<
+                             origin_data_->getName());
+    }
+}
+} // unnamed namespace
+
+namespace internal {
+
+/// \brief An encapsulation type for a pointer of an additional node
+/// associated with an \c RBNodeRRset object.
+///
+/// Currently this is defined as a structure only so that it can declared
+/// in rbnode_rrset.h; this is essentially a pointer to \c DomainNode.
+/// In future, however, this structure may have other attributes.
+struct AdditionalNodeInfo {
+    explicit AdditionalNodeInfo(DomainNode* node) : node_(node) {}
+    DomainNode* node_;
 };
+
+//
+// RBNodeRRset details
+//
+struct RBNodeRRsetImpl {
+public:
+    RBNodeRRsetImpl(const ConstRRsetPtr& rrset) : rrset_(rrset)
+    {}
+
+    ConstRRsetPtr rrset_;     ///< Underlying RRset
+    scoped_ptr<vector<AdditionalNodeInfo> > additionals_;
+};
+
+RBNodeRRset::RBNodeRRset(const ConstRRsetPtr& rrset) :
+    impl_(new RBNodeRRsetImpl(rrset))
+{
 }
 
+RBNodeRRset::~RBNodeRRset() {
+    delete impl_;
+}
+
+unsigned int
+RBNodeRRset::getRdataCount() const {
+    return (impl_->rrset_->getRdataCount());
+}
+
+const Name&
+RBNodeRRset::getName() const {
+    return (impl_->rrset_->getName());
+}
+
+const RRClass&
+RBNodeRRset::getClass() const {
+    return (impl_->rrset_->getClass());
+}
+
+const RRType&
+RBNodeRRset::getType() const {
+    return (impl_->rrset_->getType());
+}
+
+const RRTTL&
+RBNodeRRset::getTTL() const {
+    return (impl_->rrset_->getTTL());
+}
+
+void
+RBNodeRRset::setName(const Name&) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::setName() not supported");
+}
+
+void
+RBNodeRRset::setTTL(const RRTTL&) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::setTTL() not supported");
+}
+
+string
+RBNodeRRset::toText() const {
+    return (impl_->rrset_->toText());
+}
+
+unsigned int
+RBNodeRRset::toWire(AbstractMessageRenderer& renderer) const {
+    return (impl_->rrset_->toWire(renderer));
+}
+
+unsigned int
+RBNodeRRset::toWire(isc::util::OutputBuffer& buffer) const {
+    return (impl_->rrset_->toWire(buffer));
+}
+
+void
+RBNodeRRset::addRdata(ConstRdataPtr) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
+}
+
+void
+RBNodeRRset::addRdata(const Rdata&) {
+    isc_throw(isc::NotImplemented, "RBNodeRRset::addRdata() not supported");
+}
+
+RdataIteratorPtr
+RBNodeRRset::getRdataIterator() const {
+    return (impl_->rrset_->getRdataIterator());
+}
+
+RRsetPtr
+RBNodeRRset::getRRsig() const {
+    return (impl_->rrset_->getRRsig());
+}
+
+void
+RBNodeRRset::addRRsig(const ConstRdataPtr& rdata) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(rdata);
+}
+
+void
+RBNodeRRset::addRRsig(const RdataPtr& rdata) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(rdata);
+}
+
+void
+RBNodeRRset::addRRsig(const AbstractRRset& sigs) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::addRRsig(const ConstRRsetPtr& sigs) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::addRRsig(const RRsetPtr& sigs) {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->addRRsig(sigs);
+}
+
+void
+RBNodeRRset::removeRRsig() {
+    AbstractRRset* p = const_cast<AbstractRRset*>(impl_->rrset_.get());
+    p->removeRRsig();
+}
+
+ConstRRsetPtr
+RBNodeRRset::getUnderlyingRRset() const {
+    return (impl_->rrset_);
+}
+
+void
+RBNodeRRset::addAdditionalNode(const AdditionalNodeInfo& additional) {
+    // Lazy initialization
+    if (!impl_->additionals_) {
+        impl_->additionals_.reset(new vector<AdditionalNodeInfo>);
+    }
+    impl_->additionals_->push_back(additional);
+}
+
+const vector<AdditionalNodeInfo>*
+RBNodeRRset::getAdditionalNodes() const {
+    return (impl_->additionals_.get());
+}
+
+void
+RBNodeRRset::copyAdditionalNodes(RBNodeRRset& dst) const {
+    if (impl_->additionals_) {
+        dst.impl_->additionals_.reset(
+            new vector<AdditionalNodeInfo>(impl_->additionals_->begin(),
+                                           impl_->additionals_->end()));
+    }
+}
+
+} // end of internal
+
+namespace {
+/*
+ * Prepares a rrset to be return as a result.
+ *
+ * If rename is false, it returns the one provided. If it is true, it
+ * creates a new rrset with the same data but with provided name.
+ * In addition, if DNSSEC records are required by the original caller of
+ * find(), it also creates expanded RRSIG based on the RRSIG of the
+ * wildcard RRset.
+ * It is designed for wildcard case, where we create the rrsets
+ * dynamically.
+ */
+ConstRBNodeRRsetPtr
+prepareRRset(const Name& name, const ConstRBNodeRRsetPtr& rrset, bool rename,
+             ZoneFinder::FindOptions options)
+{
+    if (rename) {
+        LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
+            arg(rrset->getName()).arg(name);
+        RRsetPtr result_base(new RRset(name, rrset->getClass(),
+                                       rrset->getType(), rrset->getTTL()));
+        for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
+             i->next()) {
+            result_base->addRdata(i->getCurrent());
+        }
+        if ((options & ZoneFinder::FIND_DNSSEC) != 0) {
+            ConstRRsetPtr sig_rrset = rrset->getRRsig();
+            if (sig_rrset) {
+                RRsetPtr result_sig(new RRset(name, sig_rrset->getClass(),
+                                              RRType::RRSIG(),
+                                              sig_rrset->getTTL()));
+                for (RdataIteratorPtr i(sig_rrset->getRdataIterator());
+                     !i->isLast();
+                     i->next())
+                {
+                    result_sig->addRdata(i->getCurrent());
+                }
+                result_base->addRRsig(result_sig);
+            }
+        }
+        RBNodeRRsetPtr result(new RBNodeRRset(result_base));
+        rrset->copyAdditionalNodes(*result);
+        return (result);
+    } else {
+        return (rrset);
+    }
+}
+
+// Specialized version of ZoneFinder::ResultContext, which specifically
+// holds rrset in the form of RBNodeRRset.
+struct RBNodeResultContext {
+    /// \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.
+    RBNodeResultContext(ZoneFinder::Result code_param,
+                        ConstRBNodeRRsetPtr rrset_param,
+                        ZoneFinder::FindResultFlags flags_param,
+                        const DomainNode* node) :
+        code(code_param), rrset(rrset_param), flags(flags_param),
+        found_node(node)
+    {}
+
+    const ZoneFinder::Result code;
+    const ConstRBNodeRRsetPtr rrset;
+    const ZoneFinder::FindResultFlags flags;
+    const DomainNode* const found_node;
+};
+}
+
+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,
+            const RBNodeResultContext& result) :
+        ZoneFinder::Context(finder, options,
+                            ResultContext(result.code, result.rrset,
+                                          result.flags)),
+        rrset_(result.rrset), found_node_(result.found_node)
+    {}
+
+protected:
+    virtual void getAdditionalImpl(const vector<RRType>& requested_types,
+                                   vector<ConstRRsetPtr>& result)
+    {
+        if (!rrset_) {
+            // In this case this context should encapsulate the result of
+            // findAll() and found_node_ should point to a valid answer node.
+            if (found_node_ == NULL || found_node_->isEmpty()) {
+                isc_throw(isc::Unexpected,
+                          "Invalid call to in-memory getAdditional: caller's "
+                          "bug or broken zone");
+            }
+            BOOST_FOREACH(const DomainPair& dom_it, *found_node_->getData()) {
+                getAdditionalForRRset(*dom_it.second, requested_types,
+                                      result);
+            }
+        } else {
+            getAdditionalForRRset(*rrset_, requested_types, result);
+        }
+    }
+
+private:
+    // Retrieve additional RRsets for a given RRset associated in the context.
+    // The process is straightforward: it examines the link to
+    // AdditionalNodeInfo vector (if set), and find RRsets of the requested
+    // type for each node.
+    static void getAdditionalForRRset(const RBNodeRRset& rrset,
+                                      const vector<RRType>& requested_types,
+                                      vector<ConstRRsetPtr>& result)
+    {
+        const vector<AdditionalNodeInfo>* additionals_ =
+            rrset.getAdditionalNodes();
+        if (additionals_ == NULL) {
+            return;
+        }
+        const bool glue_ok = (rrset.getType() == RRType::NS());
+        BOOST_FOREACH(const AdditionalNodeInfo& additional, *additionals_) {
+            assert(additional.node_ != NULL);
+            if (additional.node_->isEmpty()) {
+                continue;
+            }
+            if (!glue_ok && additional.node_->getFlag(domain_flag::GLUE)) {
+                continue;
+            }
+            const bool wild_expanded =
+                additional.node_->getFlag(domain_flag::WILD_EXPANDED);
+            BOOST_FOREACH(const RRType& rrtype, requested_types) {
+                Domain::const_iterator found =
+                    additional.node_->getData()->find(rrtype);
+                if (found != additional.node_->getData()->end()) {
+                    // If the additional node was generated as a result of
+                    // wildcard expansion, we return the underlying RRset,
+                    // in case the caller has the same RRset but as a result
+                    // of normal find() and needs to know they are of the same
+                    // kind; otherwise we simply use the stored RBNodeRRset.
+                    if (wild_expanded) {
+                        result.push_back(found->second->getUnderlyingRRset());
+                    } else {
+                        result.push_back(found->second);
+                    }
+                }
+            }
+        }
+    }
+
+    const ConstRBNodeRRsetPtr rrset_;
+    const DomainNode* const found_node_;
+};
+
 // Private data and hidden methods of InMemoryZoneFinder
 struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
     // Constructor
@@ -117,8 +762,6 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         zone_data_(new ZoneData(origin_))
     {}
 
-    static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
-
     // Information about the zone
     RRClass zone_class_;
     Name origin_;
@@ -127,6 +770,17 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
     // The actual zone data
     scoped_ptr<ZoneData> zone_data_;
 
+    // Common process for zone load.
+    // rrset_installer is a functor that takes another functor as an argument,
+    // and expected to call the latter for each RRset of the zone.  How the
+    // sequence of the RRsets is generated depends on the internal
+    // details  of the loader: either from a textual master file or from
+    // another data source.
+    // filename is the file name of the master file or empty if the zone is
+    // loaded from another data source.
+    void load(const string& filename,
+              boost::function<void(LoadCallback)> rrset_installer);
+
     // Add the necessary magic for any wildcard contained in 'name'
     // (including itself) to be found in the zone.
     //
@@ -157,7 +811,7 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                                                          &node));
                 assert(result == DomainTree::SUCCESS ||
                        result == DomainTree::ALREADYEXISTS);
-                node->setFlag(DOMAINFLAG_WILD);
+                node->setFlag(domain_flag::WILD);
 
                 // Ensure a separate level exists for the wildcard name.
                 // Note: for 'name' itself we do this later anyway, but the
@@ -410,7 +1064,8 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             return (result::EXIST);
         }
 
-        zone_data.nsec3_data_->map_.insert(NSEC3Pair(fst_label, rrset));
+        zone_data.nsec3_data_->map_.insert(
+            NSEC3Pair(fst_label, ConstRBNodeRRsetPtr(new RBNodeRRset(rrset))));
         return (result::SUCCESS);
     }
 
@@ -419,14 +1074,21 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * access is without the impl_-> and it will get inlined anyway.
      */
     // Implementation of InMemoryZoneFinder::add
-    result::Result add(const ConstRRsetPtr& rrset, ZoneData& zone_data) {
+    result::Result add(const ConstRRsetPtr& rawrrset, ZoneData& zone_data,
+                       vector<RBNodeRRset*>* need_additionals)
+    {
         // Sanitize input.  This will cause an exception to be thrown
         // if the input RRset is empty.
-        addValidation(rrset);
+        addValidation(rawrrset);
 
         // OK, can add the RRset.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
-            arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
+            arg(rawrrset->getName()).arg(rawrrset->getType()).arg(origin_);
+
+        // ... although instead of loading the RRset directly, we encapsulate
+        // it within an RBNodeRRset.  This contains additional information that
+        // speeds up queries.
+        RBNodeRRsetPtr rrset(new RBNodeRRset(rawrrset));
 
         if (rrset->getType() == RRType::NSEC3()) {
             return (addNSEC3(rrset, zone_data));
@@ -483,6 +1145,12 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 node->setFlag(DomainNode::FLAG_CALLBACK);
             }
 
+            if (need_additionals != NULL &&
+                (rrset->getType() == RRType::NS() ||
+                 rrset->getType() == RRType::MX())) {
+                need_additionals->push_back(rrset.get());
+            }
+
             // 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() &&
@@ -499,6 +1167,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                     isc_throw(AddError, "NSEC3PARAM with inconsistent "
                               "parameters: " << rrset->toText());
                 }
+            } else if (rrset->getType() == RRType::NSEC()) {
+                // If it is NSEC signed zone, so we put a flag there
+                // (flag is enough)
+                zone_data.nsec_signed_ = true;
             }
             return (result::SUCCESS);
         } else {
@@ -511,8 +1183,10 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
      * Same as above, but it checks the return value and if it already exists,
      * it throws.
      */
-    void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data) {
-        switch (add(set, *zone_data)) {
+    void addFromLoad(const ConstRRsetPtr& set, ZoneData* zone_data,
+                     vector<RBNodeRRset*>* need_additionals)
+    {
+        switch (add(set, *zone_data, need_additionals)) {
         case result::EXIST:
             LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
                 arg(set->getName()).arg(set->getType());
@@ -525,266 +1199,61 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
         }
     }
 
-    // Maintain intermediate data specific to the search context used in
-    /// \c find().
-    ///
-    /// It will be passed to \c zonecutCallback() and record a possible
-    /// zone cut node and related RRset (normally NS or DNAME).
-    struct FindState {
-        FindState(FindOptions options) :
-            zonecut_node_(NULL),
-            dname_node_(NULL),
-            options_(options)
-        {}
-        const DomainNode* zonecut_node_;
-        const DomainNode* dname_node_;
-        ConstRRsetPtr rrset_;
-        const FindOptions options_;
-    };
-
-    // A callback called from possible zone cut nodes and nodes with DNAME.
-    // This will be passed from the \c find() method to \c RBTree::find().
-    static bool cutCallback(const DomainNode& 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,
-        // the NS is authoritative, not delegation (corner case explicitly
-        // allowed by section 3 of 2672)
-        const Domain::const_iterator foundDNAME(node.getData()->find(
-            RRType::DNAME()));
-        if (foundDNAME != node.getData()->end()) {
-            LOG_DEBUG(logger, DBG_TRACE_DETAILED,
-                      DATASRC_MEM_DNAME_ENCOUNTERED);
-            state->dname_node_ = &node;
-            state->rrset_ = foundDNAME->second;
-            // No more processing below the DNAME (RFC 2672, section 3
-            // forbids anything to exist below it, so there's no need
-            // to actually search for it). This is strictly speaking
-            // a different way than described in 4.1 of that RFC,
-            // but because of the assumption in section 3, it has the
-            // same behaviour.
-            return (true);
-        }
-
-        // Look for NS
-        const Domain::const_iterator foundNS(node.getData()->find(
-            RRType::NS()));
-        if (foundNS != node.getData()->end()) {
-            // We perform callback check only for the highest zone cut in the
-            // rare case of nested zone cuts.
-            if (state->zonecut_node_ != NULL) {
-                return (false);
-            }
-
-            LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
-
-            // BIND 9 checks if this node is not the origin.  That's probably
-            // because it can support multiple versions for dynamic updates
-            // and IXFR, and it's possible that the callback is called at
-            // the apex and the DNAME doesn't exist for a particular version.
-            // It cannot happen for us (at least for now), so we don't do
-            // that check.
-            state->zonecut_node_ = &node;
-            state->rrset_ = foundNS->second;
-
-            // Unless glue is allowed the search stops here, so we return
-            // false; otherwise return true to continue the search.
-            return ((state->options_ & FIND_GLUE_OK) == 0);
-        }
-
-        // This case should not happen because we enable callback only
-        // when we add an RR searched for above.
-        assert(0);
-        // This is here to avoid warning (therefore compilation error)
-        // in case assert is turned off. Otherwise we could get "Control
-        // reached end of non-void function".
-        return (false);
-    }
-
-    /*
-     * Prepares a rrset to be return as a result.
-     *
-     * If rename is false, it returns the one provided. If it is true, it
-     * creates a new rrset with the same data but with provided name.
-     * It is designed for wildcard case, where we create the rrsets
-     * dynamically.
-     */
-    static ConstRRsetPtr prepareRRset(const Name& name, const ConstRRsetPtr&
-        rrset, bool rename)
-    {
-        if (rename) {
-            LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
-                arg(rrset->getName()).arg(name);
-            /*
-             * We lose a signature here. But it would be wrong anyway, because
-             * the name changed. This might turn out to be unimportant in
-             * future, because wildcards will probably be handled somehow
-             * by DNSSEC.
-             */
-            RRsetPtr result(new RRset(name, rrset->getClass(),
-                rrset->getType(), rrset->getTTL()));
-            for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
-                i->next()) {
-                result->addRdata(i->getCurrent());
-            }
-            return (result);
-        } else {
-            return (rrset);
-        }
-    }
-
-    // Set up FindResult object as a return value of find(), taking into
+    // Set up FindContext object as a return value of find(), taking into
     // account wildcard matches and DNSSEC information.  We set the NSEC/NSEC3
     // flag when applicable regardless of the find option; the caller would
     // simply ignore these when they didn't request DNSSEC related results.
-    FindResult createFindResult(Result code, ConstRRsetPtr rrset,
-                                bool wild) const
+    // When the optional parameter 'node' is given (in which case it should be
+    // non NULL), it means it's a result of ANY query and the context should
+    // remember the matched node.
+    RBNodeResultContext createFindResult(Result code,
+                                         ConstRBNodeRRsetPtr rrset,
+                                         bool wild = false,
+                                         const DomainNode* node = NULL) const
     {
         FindResultFlags flags = RESULT_DEFAULT;
         if (wild) {
             flags = flags | RESULT_WILDCARD;
         }
-        if ((code == NXRRSET || code == NXDOMAIN || wild) &&
-            zone_data_->nsec3_data_) {
-            flags = flags | RESULT_NSEC3_SIGNED;
+        if (code == NXRRSET || code == NXDOMAIN || wild) {
+            if (zone_data_->nsec3_data_) {
+                flags = flags | RESULT_NSEC3_SIGNED;
+            }
+            if (zone_data_->nsec_signed_) {
+                flags = flags | RESULT_NSEC_SIGNED;
+            }
         }
-        return (FindResult(code, rrset, flags));
+        return (RBNodeResultContext(code, rrset, flags, node));
     }
 
     // Implementation of InMemoryZoneFinder::find
-    FindResult find(const Name& name, RRType type,
-                    std::vector<ConstRRsetPtr> *target,
-                    const FindOptions options) const
+    RBNodeResultContext find(const Name& name, RRType type,
+                             std::vector<ConstRRsetPtr>* target,
+                             const FindOptions options) const
     {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
             arg(type);
-        // Get the node
-        DomainNode* node(NULL);
-        FindState state(options);
-        RBTreeNodeChain<Domain> node_path;
-        bool rename(false);
-        switch (zone_data_->domains_.find(name, &node, node_path, cutCallback,
-                                          &state)) {
-            case DomainTree::PARTIALMATCH:
-                /*
-                 * In fact, we could use a single variable instead of
-                 * dname_node_ and zonecut_node_. But then we would need
-                 * to distinquish these two cases by something else and
-                 * it seemed little more confusing to me when I wrote it.
-                 *
-                 * Usually at most one of them will be something else than
-                 * NULL (it might happen both are NULL, in which case we
-                 * consider it NOT FOUND). There's one corner case when
-                 * both might be something else than NULL and it is in case
-                 * there's a DNAME under a zone cut and we search in
-                 * glue OK mode ‒ in that case we don't stop on the domain
-                 * with NS and ignore it for the answer, but it gets set
-                 * anyway. Then we find the DNAME and we need to act by it,
-                 * therefore we first check for DNAME and then for NS. In
-                 * all other cases it doesn't matter, as at least one of them
-                 * is NULL.
-                 */
-                if (state.dname_node_ != NULL) {
-                    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
-                        arg(state.rrset_->getName());
-                    // We were traversing a DNAME node (and wanted to go
-                    // lower below it), so return the DNAME
-                    return (FindResult(DNAME, prepareRRset(name, state.rrset_,
-                                                           false)));
-                }
-                if (state.zonecut_node_ != NULL) {
-                    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
-                        arg(state.rrset_->getName());
-                    return (FindResult(DELEGATION,
-                                       prepareRRset(name, state.rrset_,
-                                                    false)));
-                }
 
-                // If the RBTree search stopped at a node for a super domain
-                // of the search name, it means the search name exists in
-                // the zone but is empty.  Treat it as NXRRSET.
-                if (node_path.getLastComparisonResult().getRelation() ==
-                    NameComparisonResult::SUPERDOMAIN) {
-                    LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
-                        arg(name);
-                    return (createFindResult(NXRRSET, ConstRRsetPtr(), false));
-                }
-
-                /*
-                 * No redirection anywhere. Let's try if it is a wildcard.
-                 *
-                 * The wildcard is checked after the empty non-terminal domain
-                 * case above, because if that one triggers, it means we should
-                 * not match according to 4.3.3 of RFC 1034 (the query name
-                 * is known to exist).
-                 */
-                if (node->getFlag(DOMAINFLAG_WILD)) {
-                    /* Should we cancel this match?
-                     *
-                     * If we compare with some node and get a common ancestor,
-                     * it might mean we are comparing with a non-wildcard node.
-                     * In that case, we check which part is common. If we have
-                     * something in common that lives below the node we got
-                     * (the one above *), then we should cancel the match
-                     * according to section 4.3.3 of RFC 1034 (as the name
-                     * between the wildcard domain and the query name is known
-                     * to exist).
-                     *
-                     * Because the way the tree stores relative names, we will
-                     * have exactly one common label (the ".") in case we have
-                     * nothing common under the node we got and we will get
-                     * more common labels otherwise (yes, this relies on the
-                     * internal RBTree structure, which leaks out through this
-                     * little bit).
-                     *
-                     * If the empty non-terminal node actually exists in the
-                     * tree, then this cancellation is not needed, because we
-                     * will not get here at all.
-                     */
-                    if (node_path.getLastComparisonResult().getRelation() ==
-                        NameComparisonResult::COMMONANCESTOR && node_path.
-                        getLastComparisonResult().getCommonLabels() > 1) {
-                        LOG_DEBUG(logger, DBG_TRACE_DATA,
-                                     DATASRC_MEM_WILDCARD_CANCEL).arg(name);
-                        return (createFindResult(NXDOMAIN, ConstRRsetPtr(),
-                                                 false));
-                    }
-                    const Name wildcard(Name("*").concatenate(
-                        node_path.getAbsoluteName()));
-                    DomainTree::Result result =
-                        zone_data_->domains_.find(wildcard, &node);
-                    /*
-                     * Otherwise, why would the DOMAINFLAG_WILD be there if
-                     * there was no wildcard under it?
-                     */
-                    assert(result == DomainTree::EXACTMATCH);
-                    /*
-                     * We have the wildcard node now. Jump below the switch,
-                     * where handling of the common (exact-match) case is.
-                     *
-                     * However, rename it to the searched name.
-                     */
-                    rename = true;
-                    break;
-                }
-
-                // fall through
-            case DomainTree::NOTFOUND:
-                LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
-                    arg(name);
-                return (createFindResult(NXDOMAIN, ConstRRsetPtr(), false));
-            case DomainTree::EXACTMATCH: // This one is OK, handle it
-                break;
-            default:
-                assert(0);
+        // Get the node.  All other cases than an exact match are handled
+        // in findNode().  We simply construct a result structure and return.
+        const ZoneData::FindNodeResult node_result =
+            zone_data_->findNode<ZoneData::FindNodeResult>(name, options);
+        if (node_result.code != SUCCESS) {
+            return (createFindResult(node_result.code, node_result.rrset));
         }
+
+        // We've found an exact match, may or may not be a result of wildcard.
+        const DomainNode* node = node_result.node;
         assert(node != NULL);
+        const bool rename = ((node_result.flags &
+                              ZoneData::FindNodeResult::FIND_WILDCARD) != 0);
 
         // If there is an exact match but the node is empty, it's equivalent
         // to NXRRSET.
         if (node->isEmpty()) {
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
                 arg(name);
-            return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
+            return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
         }
 
         Domain::const_iterator found;
@@ -799,8 +1268,9 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             if (found != node->getData()->end()) {
                 LOG_DEBUG(logger, DBG_TRACE_DATA,
                           DATASRC_MEM_EXACT_DELEGATION).arg(name);
-                return (FindResult(DELEGATION,
-                                   prepareRRset(name, found->second, rename)));
+                return (createFindResult(DELEGATION,
+                                         prepareRRset(name, found->second,
+                                                      rename, options)));
             }
         }
 
@@ -810,11 +1280,13 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
             for (found = node->getData()->begin();
                  found != node->getData()->end(); ++found)
             {
-                target->push_back(prepareRRset(name, found->second, rename));
+                target->push_back(prepareRRset(name, found->second, rename,
+                                               options));
             }
             LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
                 arg(name);
-            return (createFindResult(SUCCESS, ConstRRsetPtr(), rename));
+            return (createFindResult(SUCCESS, ConstRBNodeRRsetPtr(), rename,
+                                     node));
         }
 
         found = node->getData()->find(type);
@@ -824,25 +1296,28 @@ struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
                 arg(type);
             return (createFindResult(SUCCESS, prepareRRset(name,
                                                            found->second,
-                                                           rename), rename));
+                                                           rename, options),
+                                     rename));
         } else {
             // Next, try CNAME.
             found = node->getData()->find(RRType::CNAME());
             if (found != node->getData()->end()) {
                 LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
                 return (createFindResult(CNAME,
-                                         prepareRRset(name, found->second,
-                                                      rename), rename));
+                                          prepareRRset(name, found->second,
+                                                       rename, options),
+                                          rename));
             }
         }
         // No exact match or CNAME.  Return NXRRSET.
         LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
             arg(name);
-        return (createFindResult(NXRRSET, ConstRRsetPtr(), rename));
+        return (createFindResult(NXRRSET, ConstRBNodeRRsetPtr(), rename));
     }
 };
 
-InMemoryZoneFinder::InMemoryZoneFinder(const RRClass& zone_class, const Name& origin) :
+InMemoryZoneFinder::InMemoryZoneFinder(const RRClass& zone_class,
+                                       const Name& origin) :
     impl_(new InMemoryZoneFinderImpl(zone_class, origin))
 {
     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin).
@@ -865,19 +1340,23 @@ InMemoryZoneFinder::getClass() const {
     return (impl_->zone_class_);
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 InMemoryZoneFinder::find(const Name& name, const RRType& type,
-                 const FindOptions options)
+                         const FindOptions options)
 {
-    return (impl_->find(name, type, NULL, options));
+    return (ZoneFinderContextPtr(
+                new Context(*this, options, impl_->find(name, type, NULL,
+                                                        options))));
 }
 
-ZoneFinder::FindResult
+ZoneFinderContextPtr
 InMemoryZoneFinder::findAll(const Name& name,
                             std::vector<ConstRRsetPtr>& target,
                             const FindOptions options)
 {
-    return (impl_->find(name, RRType::ANY(), &target, options));
+    return (ZoneFinderContextPtr(
+                new Context(*this, options, impl_->find(name, RRType::ANY(),
+                                                        &target, options))));
 }
 
 ZoneFinder::FindNSEC3Result
@@ -899,7 +1378,7 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
     const NameComparisonResult cmp_result = name.compare(impl_->origin_);
     if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
         cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
-        isc_throw(InvalidParameter, "findNSEC3 attempt for out-of-zone name: "
+        isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
                   << name << ", zone: " << impl_->origin_ << "/"
                   << impl_->zone_class_);
     }
@@ -909,7 +1388,7 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
     const unsigned int olabels = impl_->origin_.getLabelCount();
     const unsigned int qlabels = name.getLabelCount();
 
-    ConstRRsetPtr covering_proof; // placeholder of the next closer proof
+    ConstRBNodeRRsetPtr covering_proof; // placeholder of the next closer proof
     // 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.
@@ -959,20 +1438,162 @@ InMemoryZoneFinder::findNSEC3(const Name& name, bool recursive) {
 
 result::Result
 InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
-    return (impl_->add(rrset, *impl_->zone_data_));
+    return (impl_->add(rrset, *impl_->zone_data_, NULL));
 }
 
+namespace {
+// This should eventually be more generalized.
+const Name
+getAdditionalName(RRType rrtype, const rdata::Rdata& rdata) {
+    if (rrtype == RRType::NS()) {
+        const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+        return (ns.getNSName());
+    } else {
+        // In our usage the only other possible case is MX.
+        assert(rrtype == RRType::MX());
+        const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+        return (mx.getMXName());
+    }
+}
 
 void
-InMemoryZoneFinder::load(const string& filename) {
-    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
-        arg(filename);
-    // Load it into temporary zone data
-    scoped_ptr<ZoneData> tmp(new ZoneData(getOrigin()));
+convertAndInsert(const DomainPair& rrset_item, DomainPtr dst_domain,
+                 const Name* dstname)
+{
+    // We copy RRSIGs, too, if they are attached in case we need it in
+    // getAdditional().
+    dst_domain->insert(DomainPair(rrset_item.first,
+                                  prepareRRset(*dstname, rrset_item.second,
+                                               true,
+                                               ZoneFinder::FIND_DNSSEC)));
+}
 
-    masterLoad(filename.c_str(), getOrigin(), getClass(),
-               boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_,
-                           _1, tmp.get()));
+void
+addAdditional(RBNodeRRset* rrset, ZoneData* zone_data,
+              vector<RBNodeRRset*>* wild_rrsets)
+{
+    RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
+    bool match_wild = false;    // will be true if wildcard match is found
+    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+        // For each domain name that requires additional section processing
+        // in each RDATA, search the tree for the name and remember it if
+        // found.  If the name is under a zone cut (for a delegation to a
+        // child zone), mark the node as "GLUE", so we can selectively
+        // include/exclude them when we use it.
+
+        const Name& name = getAdditionalName(rrset->getType(),
+                                             rdata_iterator->getCurrent());
+        // if the name is not in or below this zone, skip it
+        const NameComparisonResult::NameRelation reln =
+            name.compare(zone_data->origin_data_->getName()).getRelation();
+         if (reln != NameComparisonResult::SUBDOMAIN &&
+             reln != NameComparisonResult::EQUAL) {
+            continue;
+        }
+        const ZoneData::FindMutableNodeResult result =
+            zone_data->findNode<ZoneData::FindMutableNodeResult>(
+                name, ZoneFinder::FIND_GLUE_OK);
+        if (result.code != ZoneFinder::SUCCESS) {
+            // We are not interested in anything but a successful match.
+            continue;
+        }
+        DomainNode* node = result.node;
+        assert(node != NULL);
+        if ((result.flags & ZoneData::FindNodeResult::FIND_ZONECUT) != 0 ||
+            (node->getFlag(DomainNode::FLAG_CALLBACK) &&
+             node->getData()->find(RRType::NS()) != node->getData()->end())) {
+            // The node is under or at a zone cut; mark it as a glue.
+            node->setFlag(domain_flag::GLUE);
+        }
+
+        // A rare case: the additional name may have to be expanded with a
+        // wildcard.  We'll store the name in a separate auxiliary tree,
+        // copying all RRsets of the original wildcard node with expanding
+        // the owner name.  This is costly in terms of memory, but this case
+        // should be pretty rare.  On the other hand we won't have to worry
+        // about wildcard expansion in getAdditional, which is quite
+        // performance sensitive.
+        DomainNode* wildnode = NULL;
+        if ((result.flags & ZoneData::FindNodeResult::FIND_WILDCARD) != 0) {
+            // Wildcard and glue shouldn't coexist.  Make it sure here.
+            assert(!node->getFlag(domain_flag::GLUE));
+
+            if (zone_data->getAuxWildDomains().insert(name, &wildnode)
+                == DomainTree::SUCCESS) {
+                // If we first insert the node, copy the RRsets.  If the
+                // original node was empty, we add empty data so
+                // addWildAdditional() can get an exactmatch for this name.
+                DomainPtr dst_domain(new Domain);
+                if (!node->isEmpty()) {
+                    for_each(node->getData()->begin(), node->getData()->end(),
+                             boost::bind(convertAndInsert, _1, dst_domain,
+                                         &name));
+                }
+                wildnode->setData(dst_domain);
+                // Mark the node as "wildcard expanded" so it can be
+                // distinguished at lookup time.
+                wildnode->setFlag(domain_flag::WILD_EXPANDED);
+            }
+            match_wild = true;
+            node = wildnode;
+        }
+
+        // If this name wasn't subject to wildcard substitution, we can add
+        // the additional information to the RRset now; otherwise I'll defer
+        // it until the entire auxiliary tree is built (pointers may be
+        // invalidated as we build it).
+        if (wildnode == NULL) {
+            // Note that node may be empty.  We should keep it in the list
+            // in case we dynamically update the tree and it becomes non empty
+            // (which is not supported yet)
+            rrset->addAdditionalNode(AdditionalNodeInfo(node));
+        }
+    }
+
+    if (match_wild) {
+        wild_rrsets->push_back(rrset);
+    }
+}
+
+void
+addWildAdditional(RBNodeRRset* rrset, ZoneData* zone_data) {
+    // Similar to addAdditional(), but due to the first stage we know that
+    // the rrset should contain a name stored in the auxiliary trees, and
+    // that it should be found as an exact match.  The RRset may have other
+    // names that didn't require wildcard expansion, but we can simply ignore
+    // them in this context.  (Note that if we find an exact match in the
+    // auxiliary tree, it shouldn't be in the original zone; otherwise it
+    // shouldn't have resulted in wildcard in the first place).
+
+    RdataIteratorPtr rdata_iterator = rrset->getRdataIterator();
+    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+        const Name& name = getAdditionalName(rrset->getType(),
+                                             rdata_iterator->getCurrent());
+        DomainNode* wildnode = NULL;
+        if (zone_data->getAuxWildDomains().find(name, &wildnode) ==
+            DomainTree::EXACTMATCH) {
+            rrset->addAdditionalNode(AdditionalNodeInfo(wildnode));
+        }
+    }
+}
+}
+
+void
+InMemoryZoneFinder::InMemoryZoneFinderImpl::load(
+    const string& filename,
+    boost::function<void(LoadCallback)> rrset_installer)
+{
+    vector<RBNodeRRset*> need_additionals;
+    scoped_ptr<ZoneData> tmp(new ZoneData(origin_));
+
+    rrset_installer(boost::bind(&InMemoryZoneFinderImpl::addFromLoad, this,
+                                _1, tmp.get(), &need_additionals));
+
+    vector<RBNodeRRset*> wild_additionals;
+    for_each(need_additionals.begin(), need_additionals.end(),
+             boost::bind(addAdditional, _1, tmp.get(), &wild_additionals));
+    for_each(wild_additionals.begin(), wild_additionals.end(),
+             boost::bind(addWildAdditional, _1, tmp.get()));
 
     // If the zone is NSEC3-signed, check if it has NSEC3PARAM
     if (tmp->nsec3_data_) {
@@ -983,16 +1604,77 @@ InMemoryZoneFinder::load(const string& filename) {
         if (tmp->origin_data_->getData()->find(RRType::NSEC3PARAM()) ==
             tmp->origin_data_->getData()->end()) {
             LOG_WARN(logger, DATASRC_MEM_NO_NSEC3PARAM).
-                arg(getOrigin()).arg(getClass());
+                arg(origin_).arg(zone_class_);
         }
     }
 
     // If it went well, put it inside
-    impl_->file_name_ = filename;
-    tmp.swap(impl_->zone_data_);
+    file_name_ = filename;
+    tmp.swap(zone_data_);
     // And let the old data die with tmp
 }
 
+namespace {
+// A wrapper for dns::masterLoad used by load() below.  Essentially it
+// converts the two callback types.  Note the mostly redundant wrapper of
+// boost::bind.  It converts function<void(ConstRRsetPtr)> to
+// function<void(RRsetPtr)> (masterLoad() expects the latter).  SunStudio
+// doesn't seem to do this conversion if we just pass 'callback'.
+void
+masterLoadWrapper(const char* const filename, const Name& origin,
+                  const RRClass& zone_class, LoadCallback callback)
+{
+    masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
+}
+
+// The installer called from Impl::load() for the iterator version of load().
+void
+generateRRsetFromIterator(ZoneIterator* iterator, LoadCallback callback) {
+    ConstRRsetPtr rrset;
+    vector<ConstRRsetPtr> rrsigs; // placeholder for RRSIGs until "commitable".
+
+    // The current internal implementation assumes an RRSIG is always added
+    // after the RRset they cover.  So we store any RRSIGs in 'rrsigs' until
+    // it's safe to add them; based on our assumption if the owner name
+    // changes, all covered RRsets of the previous name should have been
+    // installed and any pending RRSIGs can be added at that point.  RRSIGs
+    // of the last name from the iterator must be added separately.
+    while ((rrset = iterator->getNextRRset()) != NULL) {
+        if (!rrsigs.empty() && rrset->getName() != rrsigs[0]->getName()) {
+            BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
+                callback(sig_rrset);
+            }
+            rrsigs.clear();
+        }
+        if (rrset->getType() == RRType::RRSIG()) {
+            rrsigs.push_back(rrset);
+        } else {
+            callback(rrset);
+        }
+    }
+
+    BOOST_FOREACH(ConstRRsetPtr sig_rrset, rrsigs) {
+        callback(sig_rrset);
+    }
+}
+}
+
+void
+InMemoryZoneFinder::load(const string& filename) {
+    LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
+        arg(filename);
+
+    impl_->load(filename,
+                boost::bind(masterLoadWrapper, filename.c_str(), getOrigin(),
+                            getClass(), _1));
+}
+
+void
+InMemoryZoneFinder::load(ZoneIterator& iterator) {
+    impl_->load(string(),
+                boost::bind(generateRRsetFromIterator, &iterator, _1));
+}
+
 void
 InMemoryZoneFinder::swap(InMemoryZoneFinder& zone_finder) {
     LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()).
@@ -1198,147 +1880,5 @@ InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
               "in memory data source");
 }
 
-namespace {
-// convencience function to add an error message to a list of those
-// (TODO: move functions like these to some util lib?)
-void
-addError(ElementPtr errors, const std::string& error) {
-    if (errors != ElementPtr() && errors->getType() == Element::list) {
-        errors->add(Element::create(error));
-    }
-}
-
-/// Check if the given element exists in the map, and if it is a string
-bool
-checkConfigElementString(ConstElementPtr config, const std::string& name,
-                         ElementPtr errors)
-{
-    if (!config->contains(name)) {
-        addError(errors,
-                 "Config for memory backend does not contain a '"
-                 +name+
-                 "' value");
-        return false;
-    } else if (!config->get(name) ||
-               config->get(name)->getType() != Element::string) {
-        addError(errors, "value of " + name +
-                 " in memory backend config is not a string");
-        return false;
-    } else {
-        return true;
-    }
-}
-
-bool
-checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
-    bool result = true;
-    if (!config || config->getType() != Element::map) {
-        addError(errors, "Elements in memory backend's zone list must be maps");
-        result = false;
-    } else {
-        if (!checkConfigElementString(config, "origin", errors)) {
-            result = false;
-        }
-        if (!checkConfigElementString(config, "file", errors)) {
-            result = false;
-        }
-        // we could add some existence/readabilty/parsability checks here
-        // if we want
-    }
-    return result;
-}
-
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
-    /* Specific configuration is under discussion, right now this accepts
-     * the 'old' configuration, see [TODO]
-     * So for memory datasource, we get a structure like this:
-     * { "type": string ("memory"),
-     *   "class": string ("IN"/"CH"/etc),
-     *   "zones": list
-     * }
-     * Zones list is a list of maps:
-     * { "origin": string,
-     *     "file": string
-     * }
-     *
-     * At this moment we cannot be completely sure of the contents of the
-     * structure, so we have to do some more extensive tests than should
-     * strictly be necessary (e.g. existence and type of elements)
-     */
-    bool result = true;
-
-    if (!config || config->getType() != Element::map) {
-        addError(errors, "Base config for memory backend must be a map");
-        result = false;
-    } else {
-        if (!checkConfigElementString(config, "type", errors)) {
-            result = false;
-        } else {
-            if (config->get("type")->stringValue() != "memory") {
-                addError(errors,
-                         "Config for memory backend is not of type \"memory\"");
-                result = false;
-            }
-        }
-        if (!checkConfigElementString(config, "class", errors)) {
-            result = false;
-        } else {
-            try {
-                RRClass rrc(config->get("class")->stringValue());
-            } catch (const isc::Exception& rrce) {
-                addError(errors,
-                         "Error parsing class config for memory backend: " +
-                         std::string(rrce.what()));
-                result = false;
-            }
-        }
-        if (!config->contains("zones")) {
-            addError(errors, "No 'zones' element in memory backend config");
-            result = false;
-        } else if (!config->get("zones") ||
-                   config->get("zones")->getType() != Element::list) {
-            addError(errors, "'zones' element in memory backend config is not a list");
-            result = false;
-        } else {
-            BOOST_FOREACH(ConstElementPtr zone_config,
-                          config->get("zones")->listValue()) {
-                if (!checkZoneConfig(zone_config, errors)) {
-                    result = false;
-                }
-            }
-        }
-    }
-
-    return (result);
-    return true;
-}
-
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
-    ElementPtr errors(Element::createList());
-    if (!checkConfig(config, errors)) {
-        error = "Configuration error: " + errors->str();
-        return (NULL);
-    }
-    try {
-        return (new InMemoryClient());
-    } catch (const std::exception& exc) {
-        error = std::string("Error creating memory datasource: ") + exc.what();
-        return (NULL);
-    } catch (...) {
-        error = std::string("Error creating memory datasource, "
-                            "unknown exception");
-        return (NULL);
-    }
-}
-
-void destroyInstance(DataSourceClient* instance) {
-    delete instance;
-}
-
-
 } // end of namespace datasrc
 } // end of namespace isc
diff --git a/src/lib/datasrc/memory_datasrc.h b/src/lib/datasrc/memory_datasrc.h
index b960ab9..c687d1b 100644
--- a/src/lib/datasrc/memory_datasrc.h
+++ b/src/lib/datasrc/memory_datasrc.h
@@ -70,18 +70,20 @@ public:
     /// See documentation in \c Zone.
     ///
     /// It returns NULL pointer in case of NXDOMAIN and NXRRSET.
-    virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type,
-                            const FindOptions options = FIND_DEFAULT);
+    virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
+                                      const isc::dns::RRType& type,
+                                      const FindOptions options =
+                                      FIND_DEFAULT);
 
     /// \brief Version of find that returns all types at once
     ///
     /// It acts the same as find, just that when the correct node is found,
     /// all the RRsets are filled into the target parameter instead of being
     /// returned by the result.
-    virtual FindResult findAll(const isc::dns::Name& name,
-                               std::vector<isc::dns::ConstRRsetPtr>& target,
-                               const FindOptions options = FIND_DEFAULT);
+    virtual ZoneFinderContextPtr findAll(
+        const isc::dns::Name& name,
+        std::vector<isc::dns::ConstRRsetPtr>& target,
+        const FindOptions options = FIND_DEFAULT);
 
     /// Look for NSEC3 for proving (non)existence of given name.
     ///
@@ -123,16 +125,6 @@ public:
     ///    exists).
     result::Result add(const isc::dns::ConstRRsetPtr& rrset);
 
-    /// \brief RRSet out of zone exception.
-    ///
-    /// This is thrown if addition of an RRset that doesn't belong under the
-    /// zone's origin is requested.
-    struct OutOfZone : public InvalidParameter {
-        OutOfZone(const char* file, size_t line, const char* what) :
-            InvalidParameter(file, line, what)
-        { }
-    };
-
     /// \brief RRset is NULL exception.
     ///
     /// This is thrown if the provided RRset parameter is NULL.
@@ -196,6 +188,26 @@ public:
     ///     configuration reloading is written.
     void load(const std::string& filename);
 
+    /// \brief Load zone from another data source.
+    ///
+    /// This is similar to the other version, but zone's RRsets are provided
+    /// by an iterator of another data source.  On successful load, the
+    /// internal filename will be cleared.
+    ///
+    /// This implementation assumes the iterator produces combined RRsets,
+    /// that is, there should exactly one RRset for the same owner name and
+    /// RR type.  This means the caller is expected to create the iterator
+    /// with \c separate_rrs being \c false.  This implementation also assumes
+    /// RRsets of different names are not mixed; so if the iterator produces
+    /// an RRset of a different name than that of the previous RRset, that
+    /// previous name must never appear in the subsequent sequence of RRsets.
+    /// Note that the iterator API does not ensure this.  If the underlying
+    /// implementation does not follow it, load() will fail.  Note, however,
+    /// that this whole interface is tentative.  in-memory zone loading will
+    /// have to be revisited fundamentally, and at that point this restriction
+    /// probably won't matter.
+    void load(ZoneIterator& iterator);
+
     /// Exchanges the content of \c this zone finder with that of the given
     /// \c zone_finder.
     ///
@@ -216,6 +228,12 @@ private:
     // extracts the pointer to data and puts it into the iterator.
     // The access is read only.
     friend class InMemoryClient;
+
+    /// \brief In-memory version of finder context.
+    ///
+    /// The implementation (and any specialized interface) is completely local
+    /// to the InMemoryZoneFinder class, so it's defined as private
+    class Context;
 };
 
 /// \brief A data source client that holds all necessary data in memory.
diff --git a/src/lib/datasrc/memory_datasrc_link.cc b/src/lib/datasrc/memory_datasrc_link.cc
new file mode 100644
index 0000000..a0b4bf6
--- /dev/null
+++ b/src/lib/datasrc/memory_datasrc_link.cc
@@ -0,0 +1,173 @@
+// 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 <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/client.h>
+#include <datasrc/memory_datasrc.h>
+
+#include <boost/foreach.hpp>
+
+#include <string>
+
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+// convencience function to add an error message to a list of those
+// (TODO: move functions like these to some util lib?)
+void
+addError(ElementPtr errors, const std::string& error) {
+    if (errors != ElementPtr() && errors->getType() == Element::list) {
+        errors->add(Element::create(error));
+    }
+}
+
+/// Check if the given element exists in the map, and if it is a string
+bool
+checkConfigElementString(ConstElementPtr config, const std::string& name,
+                         ElementPtr errors)
+{
+    if (!config->contains(name)) {
+        addError(errors,
+                 "Config for memory backend does not contain a '"
+                 +name+
+                 "' value");
+        return false;
+    } else if (!config->get(name) ||
+               config->get(name)->getType() != Element::string) {
+        addError(errors, "value of " + name +
+                 " in memory backend config is not a string");
+        return false;
+    } else {
+        return true;
+    }
+}
+
+bool
+checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
+    bool result = true;
+    if (!config || config->getType() != Element::map) {
+        addError(errors, "Elements in memory backend's zone list must be maps");
+        result = false;
+    } else {
+        if (!checkConfigElementString(config, "origin", errors)) {
+            result = false;
+        }
+        if (!checkConfigElementString(config, "file", errors)) {
+            result = false;
+        }
+        // we could add some existence/readabilty/parsability checks here
+        // if we want
+    }
+    return result;
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+    /* Specific configuration is under discussion, right now this accepts
+     * the 'old' configuration, see [TODO]
+     * So for memory datasource, we get a structure like this:
+     * { "type": string ("memory"),
+     *   "class": string ("IN"/"CH"/etc),
+     *   "zones": list
+     * }
+     * Zones list is a list of maps:
+     * { "origin": string,
+     *     "file": string
+     * }
+     *
+     * At this moment we cannot be completely sure of the contents of the
+     * structure, so we have to do some more extensive tests than should
+     * strictly be necessary (e.g. existence and type of elements)
+     */
+    bool result = true;
+
+    if (!config || config->getType() != Element::map) {
+        addError(errors, "Base config for memory backend must be a map");
+        result = false;
+    } else {
+        if (!checkConfigElementString(config, "type", errors)) {
+            result = false;
+        } else {
+            if (config->get("type")->stringValue() != "memory") {
+                addError(errors,
+                         "Config for memory backend is not of type \"memory\"");
+                result = false;
+            }
+        }
+        if (!checkConfigElementString(config, "class", errors)) {
+            result = false;
+        } else {
+            try {
+                RRClass rrc(config->get("class")->stringValue());
+            } catch (const isc::Exception& rrce) {
+                addError(errors,
+                         "Error parsing class config for memory backend: " +
+                         std::string(rrce.what()));
+                result = false;
+            }
+        }
+        if (!config->contains("zones")) {
+            addError(errors, "No 'zones' element in memory backend config");
+            result = false;
+        } else if (!config->get("zones") ||
+                   config->get("zones")->getType() != Element::list) {
+            addError(errors, "'zones' element in memory backend config is not a list");
+            result = false;
+        } else {
+            BOOST_FOREACH(ConstElementPtr zone_config,
+                          config->get("zones")->listValue()) {
+                if (!checkZoneConfig(zone_config, errors)) {
+                    result = false;
+                }
+            }
+        }
+    }
+
+    return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+    ElementPtr errors(Element::createList());
+    if (!checkConfig(config, errors)) {
+        error = "Configuration error: " + errors->str();
+        return (NULL);
+    }
+    try {
+        return (new isc::datasrc::InMemoryClient());
+    } catch (const std::exception& exc) {
+        error = std::string("Error creating memory datasource: ") + exc.what();
+        return (NULL);
+    } catch (...) {
+        error = std::string("Error creating memory datasource, "
+                            "unknown exception");
+        return (NULL);
+    }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+    delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/rbnode_rrset.h b/src/lib/datasrc/rbnode_rrset.h
new file mode 100644
index 0000000..3e5d20a
--- /dev/null
+++ b/src/lib/datasrc/rbnode_rrset.h
@@ -0,0 +1,228 @@
+// 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 __RBNODE_RRSET_H
+#define __RBNODE_RRSET_H
+
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+#include <dns/rrtype.h>
+#include <util/buffer.h>
+
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace datasrc {
+namespace internal {
+
+/// \brief The actual content of \c RBNodeRRset
+///
+///  This is defined in the namespace-scope (not hidden in the main class)
+/// so that the In-memory data source implementation can refer to it.
+struct RBNodeRRsetImpl;
+
+// Forward declaration of an opaque data type defined and used within the
+// implementation.  This is public only because it needs to be used within
+// the in-memory data source implementation, but conceptually this is a
+// private type for the in-memory data source implementation.
+// Note that the definition of the structure is still hidden within the
+// implementation, so, basically, a normal application should never be able
+// to use it directly even if it peeks into the "internal" namespace.
+struct AdditionalNodeInfo;
+
+/// \brief Special RRset for optimizing memory datasource requirement
+///
+/// To speed up the performance of the in-memory data source, at load time
+/// associate relevant "additional section" data with each RRset in the
+/// data source.
+///
+/// This class, derived from AbstractRRset, holds a "const" pointer to the
+/// underlying RRset object.  All calls to methods on the class are passed to
+/// the underlying object.  However, there are some restrictions:
+///
+/// - Calls to methods that change attributes of the underlying RRset (such as
+///   TTL or Name) cause an exception to be thrown.  The in-memory data source
+///   does not allow modification of these attributes.  In theory, it is a bad
+///   practice in that it doesn't preserve the assumed behavior of the base
+///   class.  In practice, however, it should be acceptable because this
+///   class is effectively hidden from applications and will only be given
+///   to them as a const pointer to the base class via find() variants.
+///   So the application cannot call non const methods anyway unless it
+///   intentionally breaks the constness.
+///
+/// - Calls that add the pointer to the associated RRSIG to the RRset are
+///   allowed (even though the pointer is to a "const" RRset).  The reason here
+///   is that RRSIGs are added to the in-memory data source after the
+///   RBNodeRRset objects have been created.  Thus there has to be the
+///   capability of modifying this information.
+///
+/// The class is not derived from RRset itself to simplify coding: part of the
+/// loading of the memory data source is handled in the BIND 10 "libdns++"
+/// code, which creates RRsets and passes them to the data source code.  This
+/// does not have to be altered if encapsulation, rather than inheritance, is
+/// used.
+///
+/// \note This class is exposed in this separate header file so that test code
+/// can refer to its definition, and only for that purpose.  Otherwise this is
+/// essentially a private class of the in-memory data source implementation,
+/// and an application shouldn't directly refer to this class.
+/// 
+// Note: non-Doxygen-documented methods are documented in the base class.
+
+class RBNodeRRset : public isc::dns::AbstractRRset {
+
+private:
+    // Note: The copy constructor and the assignment operator are intentionally
+    // defined as private as we would normally not duplicate a RBNodeRRset.
+    // (We use the "private" method instead of inheriting from
+    // boost::noncopyable so as to avoid multiple inheritance.)
+    RBNodeRRset(const RBNodeRRset& source);
+    RBNodeRRset& operator=(const RBNodeRRset& source);
+
+public:
+    /// \brief Usual Constructor
+    ///
+    /// Creates an RBNodeRRset from the pointer to the RRset passed to it.
+    ///
+    /// \param rrset Pointer to underlying RRset encapsulated by this object.
+    explicit RBNodeRRset(const isc::dns::ConstRRsetPtr& rrset);
+
+    /// \brief Destructor
+    virtual ~RBNodeRRset();
+
+    // Getter and Setter Methods
+    //
+    // The getter methods pass the call through to the underlying RRset.  The
+    // setter methods thrown an exception - this specialisation of the RRset
+    // object does not expect the underlying RRset to be modified.
+
+    virtual unsigned int getRdataCount() const;
+
+    virtual const isc::dns::Name& getName() const;
+
+    virtual const isc::dns::RRClass& getClass() const;
+
+    virtual const isc::dns::RRType& getType() const;
+
+    virtual const isc::dns::RRTTL& getTTL() const;
+
+    virtual void setName(const isc::dns::Name&);
+
+    virtual void setTTL(const isc::dns::RRTTL&);
+
+    virtual std::string toText() const;
+
+    virtual bool isSameKind(const AbstractRRset& other) const {
+        // This code is an optimisation for comparing
+        // RBNodeRRsets. However, in doing this optimisation,
+        // semantically the code is not "is same kind" but is instead
+        // "is identical object" in the case where RBNodeRRsets are compared.
+
+        const RBNodeRRset* rb = dynamic_cast<const RBNodeRRset*>(&other);
+        if (rb != NULL) {
+            return (this == rb);
+        } else {
+            return (AbstractRRset::isSameKind(other));
+        }
+    }
+
+    virtual unsigned int toWire(
+        isc::dns::AbstractMessageRenderer& renderer) const;
+
+    virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const;
+
+    virtual void addRdata(isc::dns::rdata::ConstRdataPtr);
+
+    virtual void addRdata(const isc::dns::rdata::Rdata&);
+
+    virtual isc::dns::RdataIteratorPtr getRdataIterator() const;
+
+    virtual isc::dns::RRsetPtr getRRsig() const;
+
+    // With all the RRsig methods, we have the problem that we store the
+    // underlying RRset using a ConstRRsetPtr - a pointer to a "const" RRset -
+    // but we need to modify it by adding or removing an RRSIG.  We overcome
+    // this by temporarily violating the "const" nature of the RRset to add the
+    // data.
+
+    virtual void addRRsig(const isc::dns::rdata::ConstRdataPtr& rdata);
+
+    virtual void addRRsig(const isc::dns::rdata::RdataPtr& rdata);
+
+    virtual void addRRsig(const AbstractRRset& sigs);
+
+    virtual void addRRsig(const isc::dns::ConstRRsetPtr& sigs);
+
+    virtual void addRRsig(const isc::dns::RRsetPtr& sigs);
+
+    virtual void removeRRsig();
+
+    /// \brief Associate a link to an RB node of the additional record.
+    ///
+    /// This method adds a given opaque object that holds a link to an RB node
+    /// of the underlying in-memory data source that is corresponding to an
+    /// RDATA of this RRset.
+    ///
+    /// This method is exposed as public so it can be used within the in-memory
+    /// data source implementation, and only for that purpose.
+    ///
+    /// \param additional An opaque \c AdditionalNodeInfo object to be
+    /// associated with this RRset.
+    void addAdditionalNode(const AdditionalNodeInfo& additional);
+
+    /// \brief Return a pointer to the list (vector) of additional RB nodes.
+    ///
+    /// This method returns a pointer to a vector storing the opaque
+    /// \c AdditionalNodeInfo object that may be possibly set in this RRset.
+    /// Not all RRsets are associated with additional nodes; if no
+    /// such node is stored, this method returns NULL.
+    ///
+    /// Like \c addAdditionalNode(), this method is exposed as public only for
+    /// the in-memory data source implementation.
+    ///
+    /// \return A pointer to the associated vector of \c AdditionalNodeInfo;
+    /// NULL if no additional nodes are associated to this RRset.
+    const std::vector<AdditionalNodeInfo>* getAdditionalNodes() const;
+
+    /// \brief Copy the list of additional RB nodes to another RRset.
+    ///
+    /// This method copies the internal list (an STL vector in the actual
+    /// implementation) of additional RB nodes for this RRset to another
+    /// \c RBNodeRRset object.  The copy destination is generally expected to
+    /// be newly created and have an empty list, but this method does not
+    /// check the condition.  If the destination already has a non empty list,
+    /// the existing entries will be lost.
+    ///
+    /// \param dst The \c RBNodeRRset object to which the additional
+    /// RB node list is to be copied.
+    void copyAdditionalNodes(RBNodeRRset& dst) const;
+
+    /// \brief Return underlying RRset pointer
+    ///
+    /// ... mainly for testing.
+    isc::dns::ConstRRsetPtr getUnderlyingRRset() const;
+
+private:
+    RBNodeRRsetImpl* impl_;
+};
+
+}   // namespace internal
+}   // namespace datasrc
+}   // namespace isc
+
+#endif  // __RBNODE_RRSET_H
diff --git a/src/lib/datasrc/rbtree.h b/src/lib/datasrc/rbtree.h
index 4757a45..dbf0591 100644
--- a/src/lib/datasrc/rbtree.h
+++ b/src/lib/datasrc/rbtree.h
@@ -123,7 +123,9 @@ public:
     /// set to on by the \c setFlag() method.
     enum Flags {
         FLAG_CALLBACK = 1, ///< Callback enabled. See \ref callback
-        FLAG_USER1 = 0x80000000U ///< Application specific flag
+        FLAG_USER1 = 0x80000000U, ///< Application specific flag
+        FLAG_USER2 = 0x40000000U, ///< Application specific flag
+        FLAG_USER3 = 0x20000000U  ///< Application specific flag
     };
 private:
     // Some flag values are expected to be used for internal purposes
@@ -131,7 +133,8 @@ private:
     // limit the settable flags via the \c setFlag() method to those
     // explicitly defined in \c Flags.  This constant represents all
     // such flags.
-    static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1);
+    static const uint32_t SETTABLE_FLAGS = (FLAG_CALLBACK | FLAG_USER1 |
+                                            FLAG_USER2 | FLAG_USER3);
 
 public:
 
@@ -1159,6 +1162,10 @@ RBTree<T>::insert(const isc::dns::Name& target_name, RBNode<T>** new_node) {
 }
 
 
+// Note: when we redesign this (still keeping the basic concept), we should
+// change this part so the newly created node will be used for the inserted
+// name (and therefore the name for the existing node doesn't change).
+// Otherwise, things like shortcut links between nodes won't work.
 template <typename T>
 void
 RBTree<T>::nodeFission(RBNode<T>& node, const isc::dns::Name& base_name) {
diff --git a/src/lib/datasrc/sqlite3_accessor.cc b/src/lib/datasrc/sqlite3_accessor.cc
index fb2ffef..ba21de8 100644
--- a/src/lib/datasrc/sqlite3_accessor.cc
+++ b/src/lib/datasrc/sqlite3_accessor.cc
@@ -15,9 +15,10 @@
 #include <sqlite3.h>
 
 #include <string>
+#include <utility>
 #include <vector>
 
-#include <boost/foreach.hpp>
+#include <exceptions/exceptions.h>
 
 #include <datasrc/sqlite3_accessor.h>
 #include <datasrc/logger.h>
@@ -29,9 +30,20 @@
 using namespace std;
 using namespace isc::data;
 
-#define SQLITE_SCHEMA_VERSION 1
-
-#define CONFIG_ITEM_DATABASE_FILE "database_file"
+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;
+}
 
 namespace isc {
 namespace datasrc {
@@ -54,11 +66,16 @@ enum StatementID {
     ITERATE = 9,
     FIND_PREVIOUS = 10,
     ADD_RECORD_DIFF = 11,
-    GET_RECORD_DIFF = 12,       // This is temporary for testing "add diff"
-    LOW_DIFF_ID = 13,
-    HIGH_DIFF_ID = 14,
-    DIFF_RECS = 15,
-    NUM_STATEMENTS = 16
+    LOW_DIFF_ID = 12,
+    HIGH_DIFF_ID = 13,
+    DIFF_RECS = 14,
+    NSEC3 = 15,
+    NSEC3_PREVIOUS = 16,
+    NSEC3_LAST = 17,
+    ADD_NSEC3_RECORD = 18,
+    DEL_ZONE_NSEC3_RECORDS = 19,
+    DEL_NSEC3_RECORD = 20,
+    NUM_STATEMENTS = 21
 };
 
 const char* const text_statements[NUM_STATEMENTS] = {
@@ -78,8 +95,19 @@ const char* const text_statements[NUM_STATEMENTS] = {
         "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
     "DELETE FROM records WHERE zone_id=?1 AND name=?2 " // DEL_RECORD
         "AND rdtype=?3 AND rdata=?4",
-    "SELECT rdtype, ttl, sigtype, rdata, name FROM records " // ITERATE
-        "WHERE zone_id = ?1 ORDER BY rname, rdtype",
+    // The following iterates the whole zone. As the NSEC3 records
+    // (and corresponding RRSIGs) live in separate table, we need to
+    // take both of them. As the RRSIGs are for NSEC3s in the other
+    // table, we can easily hardcode the sigtype.
+    //
+    // The extra column is so we can order it by rname. This is to
+    // preserve the previous order, mostly for tests.
+    // TODO: Is it possible to get rid of the ordering?
+    "SELECT rdtype, ttl, sigtype, rdata, name, rname FROM records " // ITERATE
+        "WHERE zone_id = ?1 "
+        "UNION "
+        "SELECT rdtype, ttl, \"NSEC3\", rdata, owner, owner FROM nsec3 "
+        "WHERE zone_id = ?1 ORDER by rname, rdtype",
     /*
      * This one looks for previous name with NSEC record. It is done by
      * using the reversed name. The NSEC is checked because we need to
@@ -87,12 +115,10 @@ const char* const text_statements[NUM_STATEMENTS] = {
      */
     "SELECT name FROM records " // FIND_PREVIOUS
         "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
-        "rname < $2 ORDER BY rname DESC LIMIT 1",
+        "rname < ?2 ORDER BY rname DESC LIMIT 1",
     "INSERT INTO diffs "        // ADD_RECORD_DIFF
         "(zone_id, version, operation, name, rrtype, ttl, rdata) "
         "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
-    "SELECT name, rrtype, ttl, rdata, version, operation " // GET_RECORD_DIFF
-        "FROM diffs WHERE zone_id = ?1 ORDER BY id, operation",
 
     // Two statements to select the lowest ID and highest ID in a set of
     // differences.
@@ -107,13 +133,35 @@ const char* const text_statements[NUM_STATEMENTS] = {
     // that the columns match the column IDs passed to the iterator
     "SELECT rrtype, ttl, id, rdata, name FROM diffs "   // DIFF_RECS
         "WHERE zone_id=?1 AND id>=?2 and id<=?3 "
-        "ORDER BY id ASC"
+        "ORDER BY id ASC",
+
+    // NSEC3: Query to get the NSEC3 records
+    //
+    // The "1" in SELECT is for positioning the rdata column to the
+    // expected position, so we can reuse the same code as for other
+    // lookups.
+    "SELECT rdtype, ttl, 1, rdata FROM nsec3 WHERE zone_id=?1 AND "
+        "hash=?2",
+    // NSEC3_PREVIOUS: For getting the previous NSEC3 hash
+    "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 AND hash < ?2 "
+        "ORDER BY hash DESC LIMIT 1",
+    // NSEC3_LAST: And for wrap-around
+    "SELECT DISTINCT hash FROM nsec3 WHERE zone_id=?1 "
+        "ORDER BY hash DESC LIMIT 1",
+    // ADD_NSEC3_RECORD: Add NSEC3-related (NSEC3 or NSEC3-covering RRSIG) RR
+    "INSERT INTO nsec3 (zone_id, hash, owner, ttl, rdtype, rdata) "
+    "VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
+    // DEL_ZONE_NSEC3_RECORDS: delete all NSEC3-related records from the zone
+    "DELETE FROM nsec3 WHERE zone_id=?1",
+    // DEL_NSEC3_RECORD: delete specified NSEC3-related records
+    "DELETE FROM nsec3 WHERE zone_id=?1 AND hash=?2 "
+    "AND rdtype=?3 AND rdata=?4"
 };
 
 struct SQLite3Parameters {
     SQLite3Parameters() :
-        db_(NULL), version_(-1), in_transaction(false), updating_zone(false),
-        updated_zone_id(-1)
+        db_(NULL), major_version_(-1), minor_version_(-1),
+        in_transaction(false), updating_zone(false), updated_zone_id(-1)
     {
         for (int i = 0; i < NUM_STATEMENTS; ++i) {
             statements_[i] = NULL;
@@ -151,10 +199,12 @@ struct SQLite3Parameters {
     }
 
     sqlite3* db_;
-    int version_;
+    int major_version_;
+    int minor_version_;
     bool in_transaction; // whether or not a transaction has been started
     bool updating_zone;          // whether or not updating the zone
     int updated_zone_id;        // valid only when in_transaction is true
+    string updated_zone_origin_; // ditto, and only needed to handle NSEC3s
 private:
     // statements_ are private and must be accessed via getStatement() outside
     // of this structure.
@@ -169,6 +219,10 @@ private:
 // statement, which is completed with a single "step" (normally within a
 // single call to an SQLite3Database method).  In particular, it cannot be
 // used for "SELECT" variants, which generally expect multiple matching rows.
+//
+// The bindXXX methods are straightforward wrappers for the corresponding
+// sqlite3_bind_xxx functions that make bindings with the given parameters
+// on the statement maintained in this class.
 class StatementProcessor {
 public:
     // desc will be used on failure in the what() message of the resulting
@@ -185,6 +239,33 @@ public:
         sqlite3_reset(stmt_);
     }
 
+    void bindInt(int index, int val) {
+        if (sqlite3_bind_int(stmt_, index, val) != SQLITE_OK) {
+            isc_throw(DataSourceError,
+                      "failed to bind SQLite3 parameter: " <<
+                      sqlite3_errmsg(dbparameters_.db_));
+        }
+    }
+
+    void bindInt64(int index, sqlite3_int64 val) {
+        if (sqlite3_bind_int64(stmt_, index, val) != SQLITE_OK) {
+            isc_throw(DataSourceError,
+                      "failed to bind SQLite3 parameter: " <<
+                      sqlite3_errmsg(dbparameters_.db_));
+        }
+    }
+
+    // For simplicity, we assume val is a NULL-terminated string, and the
+    // entire non NUL characters are to be bound.  The destructor parameter
+    // is normally either SQLITE_TRANSIENT or SQLITE_STATIC.
+    void bindText(int index, const char* val, void(*destructor)(void*)) {
+        if (sqlite3_bind_text(stmt_, index, val, -1, destructor)
+            != SQLITE_OK) {
+            isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
+                      sqlite3_errmsg(dbparameters_.db_));
+        }
+    }
+
     void exec() {
         if (sqlite3_step(stmt_) != SQLITE_DONE) {
             sqlite3_reset(stmt_);
@@ -242,34 +323,42 @@ public:
 };
 
 const char* const SCHEMA_LIST[] = {
-    "CREATE TABLE schema_version (version INTEGER NOT NULL)",
-    "INSERT INTO schema_version VALUES (1)",
+    "CREATE TABLE schema_version (version INTEGER NOT NULL, "
+        "minor INTEGER NOT NULL DEFAULT 0)",
+    "INSERT INTO schema_version VALUES (2, 0)",
     "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
-    "name STRING NOT NULL COLLATE NOCASE, "
-    "rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN', "
+    "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 STRING NOT NULL COLLATE NOCASE, "
-        "rname STRING NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
-        "rdtype STRING NOT NULL COLLATE NOCASE, sigtype STRING COLLATE NOCASE, "
-        "rdata STRING NOT NULL)",
+        "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)",
+    // The next index is a tricky one.  It's necessary for
+    // FIND_PREVIOUS to use the index efficiently; since there's an
+    // "inequality", the rname column must be placed later.  records_byrname
+    // may not be sufficient especially when the zone is not signed (and
+    // defining a separate index for rdtype only doesn't work either; SQLite3
+    // would then create a temporary B-tree for "ORDER BY").
+    "CREATE INDEX records_bytype_and_rname ON records (rdtype, rname)",
     "CREATE TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
-        "hash STRING NOT NULL COLLATE NOCASE, "
-        "owner STRING NOT NULL COLLATE NOCASE, "
-        "ttl INTEGER NOT NULL, rdtype STRING NOT NULL COLLATE NOCASE, "
-        "rdata STRING 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 TABLE diffs (id INTEGER PRIMARY KEY, "
         "zone_id INTEGER NOT NULL, "
         "version INTEGER NOT NULL, "
         "operation INTEGER NOT NULL, "
-        "name STRING NOT NULL COLLATE NOCASE, "
-        "rrtype STRING NOT NULL COLLATE NOCASE, "
+        "name TEXT NOT NULL COLLATE NOCASE, "
+        "rrtype TEXT NOT NULL COLLATE NOCASE, "
         "ttl INTEGER NOT NULL, "
-        "rdata STRING NOT NULL)",
+        "rdata TEXT NOT NULL)",
     NULL
 };
 
@@ -295,14 +384,13 @@ void doSleep() {
 
 // returns the schema version if the schema version table exists
 // returns -1 if it does not
-int checkSchemaVersion(sqlite3* db) {
+int checkSchemaVersionElement(sqlite3* db, const char* const 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, "SELECT version FROM schema_version",
-                                    -1, &prepared, NULL);
+        int rc = sqlite3_prepare_v2(db, query, -1, &prepared, NULL);
         if (rc == SQLITE_ERROR) {
             // this is the error that is returned when the table does not
             // exist
@@ -324,50 +412,116 @@ int checkSchemaVersion(sqlite3* db) {
     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> checkSchemaVersion(sqlite3* db) {
+    int major = checkSchemaVersionElement(db,
+        "SELECT version FROM schema_version");
+    if (major == -1) {
+        return (make_pair(-1, -1));
+    } else if (major == 1) {
+        return (make_pair(1, 0));
+    } else {
+        int minor = checkSchemaVersionElement(db,
+            "SELECT minor FROM schema_version");
+        return (make_pair(major, minor));
+    }
+}
+
+// A helper class used in createDatabase() 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));
+            }
+            doSleep();
+        }
+        // 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
-int create_database(sqlite3* db) {
+pair<int, int>
+createDatabase(sqlite3* db) {
+    logger.info(DATASRC_SQLITE_SETUP);
+
     // try to get an exclusive lock. Once that is obtained, do the version
     // check *again*, just in case this process was racing another
-    //
-    // try for 5 secs (50*0.1)
-    int rc;
-    logger.info(DATASRC_SQLITE_SETUP);
-    for (size_t i = 0; i < 50; ++i) {
-        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));
-        }
-        doSleep();
-    }
-    int schema_version = checkSchemaVersion(db);
-    if (schema_version == -1) {
+    ScopedTransaction trasaction(db);
+    pair<int, int> schema_version = checkSchemaVersion(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]);
+                          "Failed to set up schema " << SCHEMA_LIST[i]);
             }
         }
-        sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL);
-        return (SQLITE_SCHEMA_VERSION);
-    } else {
-        return (schema_version);
+        trasaction.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 = checkSchemaVersion(db);
     }
+
+    return (schema_version);
 }
 
 void
 checkAndSetupSchema(Initializer* initializer) {
     sqlite3* const db = initializer->params_.db_;
 
-    int schema_version = checkSchemaVersion(db);
-    if (schema_version != SQLITE_SCHEMA_VERSION) {
-        schema_version = create_database(db);
-    }
-    initializer->params_.version_ = schema_version;
+    pair<int, int> schema_version = checkSchemaVersion(db);
+    if (schema_version.first == -1) {
+        schema_version = createDatabase(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 SQLite3 database version: " <<
+                  schema_version.first << "." << schema_version.second);
+    } 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;
 }
 
 }
@@ -486,19 +640,46 @@ public:
         bindZoneId(id);
     }
 
+    // What kind of query it is - selection of the statement for DB
+    enum QueryType {
+        QT_ANY, // Directly for a domain
+        QT_SUBDOMAINS, // Subdomains of a given domain
+        QT_NSEC3 // Domain in the NSEC3 namespace (the name is is the hash,
+                 // not the whole name)
+    };
+
     // Construct an iterator for records with a specific name. When constructed
     // this way, the getNext() call will copy all fields except name
     Context(const boost::shared_ptr<const SQLite3Accessor>& accessor, int id,
-            const std::string& name, bool subdomains) :
-        iterator_type_(ITT_NAME),
+            const std::string& name, QueryType qtype) :
+        iterator_type_(qtype == QT_NSEC3 ? ITT_NSEC3 : ITT_NAME),
         accessor_(accessor),
         statement_(NULL),
         name_(name)
     {
+        // Choose the statement text depending on the query type
+        const char* statement(NULL);
+        switch (qtype) {
+            case QT_ANY:
+                statement = text_statements[ANY];
+                break;
+            case QT_SUBDOMAINS:
+                statement = text_statements[ANY_SUB];
+                break;
+            case QT_NSEC3:
+                statement = text_statements[NSEC3];
+                break;
+            default:
+                // Can Not Happen - there isn't any other type of query
+                // and all the calls to the constructor are from this
+                // file. Therefore no way to test it throws :-(.
+                isc_throw(Unexpected,
+                          "Invalid qtype passed - unreachable code branch "
+                          "reached");
+        }
+
         // We create the statement now and then just keep getting data from it
-        statement_ = prepare(accessor->dbparameters_->db_,
-                             subdomains ? text_statements[ANY_SUB] :
-                             text_statements[ANY]);
+        statement_ = prepare(accessor->dbparameters_->db_, statement);
         bindZoneId(id);
         bindName(name_);
     }
@@ -515,7 +696,11 @@ public:
             // For both types, we copy the first four columns
             copyColumn(data, TYPE_COLUMN);
             copyColumn(data, TTL_COLUMN);
-            copyColumn(data, SIGTYPE_COLUMN);
+            // The NSEC3 lookup does not provide the SIGTYPE, it is not
+            // necessary and not contained in the table.
+            if (iterator_type_ != ITT_NSEC3) {
+                copyColumn(data, SIGTYPE_COLUMN);
+            }
             copyColumn(data, RDATA_COLUMN);
             // Only copy Name if we are iterating over every record
             if (iterator_type_ == ITT_ALL) {
@@ -541,7 +726,8 @@ private:
     // See description of getNext() and the constructors
     enum IteratorType {
         ITT_ALL,
-        ITT_NAME
+        ITT_NAME,
+        ITT_NSEC3
     };
 
     void copyColumn(std::string (&data)[COLUMN_COUNT], int column) {
@@ -588,7 +774,15 @@ SQLite3Accessor::getRecords(const std::string& name, int id,
                             bool subdomains) const
 {
     return (IteratorContextPtr(new Context(shared_from_this(), id, name,
-                                           subdomains)));
+                                           subdomains ?
+                                           Context::QT_SUBDOMAINS :
+                                           Context::QT_ANY)));
+}
+
+DatabaseAccessor::IteratorContextPtr
+SQLite3Accessor::getNSEC3Records(const std::string& hash, int id) const {
+    return (IteratorContextPtr(new Context(shared_from_this(), id, hash,
+                                           Context::QT_NSEC3)));
 }
 
 DatabaseAccessor::IteratorContextPtr
@@ -652,7 +846,7 @@ public:
     ///
     /// \return bool true if data is returned, false if not.
     ///
-    /// \exceptions any Varied
+    /// \exception any Varied
     bool getNext(std::string (&data)[COLUMN_COUNT]) {
 
         if (last_status_ != SQLITE_DONE) {
@@ -805,7 +999,7 @@ private:
             // No data returned but the SQL query succeeded.  Only possibility
             // is that there is no entry in the differences table for the given
             // zone and version.
-            isc_throw(NoSuchSerial, "No entry in differences table for " <<
+            isc_throw(NoSuchSerial, "No entry in differences table for" <<
                       " zone ID " << zone_id << ", serial number " << serial);
         }
 
@@ -867,19 +1061,22 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
                        "start an SQLite3 update transaction").exec();
 
     if (replace) {
+        // First, clear all current data from tables.
+        typedef pair<StatementID, const char* const> StatementSpec;
+        const StatementSpec delzone_stmts[] =
+            { StatementSpec(DEL_ZONE_RECORDS, "delete zone records"),
+              StatementSpec(DEL_ZONE_NSEC3_RECORDS,
+                            "delete zone NSEC3 records") };
         try {
-            StatementProcessor delzone_exec(*dbparameters_, DEL_ZONE_RECORDS,
-                                            "delete zone records");
-
-            sqlite3_stmt* stmt = dbparameters_->getStatement(DEL_ZONE_RECORDS);
-            sqlite3_clear_bindings(stmt);
-            if (sqlite3_bind_int(stmt, 1, zone_info.second) != SQLITE_OK) {
-                isc_throw(DataSourceError,
-                          "failed to bind SQLite3 parameter: " <<
-                          sqlite3_errmsg(dbparameters_->db_));
+            for (size_t i = 0;
+                 i < sizeof(delzone_stmts) / sizeof(delzone_stmts[0]);
+                 ++i) {
+                StatementProcessor delzone_proc(*dbparameters_,
+                                                delzone_stmts[i].first,
+                                                delzone_stmts[i].second);
+                delzone_proc.bindInt(1, zone_info.second);
+                delzone_proc.exec();
             }
-
-            delzone_exec.exec();
         } catch (const DataSourceError&) {
             // Once we start a transaction, if something unexpected happens
             // we need to rollback the transaction so that a subsequent update
@@ -893,6 +1090,7 @@ SQLite3Accessor::startUpdateZone(const string& zone_name, const bool replace) {
     dbparameters_->in_transaction = true;
     dbparameters_->updating_zone = true;
     dbparameters_->updated_zone_id = zone_info.second;
+    dbparameters_->updated_zone_origin_ = zone_name;
 
     return (zone_info);
 }
@@ -919,7 +1117,9 @@ SQLite3Accessor::commit() {
     StatementProcessor(*dbparameters_, COMMIT,
                        "commit an SQLite3 transaction").exec();
     dbparameters_->in_transaction = false;
+    dbparameters_->updating_zone = false;
     dbparameters_->updated_zone_id = -1;
+    dbparameters_->updated_zone_origin_.clear();
 }
 
 void
@@ -932,7 +1132,9 @@ SQLite3Accessor::rollback() {
     StatementProcessor(*dbparameters_, ROLLBACK,
                        "rollback an SQLite3 transaction").exec();
     dbparameters_->in_transaction = false;
+    dbparameters_->updating_zone = false;
     dbparameters_->updated_zone_id = -1;
+    dbparameters_->updated_zone_origin_.clear();
 }
 
 namespace {
@@ -942,29 +1144,19 @@ void
 doUpdate(SQLite3Parameters& dbparams, StatementID stmt_id,
          COLUMNS_TYPE update_params, const char* exec_desc)
 {
-    sqlite3_stmt* const stmt = dbparams.getStatement(stmt_id);
-    StatementProcessor executer(dbparams, stmt_id, exec_desc);
+    StatementProcessor proc(dbparams, stmt_id, exec_desc);
 
     int param_id = 0;
-    if (sqlite3_bind_int(stmt, ++param_id, dbparams.updated_zone_id)
-        != SQLITE_OK) {
-        isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
-                  sqlite3_errmsg(dbparams.db_));
-    }
+    proc.bindInt(++param_id, dbparams.updated_zone_id);
     const size_t column_count =
         sizeof(update_params) / sizeof(update_params[0]);
     for (int i = 0; i < column_count; ++i) {
         // The old sqlite3 data source API assumes NULL for an empty column.
         // We need to provide compatibility at least for now.
-        if (sqlite3_bind_text(stmt, ++param_id,
-                              update_params[i].empty() ? NULL :
-                              update_params[i].c_str(),
-                              -1, SQLITE_TRANSIENT) != SQLITE_OK) {
-            isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
-                      sqlite3_errmsg(dbparams.db_));
-        }
+        proc.bindText(++param_id, update_params[i].empty() ? NULL :
+                      update_params[i].c_str(), SQLITE_TRANSIENT);
     }
-    executer.exec();
+    proc.exec();
 }
 }
 
@@ -974,21 +1166,58 @@ SQLite3Accessor::addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
         isc_throw(DataSourceError, "adding record to SQLite3 "
                   "data source without transaction");
     }
-    doUpdate<const string (&)[DatabaseAccessor::ADD_COLUMN_COUNT]>(
+    doUpdate<const string (&)[ADD_COLUMN_COUNT]>(
         *dbparameters_, ADD_RECORD, columns, "add record to zone");
 }
 
 void
+SQLite3Accessor::addNSEC3RecordToZone(
+    const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
+{
+    if (!dbparameters_->updating_zone) {
+        isc_throw(DataSourceError, "adding NSEC3-related record to SQLite3 "
+                  "data source without transaction");
+    }
+
+    // XXX: the current implementation of SQLite3 schema requires the 'owner'
+    // column, and the current implementation of getAllRecords() relies on it,
+    // while the addNSEC3RecordToZone interface doesn't provide it explicitly.
+    // We should revisit it at the design level, but for now we internally
+    // convert the given parameter to satisfy the internal requirements.
+    const string sqlite3_columns[ADD_NSEC3_COLUMN_COUNT + 1] =
+        { columns[ADD_NSEC3_HASH],
+          columns[ADD_NSEC3_HASH] + "." + dbparameters_->updated_zone_origin_,
+          columns[ADD_NSEC3_TTL],
+          columns[ADD_NSEC3_TYPE], columns[ADD_NSEC3_RDATA] };
+    doUpdate<const string (&)[ADD_NSEC3_COLUMN_COUNT + 1]>(
+        *dbparameters_, ADD_NSEC3_RECORD, sqlite3_columns,
+        "add NSEC3 record to zone");
+}
+
+void
 SQLite3Accessor::deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
     if (!dbparameters_->updating_zone) {
         isc_throw(DataSourceError, "deleting record in SQLite3 "
                   "data source without transaction");
     }
-    doUpdate<const string (&)[DatabaseAccessor::DEL_PARAM_COUNT]>(
+    doUpdate<const string (&)[DEL_PARAM_COUNT]>(
         *dbparameters_, DEL_RECORD, params, "delete record from zone");
 }
 
 void
+SQLite3Accessor::deleteNSEC3RecordInZone(
+    const string (&params)[DEL_PARAM_COUNT])
+{
+    if (!dbparameters_->updating_zone) {
+        isc_throw(DataSourceError, "deleting NSEC3-related record in SQLite3 "
+                  "data source without transaction");
+    }
+    doUpdate<const string (&)[DEL_PARAM_COUNT]>(
+        *dbparameters_, DEL_NSEC3_RECORD, params,
+        "delete NSEC3 record from zone");
+}
+
+void
 SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
                                DiffOperation operation,
                                const std::string (&params)[DIFF_PARAM_COUNT])
@@ -1003,53 +1232,16 @@ SQLite3Accessor::addRecordDiff(int zone_id, uint32_t serial,
                   << dbparameters_->updated_zone_id);
     }
 
-    sqlite3_stmt* const stmt = dbparameters_->getStatement(ADD_RECORD_DIFF);
-    StatementProcessor executer(*dbparameters_, ADD_RECORD_DIFF,
-                                "add record diff");
+    StatementProcessor proc(*dbparameters_, ADD_RECORD_DIFF,
+                            "add record diff");
     int param_id = 0;
-    if (sqlite3_bind_int(stmt, ++param_id, zone_id)
-        != SQLITE_OK) {
-        isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
-                  sqlite3_errmsg(dbparameters_->db_));
-    }
-    if (sqlite3_bind_int64(stmt, ++param_id, serial)
-        != SQLITE_OK) {
-        isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
-                  sqlite3_errmsg(dbparameters_->db_));
-    }
-    if (sqlite3_bind_int(stmt, ++param_id, operation)
-        != SQLITE_OK) {
-        isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
-                  sqlite3_errmsg(dbparameters_->db_));
-    }
+    proc.bindInt(++param_id, zone_id);
+    proc.bindInt64(++param_id, serial);
+    proc.bindInt(++param_id, operation);
     for (int i = 0; i < DIFF_PARAM_COUNT; ++i) {
-        if (sqlite3_bind_text(stmt, ++param_id, params[i].c_str(),
-                              -1, SQLITE_TRANSIENT) != SQLITE_OK) {
-            isc_throw(DataSourceError, "failed to bind SQLite3 parameter: " <<
-                      sqlite3_errmsg(dbparameters_->db_));
-        }
+        proc.bindText(++param_id, params[i].c_str(), SQLITE_TRANSIENT);
     }
-    executer.exec();
-}
-
-vector<vector<string> >
-SQLite3Accessor::getRecordDiff(int zone_id) {
-    sqlite3_stmt* const stmt = dbparameters_->getStatement(GET_RECORD_DIFF);
-    sqlite3_bind_int(stmt, 1, zone_id);
-
-    vector<vector<string> > result;
-    while (sqlite3_step(stmt) == SQLITE_ROW) {
-        vector<string> row_result;
-        for (int i = 0; i < 6; ++i) {
-            row_result.push_back(convertToPlainChar(sqlite3_column_text(stmt,
-                                                                        i),
-                                                    dbparameters_->db_));
-        }
-        result.push_back(row_result);
-    }
-    sqlite3_reset(stmt);
-
-    return (result);
+    proc.exec();
 }
 
 std::string
@@ -1096,74 +1288,74 @@ SQLite3Accessor::findPreviousName(int zone_id, const std::string& rname)
     return (result);
 }
 
-namespace {
-void
-addError(ElementPtr errors, const std::string& error) {
-    if (errors != ElementPtr() && errors->getType() == Element::list) {
-        errors->add(Element::create(error));
+std::string
+SQLite3Accessor::findPreviousNSEC3Hash(int zone_id, const std::string& hash)
+    const
+{
+    sqlite3_stmt* const stmt = dbparameters_->getStatement(NSEC3_PREVIOUS);
+    sqlite3_reset(stmt);
+    sqlite3_clear_bindings(stmt);
+
+    if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
+        isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+                  " to SQL statement (find previous NSEC3): " <<
+                  sqlite3_errmsg(dbparameters_->db_));
+    }
+    if (sqlite3_bind_text(stmt, 2, hash.c_str(), -1, SQLITE_STATIC) !=
+        SQLITE_OK) {
+        isc_throw(SQLite3Error, "Could not bind hash " << hash <<
+                  " to SQL statement (find previous NSEC3): " <<
+                  sqlite3_errmsg(dbparameters_->db_));
     }
-}
 
-bool
-checkConfig(ConstElementPtr config, ElementPtr errors) {
-    /* Specific configuration is under discussion, right now this accepts
-     * the 'old' configuration, see header file
-     */
-    bool result = true;
+    std::string result;
+    const int rc = sqlite3_step(stmt);
+    if (rc == SQLITE_ROW) {
+        // We found it
+        result = convertToPlainChar(sqlite3_column_text(stmt, 0),
+                                    dbparameters_->db_);
+    }
+    sqlite3_reset(stmt);
 
-    if (!config || config->getType() != Element::map) {
-        addError(errors, "Base config for SQlite3 backend must be a map");
-        result = false;
-    } else {
-        if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
-            addError(errors,
-                     "Config for SQlite3 backend does not contain a '"
-                     CONFIG_ITEM_DATABASE_FILE
-                     "' value");
-            result = false;
-        } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
-                   config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
-                   Element::string) {
-            addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
-                     " in SQLite3 backend is not a string");
-            result = false;
-        } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
-                   "") {
-            addError(errors, "value of " CONFIG_ITEM_DATABASE_FILE
-                     " in SQLite3 backend is empty");
-            result = false;
-        }
+    if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+        // Some kind of error
+        isc_throw(SQLite3Error, "Could not get data for previous hash");
     }
 
-    return (result);
-}
+    if (rc == SQLITE_DONE) {
+        // No NSEC3 records before this hash. This means we should wrap
+        // around and take the last one.
+        sqlite3_stmt* const stmt = dbparameters_->getStatement(NSEC3_LAST);
+        sqlite3_reset(stmt);
+        sqlite3_clear_bindings(stmt);
+
+        if (sqlite3_bind_int(stmt, 1, zone_id) != SQLITE_OK) {
+            isc_throw(SQLite3Error, "Could not bind zone ID " << zone_id <<
+                      " to SQL statement (find last NSEC3): " <<
+                      sqlite3_errmsg(dbparameters_->db_));
+        }
 
-} // end anonymous namespace
-
-DataSourceClient *
-createInstance(isc::data::ConstElementPtr config, std::string& error) {
-    ElementPtr errors(Element::createList());
-    if (!checkConfig(config, errors)) {
-        error = "Configuration error: " + errors->str();
-        return (NULL);
-    }
-    std::string dbfile = config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
-    try {
-        boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
-            new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
-        return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
-    } catch (const std::exception& exc) {
-        error = std::string("Error creating sqlite3 datasource: ") + exc.what();
-        return (NULL);
-    } catch (...) {
-        error = std::string("Error creating sqlite3 datasource, "
-                            "unknown exception");
-        return (NULL);
+        const int rc = sqlite3_step(stmt);
+        if (rc == SQLITE_ROW) {
+            // We found it
+            result = convertToPlainChar(sqlite3_column_text(stmt, 0),
+                                        dbparameters_->db_);
+        }
+        sqlite3_reset(stmt);
+
+        if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
+            // Some kind of error
+            isc_throw(SQLite3Error, "Could not get data for last hash");
+        }
+
+        if (rc == SQLITE_DONE) {
+            // No NSEC3 at all in the zone. Well, bad luck, but you should not
+            // have asked in the first place.
+            isc_throw(DataSourceError, "No NSEC3 in this zone");
+        }
     }
-}
 
-void destroyInstance(DataSourceClient* instance) {
-    delete instance;
+    return (result);
 }
 
 } // end of namespace datasrc
diff --git a/src/lib/datasrc/sqlite3_accessor.h b/src/lib/datasrc/sqlite3_accessor.h
index 08be824..3e44d5b 100644
--- a/src/lib/datasrc/sqlite3_accessor.h
+++ b/src/lib/datasrc/sqlite3_accessor.h
@@ -47,6 +47,12 @@ public:
         DataSourceError(file, line, what) {}
 };
 
+class IncompatibleDbVersion : public Exception {
+public:
+    IncompatibleDbVersion(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /**
  * \brief Too Much Data
  *
@@ -141,6 +147,14 @@ public:
                                           int id,
                                           bool subdomains = false) const;
 
+    /// \brief Look up NSEC3 records for the given hash
+    ///
+    /// This implements the getNSEC3Records of DatabaseAccessor.
+    ///
+    /// \todo Actually implement, currently throws NotImplemented.
+    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
+                                               int id) const;
+
     /** \brief Look up all resource records for a zone
      *
      * This implements the getRecords() method from DatabaseAccessor
@@ -200,9 +214,15 @@ public:
     virtual void addRecordToZone(
         const std::string (&columns)[ADD_COLUMN_COUNT]);
 
+    virtual void addNSEC3RecordToZone(
+        const std::string (&columns)[ADD_NSEC3_COLUMN_COUNT]);
+
     virtual void deleteRecordInZone(
         const std::string (&params)[DEL_PARAM_COUNT]);
 
+    virtual void deleteNSEC3RecordInZone(
+        const std::string (&params)[DEL_PARAM_COUNT]);
+
     /// This derived version of the method prepares an SQLite3 statement
     /// for adding the diff first time it's called, and if it fails throws
     // an \c SQLite3Error exception.
@@ -210,16 +230,6 @@ public:
         int zone_id, uint32_t serial, DiffOperation operation,
         const std::string (&params)[DIFF_PARAM_COUNT]);
 
-    // A short term method for tests until we implement more complete
-    // API to retrieve diffs (#1330).  It returns all records of the diffs
-    // table whose zone_id column is identical to the given value.
-    // Since this is a short term workaround, it ignores some corner cases
-    // (such as an SQLite3 execution failure) and is not very efficient,
-    // in favor of brevity.  Once #1330 is completed, this method must be
-    // removed, and the tests using this method must be rewritten using the
-    // official API.
-    std::vector<std::vector<std::string> > getRecordDiff(int zone_id);
-
     /// The SQLite3 implementation of this method returns a string starting
     /// with a fixed prefix of "sqlite3_" followed by the DB file name
     /// removing any path name.  For example, for the DB file
@@ -231,6 +241,11 @@ public:
     virtual std::string findPreviousName(int zone_id, const std::string& rname)
         const;
 
+    /// \brief Conrete implemantion of the pure virtual method of
+    /// DatabaseAccessor
+    virtual std::string findPreviousNSEC3Hash(int zone_id,
+                                              const std::string& hash) const;
+
 private:
     /// \brief Private database data
     boost::scoped_ptr<SQLite3Parameters> dbparameters_;
diff --git a/src/lib/datasrc/sqlite3_accessor_link.cc b/src/lib/datasrc/sqlite3_accessor_link.cc
new file mode 100644
index 0000000..c064e0f
--- /dev/null
+++ b/src/lib/datasrc/sqlite3_accessor_link.cc
@@ -0,0 +1,107 @@
+// 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 <cc/data.h>
+
+#include <dns/rrclass.h>
+
+#include <datasrc/sqlite3_accessor.h>
+#include <datasrc/database.h>
+
+#include <string>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::data;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+
+const char* const CONFIG_ITEM_DATABASE_FILE = "database_file";
+
+void
+addError(ElementPtr errors, const std::string& error) {
+    if (errors != ElementPtr() && errors->getType() == Element::list) {
+        errors->add(Element::create(error));
+    }
+}
+
+bool
+checkConfig(ConstElementPtr config, ElementPtr errors) {
+    /* Specific configuration is under discussion, right now this accepts
+     * the 'old' configuration, see header file
+     */
+    bool result = true;
+
+    if (!config || config->getType() != Element::map) {
+        addError(errors, "Base config for SQlite3 backend must be a map");
+        result = false;
+    } else {
+        if (!config->contains(CONFIG_ITEM_DATABASE_FILE)) {
+            addError(errors,
+                     "Config for SQlite3 backend does not contain a '" +
+                     string(CONFIG_ITEM_DATABASE_FILE) +
+                     "' value");
+            result = false;
+        } else if (!config->get(CONFIG_ITEM_DATABASE_FILE) ||
+                   config->get(CONFIG_ITEM_DATABASE_FILE)->getType() !=
+                   Element::string) {
+            addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+                     " in SQLite3 backend is not a string");
+            result = false;
+        } else if (config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue() ==
+                   "") {
+            addError(errors, "value of " + string(CONFIG_ITEM_DATABASE_FILE) +
+                     " in SQLite3 backend is empty");
+            result = false;
+        }
+    }
+
+    return (result);
+}
+
+} // end unnamed namespace
+
+DataSourceClient *
+createInstance(isc::data::ConstElementPtr config, std::string& error) {
+    ElementPtr errors(Element::createList());
+    if (!checkConfig(config, errors)) {
+        error = "Configuration error: " + errors->str();
+        return (NULL);
+    }
+    const std::string dbfile =
+        config->get(CONFIG_ITEM_DATABASE_FILE)->stringValue();
+    try {
+        boost::shared_ptr<DatabaseAccessor> sqlite3_accessor(
+            new SQLite3Accessor(dbfile, "IN")); // XXX: avoid hardcode RR class
+        return (new DatabaseClient(isc::dns::RRClass::IN(), sqlite3_accessor));
+    } catch (const std::exception& exc) {
+        error = std::string("Error creating sqlite3 datasource: ") +
+            exc.what();
+        return (NULL);
+    } catch (...) {
+        error = std::string("Error creating sqlite3 datasource, "
+                            "unknown exception");
+        return (NULL);
+    }
+}
+
+void destroyInstance(DataSourceClient* instance) {
+    delete instance;
+}
+
+} // end of namespace datasrc
+} // end of namespace isc
diff --git a/src/lib/datasrc/sqlite3_datasrc.cc b/src/lib/datasrc/sqlite3_datasrc.cc
index 03b057c..b450cd5 100644
--- a/src/lib/datasrc/sqlite3_datasrc.cc
+++ b/src/lib/datasrc/sqlite3_datasrc.cc
@@ -14,19 +14,33 @@
 
 #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>
 
-#define SQLITE_SCHEMA_VERSION 1
+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;
@@ -36,13 +50,14 @@ namespace isc {
 namespace datasrc {
 
 struct Sqlite3Parameters {
-    Sqlite3Parameters() :  db_(NULL), version_(-1),
+    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 version_;
+    int major_version_;
+    int minor_version_;
     sqlite3_stmt* q_zone_;
     sqlite3_stmt* q_record_;
     sqlite3_stmt* q_addrs_;
@@ -56,30 +71,41 @@ struct Sqlite3Parameters {
 
 namespace {
 const char* const SCHEMA_LIST[] = {
-    "CREATE TABLE schema_version (version INTEGER NOT NULL)",
-    "INSERT INTO schema_version VALUES (1)",
+    "CREATE TABLE schema_version (version INTEGER NOT NULL, "
+        "minor INTEGER NOT NULL DEFAULT 0)",
+    "INSERT INTO schema_version VALUES (2, 0)",
     "CREATE TABLE zones (id INTEGER PRIMARY KEY, "
-    "name STRING NOT NULL COLLATE NOCASE, "
-    "rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN', "
+    "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 STRING NOT NULL COLLATE NOCASE, "
-    "rname STRING NOT NULL COLLATE NOCASE, ttl INTEGER NOT NULL, "
-    "rdtype STRING NOT NULL COLLATE NOCASE, sigtype STRING COLLATE NOCASE, "
-    "rdata STRING NOT NULL)",
+    "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 TABLE nsec3 (id INTEGER PRIMARY KEY, zone_id INTEGER NOT NULL, "
-    "hash STRING NOT NULL COLLATE NOCASE, "
-    "owner STRING NOT NULL COLLATE NOCASE, "
-    "ttl INTEGER NOT NULL, rdtype STRING NOT NULL COLLATE NOCASE, "
-    "rdata STRING 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 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";
 
@@ -101,12 +127,16 @@ const char* const q_referral_str = "SELECT rdtype, ttl, sigtype, rdata FROM "
 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 || '%');";
+    "WHERE zone_id=?1 AND rname LIKE ?2;";
 
 const char* const q_previous_str = "SELECT name FROM records "
-    "WHERE zone_id=?1 AND rdtype = 'NSEC' AND "
-    "rname < $2 ORDER BY rname DESC LIMIT 1";
+    "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";
@@ -306,8 +336,9 @@ Sqlite3DataSrc::findRecords(const Name& name, const RRType& rdtype,
                   " to SQL statement (qcount)");
     }
 
-    const string revname_text = name.reverse().toText();
-    rc = sqlite3_bind_text(dbparameters->q_count_, 2, revname_text.c_str(),
+    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() <<
@@ -667,15 +698,15 @@ void do_sleep() {
     nanosleep(&req, NULL);
 }
 
-// returns the schema version if the schema version table exists
+// returns the schema version element if the schema version table exists
 // returns -1 if it does not
-int check_schema_version(sqlite3* db) {
+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, q_version_str, -1, &prepared, NULL);
+        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
@@ -697,27 +728,73 @@ int check_schema_version(sqlite3* db) {
     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
-int create_database(sqlite3* db) {
+pair<int, int> create_database(sqlite3* db) {
+    logger.info(DATASRC_SQLITE_SETUP);
+
     // try to get an exclusive lock. Once that is obtained, do the version
     // check *again*, just in case this process was racing another
-    //
-    // try for 5 secs (50*0.1)
-    int rc;
-    logger.info(DATASRC_SQLITE_SETUP);
-    for (size_t i = 0; i < 50; ++i) {
-        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();
-    }
-    int schema_version = check_schema_version(db);
-    if (schema_version == -1) {
+    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) {
@@ -725,23 +802,40 @@ int create_database(sqlite3* db) {
                         "Failed to set up schema " << SCHEMA_LIST[i]);
             }
         }
-        sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, NULL);
-        return (SQLITE_SCHEMA_VERSION);
-    } else {
-        return (schema_version);
+        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_;
 
-    int schema_version = check_schema_version(db);
-    if (schema_version != SQLITE_SCHEMA_VERSION) {
+    // 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);
-    }
-    initializer->params_.version_ = schema_version;
-
+    } 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);
diff --git a/src/lib/datasrc/sqlite3_datasrc.h b/src/lib/datasrc/sqlite3_datasrc.h
index d4abef7..8ee042f 100644
--- a/src/lib/datasrc/sqlite3_datasrc.h
+++ b/src/lib/datasrc/sqlite3_datasrc.h
@@ -41,6 +41,12 @@ public:
         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.
diff --git a/src/lib/datasrc/static_datasrc.cc b/src/lib/datasrc/static_datasrc.cc
index 03f56be..77d7a1d 100644
--- a/src/lib/datasrc/static_datasrc.cc
+++ b/src/lib/datasrc/static_datasrc.cc
@@ -73,6 +73,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
     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")); 
@@ -82,6 +83,7 @@ StaticDataSrcImpl::StaticDataSrcImpl() :
     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"));
diff --git a/src/lib/datasrc/tests/.gitignore b/src/lib/datasrc/tests/.gitignore
new file mode 100644
index 0000000..bae5d90
--- /dev/null
+++ b/src/lib/datasrc/tests/.gitignore
@@ -0,0 +1,4 @@
+/run_unittests
+/run_unittests_factory
+/run_unittests_memory
+/run_unittests_sqlite3
diff --git a/src/lib/datasrc/tests/Makefile.am b/src/lib/datasrc/tests/Makefile.am
index bfb0037..90fb3e4 100644
--- a/src/lib/datasrc/tests/Makefile.am
+++ b/src/lib/datasrc/tests/Makefile.am
@@ -20,17 +20,13 @@ CLEANFILES = *.gcno *.gcda
 TESTS =
 noinst_PROGRAMS =
 if HAVE_GTEST
-TESTS += run_unittests run_unittests_sqlite3 run_unittests_memory
+TESTS += run_unittests
 
-#
-# For each specific datasource, there is a separate binary that includes
-# the code itself (we can't unittest through the public API). These need
-# to be separate because the included code, by design, contains conflicting
-# symbols.
-# We also have a 'general' run_unittests with non-datasource-specific tests
-#
+# We have two sets of tests: the general tests and factory tests (see below
+# for the latter).  They are separate binary files sharing some program files
+# and libraries.
 
-# First define the parts shared by all
+# First define the parts shared by both
 common_sources = run_unittests.cc
 common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
 common_sources += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
@@ -46,46 +42,35 @@ common_ldadd += $(top_builddir)/src/lib/cc/libcc.la
 common_ldadd += $(top_builddir)/src/lib/testutils/libtestutils.la
 common_ldadd += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 
-
 # The general tests
 run_unittests_SOURCES = $(common_sources)
 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 += test_client.h test_client.cc
 run_unittests_SOURCES += test_datasrc.h test_datasrc.cc
 run_unittests_SOURCES += rbtree_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += client_unittest.cc
+run_unittests_SOURCES += database_unittest.cc
+run_unittests_SOURCES += sqlite3_unittest.cc
+run_unittests_SOURCES += sqlite3_accessor_unittest.cc
+run_unittests_SOURCES += memory_datasrc_unittest.cc
+run_unittests_SOURCES += rbnode_rrset_unittest.cc
+run_unittests_SOURCES += zone_finder_context_unittest.cc
+run_unittests_SOURCES += faked_nsec3.h faked_nsec3.cc
+
+# We need the actual module implementation in the tests (they are not part
+# of libdatasrc)
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
+run_unittests_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
 run_unittests_LDADD = $(common_ldadd)
 
-
-# SQlite3 datasource tests
-run_unittests_sqlite3_SOURCES = $(common_sources)
-run_unittests_sqlite3_SOURCES += database_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_unittest.cc
-run_unittests_sqlite3_SOURCES += sqlite3_accessor_unittest.cc
-run_unittests_sqlite3_SOURCES += $(top_srcdir)/src/lib/datasrc/sqlite3_accessor.cc
-
-run_unittests_sqlite3_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_sqlite3_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
-run_unittests_sqlite3_LDADD = $(common_ldadd)
-
-# In-memory datasource tests
-run_unittests_memory_SOURCES = $(common_sources)
-run_unittests_memory_SOURCES += memory_datasrc_unittest.cc
-run_unittests_memory_SOURCES += $(top_srcdir)/src/lib/datasrc/memory_datasrc.cc
-
-run_unittests_memory_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-run_unittests_memory_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
-run_unittests_memory_LDADD = $(common_ldadd)
-
 noinst_PROGRAMS+= $(TESTS)
 
 # For the factory unit tests, we need to specify that we want
@@ -109,19 +94,23 @@ endif
 endif
 
 EXTRA_DIST =  testdata/brokendb.sqlite3
+EXTRA_DIST += testdata/contexttest.zone
+EXTRA_DIST += testdata/diffs.sqlite3
+EXTRA_DIST += testdata/example2.com
+EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/example.com.signed
 EXTRA_DIST += testdata/example.org
 EXTRA_DIST += testdata/example.org.nsec3-signed
 EXTRA_DIST += testdata/example.org.nsec3-signed-noparam
 EXTRA_DIST += testdata/example.org.sqlite3
-EXTRA_DIST += testdata/example2.com
-EXTRA_DIST += testdata/example2.com.sqlite3
 EXTRA_DIST += testdata/mkbrokendb.c
 EXTRA_DIST += testdata/root.zone
+EXTRA_DIST += testdata/rrset_toWire1
+EXTRA_DIST += testdata/rrset_toWire2
 EXTRA_DIST += testdata/sql1.example.com.signed
 EXTRA_DIST += testdata/sql2.example.com.signed
 EXTRA_DIST += testdata/test-root.sqlite3
 EXTRA_DIST += testdata/test.sqlite3
-EXTRA_DIST += testdata/test.sqlite3.nodiffs
-EXTRA_DIST += testdata/rwtest.sqlite3
-EXTRA_DIST += testdata/diffs.sqlite3
+EXTRA_DIST += testdata/new_minor_schema.sqlite3
+EXTRA_DIST += testdata/newschema.sqlite3
+EXTRA_DIST += testdata/oldschema.sqlite3
diff --git a/src/lib/datasrc/tests/database_unittest.cc b/src/lib/datasrc/tests/database_unittest.cc
index 408913a..110b3f0 100644
--- a/src/lib/datasrc/tests/database_unittest.cc
+++ b/src/lib/datasrc/tests/database_unittest.cc
@@ -12,18 +12,15 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <stdlib.h>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/lexical_cast.hpp>
-
-#include <gtest/gtest.h>
+#include "faked_nsec3.h"
 
 #include <exceptions/exceptions.h>
 
+#include <dns/masterload.h>
 #include <dns/name.h>
 #include <dns/rrttl.h>
 #include <dns/rrset.h>
+#include <dns/nsec3hash.h>
 #include <exceptions/exceptions.h>
 
 #include <datasrc/database.h>
@@ -34,6 +31,13 @@
 
 #include <testutils/dnsmessage_test.h>
 
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <cstdlib>
 #include <map>
 #include <vector>
 
@@ -44,6 +48,8 @@ using namespace std;
 using boost::dynamic_pointer_cast;
 using boost::lexical_cast;
 using namespace isc::dns;
+using namespace isc::testutils;
+using namespace isc::datasrc::test;
 
 namespace {
 
@@ -167,7 +173,10 @@ const char* const TEST_RECORDS[][5] = {
      "1234 3600 1800 2419200 7200" },
     {"example.org.", "NS", "3600", "", "ns.example.com."},
     {"example.org.", "A", "3600", "", "192.0.2.1"},
-    {"example.org.", "NSEC", "3600", "", "acnamesig1.example.org. NS A NSEC RRSIG"},
+    // Note that the RDATA text is "normalized", i.e., identical to what
+    // Rdata::toText() would produce.  some tests rely on that behavior.
+    {"example.org.", "NSEC", "3600", "",
+     "acnamesig1.example.org. A NS RRSIG NSEC"},
     {"example.org.", "RRSIG", "3600", "", "SOA 5 3 3600 20000101000000 "
               "20000201000000 12345 example.org. FAKEFAKEFAKE"},
     {"example.org.", "RRSIG", "3600", "", "NSEC 5 3 3600 20000101000000 "
@@ -205,6 +214,19 @@ const char* const TEST_RECORDS[][5] = {
     {NULL, NULL, NULL, NULL, NULL},
 };
 
+// FIXME: Taken from a different test. Fill with proper data when creating a test.
+const char* TEST_NSEC3_RECORDS[][5] = {
+    {apex_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {apex_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {ns1_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {ns1_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {w_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {w_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {zzz_hash, "NSEC3", "300", "", "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG"},
+    {zzz_hash, "RRSIG", "300", "", "NSEC3 5 4 7200 20100410172647 20100311172647 63192 example.org. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o 8gHSY5vYTtothcZQa4BMKhmGQEk="},
+    {NULL, NULL, NULL, NULL, NULL}
+};
+
 /*
  * An accessor with minimum implementation, keeping the original
  * "NotImplemented" methods.
@@ -241,7 +263,10 @@ public:
     virtual void commit() {}
     virtual void rollback() {}
     virtual void addRecordToZone(const string (&)[ADD_COLUMN_COUNT]) {}
+    virtual void addNSEC3RecordToZone(const string (&)[ADD_NSEC3_COLUMN_COUNT])
+    {}
     virtual void deleteRecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
+    virtual void deleteNSEC3RecordInZone(const string (&)[DEL_PARAM_COUNT]) {}
     virtual void addRecordDiff(int, uint32_t, DiffOperation,
                                const std::string (&)[DIFF_PARAM_COUNT]) {}
 
@@ -251,11 +276,16 @@ public:
 
     virtual IteratorContextPtr getRecords(const std::string&, int, bool)
         const
-        {
+    {
         isc_throw(isc::NotImplemented,
                   "This database datasource can't be iterated");
     }
 
+    virtual IteratorContextPtr getNSEC3Records(const std::string&, int) const {
+        isc_throw(isc::NotImplemented, "This test database datasource won't "
+                  "give you any NSEC3. Ever. Ask someone else.");
+    }
+
     virtual IteratorContextPtr getAllRecords(int) const {
         isc_throw(isc::NotImplemented,
                   "This database datasource can't be iterated");
@@ -270,6 +300,11 @@ public:
         isc_throw(isc::NotImplemented,
                   "This data source doesn't support DNSSEC");
     }
+
+    virtual std::string findPreviousNSEC3Hash(int, const std::string&) const {
+        isc_throw(isc::NotImplemented,
+                  "This test database knows nothing about NSEC3 nor order");
+    }
 private:
     const std::string database_name_;
 
@@ -344,6 +379,8 @@ public:
     MockAccessor() : rollbacked_(false), did_transaction_(false) {
         readonly_records_ = &readonly_records_master_;
         update_records_ = &update_records_master_;
+        nsec3_namespace_ = &nsec3_namespace_master_;
+        update_nsec3_namespace_ = &update_nsec3_namespace_master_;
         empty_records_ = &empty_records_master_;
         journal_entries_ = &journal_entries_master_;
         fillData();
@@ -353,6 +390,9 @@ public:
         boost::shared_ptr<MockAccessor> cloned_accessor(new MockAccessor());
         cloned_accessor->readonly_records_ = &readonly_records_master_;
         cloned_accessor->update_records_ = &update_records_master_;
+        cloned_accessor->nsec3_namespace_ = &nsec3_namespace_master_;
+        cloned_accessor->update_nsec3_namespace_ =
+            &update_nsec3_namespace_master_;
         cloned_accessor->empty_records_ = &empty_records_master_;
         cloned_accessor->journal_entries_ = &journal_entries_master_;
         latest_clone_ = cloned_accessor;
@@ -378,6 +418,28 @@ public:
     }
 
 private:
+    class DomainIterator : public IteratorContext {
+    public:
+        DomainIterator(const std::vector<std::vector<std::string> >& domain) :
+            domain_(domain),
+            position_(domain_.begin())
+        {}
+        virtual bool getNext(std::string (&columns)[COLUMN_COUNT]) {
+            if (position_ == domain_.end()) {
+                return (false);
+            } else {
+                for (size_t i(0); i < COLUMN_COUNT; ++ i) {
+                    columns[i] = (*position_)[i];
+                }
+                ++ position_;
+                return (true);
+            }
+        }
+    private:
+        const std::vector<std::vector<std::string> > domain_;
+        std::vector<std::vector<std::string> >::const_iterator position_;
+    };
+
     class MockNameIteratorContext : public IteratorContext {
     public:
         MockNameIteratorContext(const MockAccessor& mock_accessor, int zone_id,
@@ -462,62 +524,46 @@ private:
             }
 
             // Return faked data for tests
-            switch (step ++) {
-                case 0:
-                    data[DatabaseAccessor::NAME_COLUMN] = "example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
-                    data[DatabaseAccessor::TTL_COLUMN] = "3600";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
-                    return (true);
-                case 1:
-                    data[DatabaseAccessor::NAME_COLUMN] = "example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "SOA";
-                    data[DatabaseAccessor::TTL_COLUMN] = "3600";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "ns1.example.org. admin.example.org. "
-                        "1234 3600 1800 2419200 7200";
-                    return (true);
-                case 2:
-                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
-                    data[DatabaseAccessor::TTL_COLUMN] = "300";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
-                    return (true);
-                case 3:
-                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
-                    data[DatabaseAccessor::TTL_COLUMN] = "300";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
-                    return (true);
-                case 4:
-                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "AAAA";
-                    data[DatabaseAccessor::TTL_COLUMN] = "300";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::1";
-                    return (true);
-                case 5:
-                    data[DatabaseAccessor::NAME_COLUMN] = "x.example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "AAAA";
-                    data[DatabaseAccessor::TTL_COLUMN] = "300";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "2001:db8::2";
-                    return (true);
-                case 6:
-                    data[DatabaseAccessor::NAME_COLUMN] = "ttldiff.example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
-                    data[DatabaseAccessor::TTL_COLUMN] = "300";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.1";
-                    return (true);
-                case 7:
-                    data[DatabaseAccessor::NAME_COLUMN] = "ttldiff.example.org";
-                    data[DatabaseAccessor::TYPE_COLUMN] = "A";
-                    data[DatabaseAccessor::TTL_COLUMN] = "600";
-                    data[DatabaseAccessor::RDATA_COLUMN] = "192.0.2.2";
-                    return (true);
-                default:
-                    ADD_FAILURE() <<
-                        "Request past the end of iterator context";
-                case 8:
-                    return (false);
+            // This is the sequence of zone data in the order of appearance
+            // in the returned sequence from this iterator.
+            typedef const char* ColumnText[4];
+            const ColumnText zone_data[] = {
+                // A couple of basic RRs at the zone origin.
+                {"example.org", "A", "3600", "192.0.2.1"},
+                {"example.org", "SOA", "3600", "ns1.example.org. "
+                 "admin.example.org. 1234 3600 1800 2419200 7200"},
+                // RRsets sharing the same owner name with multiple RRs.
+                {"x.example.org", "A", "300", "192.0.2.1"},
+                {"x.example.org", "A", "300", "192.0.2.2"},
+                {"x.example.org", "AAAA", "300", "2001:db8::1"},
+                {"x.example.org", "AAAA", "300", "2001:db8::2"},
+                // RRSIGs.  Covered types are different and these two should
+                // be distinguished.
+                {"x.example.org", "RRSIG", "300",
+                 "A 5 3 3600 20000101000000 20000201000000 12345 "
+                 "example.org. FAKEFAKEFAKE"},
+                {"x.example.org", "RRSIG", "300",
+                 "AAAA 5 3 3600 20000101000000 20000201000000 12345 "
+                 "example.org. FAKEFAKEFAKEFAKE"},
+                // Mixture of different TTLs.  Covering both cases of small
+                // then large and large then small.  In either case the smaller
+                // TTL should win.
+                {"ttldiff.example.org", "A", "300", "192.0.2.1"},
+                {"ttldiff.example.org", "A", "600", "192.0.2.2"},
+                {"ttldiff2.example.org", "AAAA", "600", "2001:db8::1"},
+                {"ttldiff2.example.org", "AAAA", "300", "2001:db8::2"}};
+            const size_t num_rrs = sizeof(zone_data) / sizeof(zone_data[0]);
+            if (step > num_rrs) {
+                ADD_FAILURE() << "Request past the end of iterator context";
+            } else if (step < num_rrs) {
+                data[DatabaseAccessor::NAME_COLUMN] = zone_data[step][0];
+                data[DatabaseAccessor::TYPE_COLUMN] = zone_data[step][1];
+                data[DatabaseAccessor::TTL_COLUMN] = zone_data[step][2];
+                data[DatabaseAccessor::RDATA_COLUMN] = zone_data[step][3];
+                ++step;
+                return (true);
             }
+            return (false);
         }
     };
     class EmptyIteratorContext : public IteratorContext {
@@ -610,6 +656,17 @@ public:
         }
     }
 
+    virtual IteratorContextPtr getNSEC3Records(const std::string& hash,
+                                               int) const
+    {
+        Domains::const_iterator it(nsec3_namespace_->find(hash));
+        if (it == nsec3_namespace_->end()) {
+            return (IteratorContextPtr(new EmptyIteratorContext()));
+        } else {
+            return (IteratorContextPtr(new DomainIterator(it->second)));
+        }
+    }
+
     virtual pair<bool, int> startUpdateZone(const std::string& zone_name,
                                             bool replace)
     {
@@ -623,8 +680,10 @@ public:
         // original.
         if (replace) {
             update_records_->clear();
+            update_nsec3_namespace_->clear();
         } else {
             *update_records_ = *readonly_records_;
+            *update_nsec3_namespace_ = nsec3_namespace_master_;
         }
 
         if (zone_name == "bad.example.org.") {
@@ -637,7 +696,9 @@ public:
     }
     virtual void commit() {
         *readonly_records_ = *update_records_;
+        *nsec3_namespace_ = *update_nsec3_namespace_;
     }
+
     virtual void rollback() {
         // Special hook: if something with a name of "throw.example.org"
         // has been added, trigger an imaginary unexpected event with an
@@ -648,27 +709,54 @@ public:
 
         rollbacked_ = true;
     }
-    virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
+
+private:
+    // Common subroutine for addRecordToZone and addNSEC3RecordToZone.
+    void addRecord(Domains& domains,
+                   const string (&columns)[ADD_COLUMN_COUNT])
+    {
         // Copy the current value to cur_name.  If it doesn't exist,
         // operator[] will create a new one.
-        cur_name_ = (*update_records_)[columns[DatabaseAccessor::ADD_NAME]];
+        cur_name_ = domains[columns[ADD_NAME]];
 
         vector<string> record_columns;
-        record_columns.push_back(columns[DatabaseAccessor::ADD_TYPE]);
-        record_columns.push_back(columns[DatabaseAccessor::ADD_TTL]);
-        record_columns.push_back(columns[DatabaseAccessor::ADD_SIGTYPE]);
-        record_columns.push_back(columns[DatabaseAccessor::ADD_RDATA]);
-        record_columns.push_back(columns[DatabaseAccessor::ADD_NAME]);
+        record_columns.push_back(columns[ADD_TYPE]);
+        record_columns.push_back(columns[ADD_TTL]);
+        record_columns.push_back(columns[ADD_SIGTYPE]);
+        record_columns.push_back(columns[ADD_RDATA]);
+        record_columns.push_back(columns[ADD_NAME]);
 
         // copy back the added entry
         cur_name_.push_back(record_columns);
-        (*update_records_)[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
+        domains[columns[DatabaseAccessor::ADD_NAME]] = cur_name_;
 
         // remember this one so that test cases can check it.
         copy(columns, columns + DatabaseAccessor::ADD_COLUMN_COUNT,
              columns_lastadded_);
     }
 
+public:
+    virtual void addRecordToZone(const string (&columns)[ADD_COLUMN_COUNT]) {
+        addRecord(*update_records_, columns);
+    }
+
+    virtual void addNSEC3RecordToZone(
+        const string (&columns)[ADD_NSEC3_COLUMN_COUNT])
+    {
+        // Convert the NSEC3 parameters in the normal (non NSEC3) style so
+        // we can share the merge code, and then update using addRecord().
+        string normal_columns[ADD_COLUMN_COUNT];
+
+        normal_columns[ADD_TYPE] = columns[ADD_NSEC3_TYPE];
+        normal_columns[ADD_TTL] = columns[ADD_NSEC3_TTL];
+        normal_columns[ADD_SIGTYPE] = "";
+        normal_columns[ADD_RDATA] = columns[ADD_NSEC3_RDATA];
+        normal_columns[ADD_NAME] = columns[ADD_NSEC3_HASH];
+
+        addRecord(*update_nsec3_namespace_, normal_columns);
+    }
+
+private:
     // Helper predicate class used in deleteRecordInZone().
     struct deleteMatch {
         deleteMatch(const string& type, const string& rdata) :
@@ -681,19 +769,33 @@ public:
         const string& rdata_;
     };
 
-    virtual void deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
+    // Common subroutine for deleteRecordinZone and deleteNSEC3RecordInZone.
+    void deleteRecord(Domains& domains,
+                      const string (&params)[DEL_PARAM_COUNT])
+    {
         vector<vector<string> >& records =
-            (*update_records_)[params[DatabaseAccessor::DEL_NAME]];
+            domains[params[DatabaseAccessor::DEL_NAME]];
         records.erase(remove_if(records.begin(), records.end(),
                                 deleteMatch(
                                     params[DatabaseAccessor::DEL_TYPE],
                                     params[DatabaseAccessor::DEL_RDATA])),
                       records.end());
         if (records.empty()) {
-            (*update_records_).erase(params[DatabaseAccessor::DEL_NAME]);
+            domains.erase(params[DatabaseAccessor::DEL_NAME]);
         }
     }
 
+public:
+    virtual void deleteRecordInZone(const string (&params)[DEL_PARAM_COUNT]) {
+        deleteRecord(*update_records_, params);
+    }
+
+    virtual void deleteNSEC3RecordInZone(
+        const string (&params)[DEL_PARAM_COUNT])
+    {
+        deleteRecord(*update_nsec3_namespace_, params);
+    }
+
     //
     // Helper methods to keep track of some update related activities
     //
@@ -747,6 +849,21 @@ public:
             isc_throw(isc::Unexpected, "Unknown zone ID");
         }
     }
+    virtual std::string findPreviousNSEC3Hash(int,
+                                              const std::string& hash) const
+    {
+        // TODO: Provide some broken data, but it is not known yet how broken
+        // they'll have to be.
+        Domains::const_iterator it(nsec3_namespace_->lower_bound(hash));
+        // We got just after the one we want
+        if (it == nsec3_namespace_->begin()) {
+            // Hmm, we got something really small. So we wrap around.
+            // This is one after the last, so after decreasing it we'll get
+            // the biggest.
+            it = nsec3_namespace_->end();
+        }
+        return ((--it)->first);
+    }
     virtual void addRecordDiff(int id, uint32_t serial,
                                DiffOperation operation,
                                const std::string (&data)[DIFF_PARAM_COUNT])
@@ -827,6 +944,10 @@ private:
     Domains* readonly_records_;
     Domains update_records_master_;
     Domains* update_records_;
+    Domains nsec3_namespace_master_;
+    Domains* nsec3_namespace_;
+    Domains update_nsec3_namespace_master_;
+    Domains* update_nsec3_namespace_;
     const Domains empty_records_master_;
     const Domains* empty_records_;
 
@@ -890,6 +1011,20 @@ private:
         cur_name_.clear();
     }
 
+    // Works in a similar way to addCurName, but it is added to
+    // the NSEC3 namespace. You don't provide the full name, only
+    // the hash part.
+    void addCurHash(const std::string& hash) {
+        ASSERT_EQ(0, nsec3_namespace_->count(hash));
+        // Append the name to all of them
+        for (std::vector<std::vector<std::string> >::iterator
+             i = cur_name_.begin(); i != cur_name_.end(); ++ i) {
+            i->push_back(hash);
+        }
+        (*nsec3_namespace_)[hash] = cur_name_;
+        cur_name_.clear();
+    }
+
     // Fills the database with zone data.
     // This method constructs a number of resource records (with addRecord),
     // which will all be added for one domain name to the fake database
@@ -912,6 +1047,42 @@ private:
                       TEST_RECORDS[i][3], TEST_RECORDS[i][4]);
         }
         addCurName(prev_name);
+        prev_name = NULL;
+        for (int i = 0; TEST_NSEC3_RECORDS[i][0] != NULL; ++i) {
+            if (prev_name != NULL &&
+                strcmp(prev_name, TEST_NSEC3_RECORDS[i][0]) != 0) {
+                addCurHash(prev_name);
+            }
+            prev_name = TEST_NSEC3_RECORDS[i][0];
+            addRecord(TEST_NSEC3_RECORDS[i][1], TEST_NSEC3_RECORDS[i][2],
+                      TEST_NSEC3_RECORDS[i][3], TEST_NSEC3_RECORDS[i][4]);
+        }
+        addCurHash(prev_name);
+    }
+
+public:
+    // This adds the NSEC3PARAM into the apex, so we can perform some NSEC3
+    // tests. Note that the NSEC3 namespace is available in other tests, but
+    // it should not be accessed at that time.
+    void enableNSEC3() {
+        // We place the signature first, so it's in the block with the other
+        // signatures
+        vector<string> signature;
+        signature.push_back("RRSIG");
+        signature.push_back("3600");
+        signature.push_back("");
+        signature.push_back("NSEC3PARAM 5 3 3600 20000101000000 20000201000000 "
+                            "12345 example.org. FAKEFAKEFAKE");
+        signature.push_back("exmaple.org.");
+        (*readonly_records_)["example.org."].push_back(signature);
+        // Now the NSEC3 param itself
+        vector<string> param;
+        param.push_back("NSEC3PARAM");
+        param.push_back("3600");
+        param.push_back("");
+        param.push_back("1 0 12 aabbccdd");
+        param.push_back("example.org.");
+        (*readonly_records_)["example.org."].push_back(param);
     }
 };
 
@@ -966,6 +1137,11 @@ public:
                                                "FAKEFAKEFAKE"));
     }
 
+    ~ DatabaseClientTest() {
+        // Make sure we return the default creator no matter if we set it or not
+        setNSEC3HashCreator(NULL);
+    }
+
     /*
      * We initialize the client from a function, so we can call it multiple
      * times per test.
@@ -979,7 +1155,7 @@ public:
         // probably move this to some specialized templated method specific
         // to SQLite3 (or for even a longer term we should add an API to
         // purge the diffs table).
-        const char* const install_cmd = INSTALL_PROG " " TEST_DATA_DIR
+        const char* const install_cmd = INSTALL_PROG " -c " TEST_DATA_COMMONDIR
             "/rwtest.sqlite3 " TEST_DATA_BUILDDIR
             "/rwtest.sqlite3.copied";
         if (system(install_cmd) != 0) {
@@ -1087,7 +1263,7 @@ public:
                     rdata::createRdata(expected_rrset->getType(),
                                        expected_rrset->getClass(),
                                        (*it).data_[Accessor::DIFF_RDATA]));
-                isc::testutils::rrsetCheck(expected_rrset, rrset);
+                rrsetCheck(expected_rrset, rrset);
             }
             // We should have examined all entries of both expected and
             // actual data.
@@ -1126,6 +1302,9 @@ public:
     const std::vector<std::string> empty_rdatas_; // for NXRRSET/NXDOMAIN
     std::vector<std::string> expected_rdatas_;
     std::vector<std::string> expected_sig_rdatas_;
+
+    // A creator for use in several NSEC3 related tests.
+    TestNSEC3HashCreator test_nsec3_hash_creator_;
 };
 
 class TestSQLite3Accessor : public SQLite3Accessor {
@@ -1253,10 +1432,10 @@ checkRRset(isc::dns::ConstRRsetPtr rrset,
             isc::dns::rdata::createRdata(rrtype, rrclass,
                                          rdatas[i]));
     }
-    isc::testutils::rrsetCheck(expected_rrset, rrset);
+    rrsetCheck(expected_rrset, rrset);
 }
 
-// Iterate through a zone
+// Iterate through a zone, common case
 TYPED_TEST(DatabaseClientTest, iterator) {
     ZoneIteratorPtr it(this->client_->getIterator(Name("example.org")));
     ConstRRsetPtr rrset(it->getNextRRset());
@@ -1264,47 +1443,100 @@ TYPED_TEST(DatabaseClientTest, iterator) {
 
     // The first name should be the zone origin.
     EXPECT_EQ(this->zname_, rrset->getName());
+}
 
-    // The rest of the checks work only for the mock accessor.
-    if (!this->is_mock_) {
-        return;
-    }
-
-    this->expected_rdatas_.clear();
-    this->expected_rdatas_.push_back("192.0.2.1");
-    checkRRset(rrset, Name("example.org"), this->qclass_, RRType::A(),
-               this->rrttl_, this->expected_rdatas_);
-
-    rrset = it->getNextRRset();
-    this->expected_rdatas_.clear();
-    this->expected_rdatas_.push_back("ns1.example.org. admin.example.org. "
-                                     "1234 3600 1800 2419200 7200");
-    checkRRset(rrset, Name("example.org"), this->qclass_, RRType::SOA(),
-               this->rrttl_, this->expected_rdatas_);
-
-    rrset = it->getNextRRset();
-    this->expected_rdatas_.clear();
-    this->expected_rdatas_.push_back("192.0.2.1");
-    this->expected_rdatas_.push_back("192.0.2.2");
-    checkRRset(rrset, Name("x.example.org"), this->qclass_, RRType::A(),
-               RRTTL(300), this->expected_rdatas_);
-
-    rrset = it->getNextRRset();
-    this->expected_rdatas_.clear();
-    this->expected_rdatas_.push_back("2001:db8::1");
-    this->expected_rdatas_.push_back("2001:db8::2");
-    checkRRset(rrset, Name("x.example.org"), this->qclass_, RRType::AAAA(),
-               RRTTL(300), this->expected_rdatas_);
-
-    rrset = it->getNextRRset();
-    ASSERT_NE(ConstRRsetPtr(), rrset);
-    this->expected_rdatas_.clear();
-    this->expected_rdatas_.push_back("192.0.2.1");
-    this->expected_rdatas_.push_back("192.0.2.2");
-    checkRRset(rrset, Name("ttldiff.example.org"), this->qclass_, RRType::A(),
-               RRTTL(300), this->expected_rdatas_);
+// Supplemental structure used in the couple of tests below.  It represents
+// parameters of an expected RRset containing up to two RDATAs.  If it contains
+// only one RDATA, rdata2 is NULL.
+struct ExpectedRRset {
+    const char* const name;
+    const RRType rrtype;
+    const RRTTL rrttl;
+    const char* const rdata1;
+    const char* const rdata2;
+};
 
-    EXPECT_EQ(ConstRRsetPtr(), it->getNextRRset());
+// Common checker for the iterator tests below.  It extracts RRsets from the
+// give iterator and compare them to the expected sequence.
+void
+checkIteratorSequence(ZoneIterator& it, ExpectedRRset expected_sequence[],
+                      size_t num_rrsets)
+{
+    vector<string> expected_rdatas;
+    for (size_t i = 0; i < num_rrsets; ++i) {
+        const ConstRRsetPtr rrset = it.getNextRRset();
+        ASSERT_TRUE(rrset);
+
+        expected_rdatas.clear();
+        expected_rdatas.push_back(expected_sequence[i].rdata1);
+        if (expected_sequence[i].rdata2 != NULL) {
+            expected_rdatas.push_back(expected_sequence[i].rdata2);
+        }
+        checkRRset(rrset, Name(expected_sequence[i].name), RRClass::IN(),
+                   expected_sequence[i].rrtype, expected_sequence[i].rrttl,
+                   expected_rdatas);
+    }
+    EXPECT_FALSE(it.getNextRRset());
+}
+
+TEST_F(MockDatabaseClientTest, iterator) {
+    // This version of test creates an iterator that combines same types of
+    // RRs into single RRsets.
+    ExpectedRRset expected_sequence[] = {
+        {"example.org", RRType::A(), rrttl_, "192.0.2.1", NULL},
+        {"example.org", RRType::SOA(), rrttl_,
+         "ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200",
+         NULL},
+        {"x.example.org", RRType::A(), RRTTL(300), "192.0.2.1", "192.0.2.2"},
+        {"x.example.org", RRType::AAAA(), RRTTL(300),
+         "2001:db8::1", "2001:db8::2"},
+        {"x.example.org", RRType::RRSIG(), RRTTL(300),
+         "A 5 3 3600 20000101000000 20000201000000 12345 example.org. "
+         "FAKEFAKEFAKE", NULL},
+        {"x.example.org", RRType::RRSIG(), RRTTL(300),
+         "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. "
+         "FAKEFAKEFAKEFAKE", NULL},
+        {"ttldiff.example.org", RRType::A(), RRTTL(300),
+         "192.0.2.1", "192.0.2.2"},
+        {"ttldiff2.example.org", RRType::AAAA(), RRTTL(300),
+         "2001:db8::1", "2001:db8::2"}
+    };
+    checkIteratorSequence(*client_->getIterator(Name("example.org")),
+                          expected_sequence,
+                          sizeof(expected_sequence) /
+                          sizeof(expected_sequence[0]));
+}
+
+TEST_F(MockDatabaseClientTest, iteratorSeparateRRs) {
+    // This version of test creates an iterator that separates all RRs as
+    // individual RRsets.  In particular, it preserves the TTLs of an RRset
+    // even if they are different.
+    ExpectedRRset expected_sequence[] = {
+        {"example.org", RRType::A(), rrttl_, "192.0.2.1", NULL},
+        {"example.org", RRType::SOA(), rrttl_,
+         "ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200",
+         NULL},
+        {"x.example.org", RRType::A(), RRTTL(300), "192.0.2.1", NULL},
+        {"x.example.org", RRType::A(), RRTTL(300), "192.0.2.2", NULL},
+        {"x.example.org", RRType::AAAA(), RRTTL(300), "2001:db8::1", NULL},
+        {"x.example.org", RRType::AAAA(), RRTTL(300), "2001:db8::2", NULL},
+        {"x.example.org", RRType::RRSIG(), RRTTL(300),
+         "A 5 3 3600 20000101000000 20000201000000 12345 example.org. "
+         "FAKEFAKEFAKE", NULL},
+        {"x.example.org", RRType::RRSIG(), RRTTL(300),
+         "AAAA 5 3 3600 20000101000000 20000201000000 12345 example.org. "
+         "FAKEFAKEFAKEFAKE", NULL},
+        {"ttldiff.example.org", RRType::A(), RRTTL(300), "192.0.2.1", NULL},
+        {"ttldiff.example.org", RRType::A(), RRTTL(600), "192.0.2.2", NULL},
+        {"ttldiff2.example.org", RRType::AAAA(), RRTTL(600), "2001:db8::1",
+         NULL},
+        {"ttldiff2.example.org", RRType::AAAA(), RRTTL(300), "2001:db8::2",
+         NULL}
+    };
+    checkIteratorSequence(*client_->getIterator(Name("example.org"), true),
+                          expected_sequence,
+                          sizeof(expected_sequence) /
+                          sizeof(expected_sequence[0]));
 }
 
 // This has inconsistent TTL in the set (the rest, like nonsense in
@@ -1336,7 +1568,7 @@ TYPED_TEST(DatabaseClientTest, getSOAFromIterator) {
     }
     ASSERT_TRUE(rrset);
     // It should be identical to the result of getSOA().
-    isc::testutils::rrsetCheck(it->getSOA(), rrset);
+    rrsetCheck(it->getSOA(), rrset);
 }
 
 TYPED_TEST(DatabaseClientTest, noSOAFromIterator) {
@@ -1364,7 +1596,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
 
         // Confirm at least it doesn't contain any SOA
         EXPECT_EQ(ZoneFinder::NXDOMAIN,
-                  this->getFinder()->find(this->zname_, RRType::SOA()).code);
+                  this->getFinder()->find(this->zname_, RRType::SOA())->code);
     } catch (const DataSourceError&) {}
 
     ConstRRsetPtr rrset;
@@ -1374,7 +1606,7 @@ TYPED_TEST(DatabaseClientTest, iterateThenUpdate) {
     }
     ASSERT_TRUE(rrset);
     // It should be identical to the result of getSOA().
-    isc::testutils::rrsetCheck(it->getSOA(), rrset);
+    rrsetCheck(it->getSOA(), rrset);
 }
 
 TYPED_TEST(DatabaseClientTest, updateThenIterateThenUpdate) {
@@ -1422,31 +1654,33 @@ doFindTest(ZoneFinder& finder,
            const ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT)
 {
     SCOPED_TRACE("doFindTest " + name.toText() + " " + type.toText());
-    const ZoneFinder::FindResult result = finder.find(name, type, options);
-    ASSERT_EQ(expected_result, result.code) << name << " " << type;
+    ConstZoneFinderContextPtr result = finder.find(name, type, options);
+    ASSERT_EQ(expected_result, result->code) << name << " " << type;
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-              result.isWildcard());
+              result->isWildcard());
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
-              result.isNSECSigned());
+              result->isNSECSigned());
     EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
-              result.isNSEC3Signed());
-    if (!expected_rdatas.empty() && result.rrset) {
-        checkRRset(result.rrset, expected_name != Name(".") ? expected_name :
+              result->isNSEC3Signed());
+    if (!expected_rdatas.empty() && result->rrset) {
+        checkRRset(result->rrset, expected_name != Name(".") ? expected_name :
                    name, finder.getClass(), expected_type, expected_ttl,
                    expected_rdatas);
 
-        if (!expected_sig_rdatas.empty() && result.rrset->getRRsig()) {
-            checkRRset(result.rrset->getRRsig(), expected_name != Name(".") ?
+        if (!expected_sig_rdatas.empty() && result->rrset->getRRsig()) {
+            checkRRset(result->rrset->getRRsig(), expected_name != Name(".") ?
                        expected_name : name, finder.getClass(),
                        isc::dns::RRType::RRSIG(), expected_ttl,
                        expected_sig_rdatas);
         } else if (expected_sig_rdatas.empty()) {
-            EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset->getRRsig());
+            EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset->getRRsig()) <<
+                "Unexpected RRSIG: " << result->rrset->getRRsig()->toText();
         } else {
             ADD_FAILURE() << "Missing RRSIG";
         }
     } else if (expected_rdatas.empty()) {
-        EXPECT_EQ(isc::dns::RRsetPtr(), result.rrset);
+        EXPECT_EQ(isc::dns::RRsetPtr(), result->rrset) <<
+            "Unexpected RRset: " << result->rrset->toText();
     } else {
         ADD_FAILURE() << "Missing result";
     }
@@ -1460,15 +1694,26 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
                     const isc::dns::Name& expected_name =
                     isc::dns::Name::ROOT_NAME(),
                     const ZoneFinder::FindOptions options =
-                    ZoneFinder::FIND_DEFAULT)
+                    ZoneFinder::FIND_DEFAULT,
+                    ZoneFinder::FindResultFlags expected_flags =
+                                          ZoneFinder::RESULT_DEFAULT)
 {
     SCOPED_TRACE("All test for " + name.toText());
     std::vector<ConstRRsetPtr> target;
-    ZoneFinder::FindResult result(finder.findAll(name, target, options));
+    ConstZoneFinderContextPtr result(finder.findAll(name, target, options));
     EXPECT_TRUE(target.empty());
-    EXPECT_EQ(expected_result, result.code);
-    EXPECT_EQ(expected_type, result.rrset->getType());
-    RdataIteratorPtr it(result.rrset->getRdataIterator());
+    EXPECT_EQ(expected_result, result->code);
+    EXPECT_EQ(expected_type, result->rrset->getType());
+    if (expected_flags != ZoneFinder::RESULT_DEFAULT){
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
+                  result->isWildcard());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
+                  result->isNSECSigned());
+        EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
+                  result->isNSEC3Signed());
+
+    }
+    RdataIteratorPtr it(result->rrset->getRdataIterator());
     std::vector<std::string> rdata;
     while (!it->isLast()) {
         rdata.push_back(it->getCurrent().toText());
@@ -1482,7 +1727,7 @@ doFindAllTestResult(ZoneFinder& finder, const isc::dns::Name& name,
     }
     EXPECT_TRUE(expected_rdata == rdata);
     EXPECT_EQ(expected_name == isc::dns::Name::ROOT_NAME() ? name :
-              expected_name, result.rrset->getName());
+              expected_name, result->rrset->getName());
 }
 
 // When asking for an RRset where RRs somehow have different TTLs, it should 
@@ -1779,38 +2024,30 @@ TYPED_TEST(DatabaseClientTest, find) {
 }
 
 TYPED_TEST(DatabaseClientTest, findOutOfZone) {
-    // If the query name is out-of-zone it should result in NXDOMAIN
+    // If the query name is out-of-zone it should result in an exception
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
     vector<ConstRRsetPtr> target;
 
     // Superdomain
-    doFindTest(*finder, Name("org"), this->qtype_, this->qtype_,
-               this->rrttl_, ZoneFinder::NXDOMAIN,
-               this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("org"), target).code);
+    EXPECT_THROW(finder->find(Name("org"), this->qtype_), OutOfZone);
+    EXPECT_THROW(finder->findAll(Name("org"), target), OutOfZone);
+
     // sharing a common ancestor
-    doFindTest(*finder, Name("noexample.org"), this->qtype_, this->qtype_,
-               this->rrttl_, ZoneFinder::NXDOMAIN,
-               this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("noexample.org"),
-                                                    target).code);
+    EXPECT_THROW(finder->find(Name("noexample.org"), this->qtype_), OutOfZone);
+    EXPECT_THROW(finder->findAll(Name("noexample.org"), target), OutOfZone);
+
     // totally unrelated domain, smaller number of labels
-    doFindTest(*finder, Name("com"), this->qtype_, this->qtype_,
-               this->rrttl_, ZoneFinder::NXDOMAIN,
-               this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("com"), target).code);
+    EXPECT_THROW(finder->find(Name("com"), this->qtype_), OutOfZone);
+    EXPECT_THROW(finder->findAll(Name("com"), target), OutOfZone);
+
     // totally unrelated domain, same number of labels
-    doFindTest(*finder, Name("example.com"), this->qtype_, this->qtype_,
-               this->rrttl_, ZoneFinder::NXDOMAIN,
-               this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("example.com"),
-                                                    target).code);
+    EXPECT_THROW(finder->find(Name("example.com"), this->qtype_), OutOfZone);
+    EXPECT_THROW(finder->findAll(Name("example.com"), target), OutOfZone);
+
     // totally unrelated domain, larger number of labels
-    doFindTest(*finder, Name("more.example.com"), this->qtype_, this->qtype_,
-               this->rrttl_, ZoneFinder::NXDOMAIN,
-               this->empty_rdatas_, this->empty_rdatas_);
-    EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->findAll(Name("more.example.com"),
-                                                    target).code);
+    EXPECT_THROW(finder->find(Name("more.example.com"), this->qtype_),
+                 OutOfZone);
+    EXPECT_THROW(finder->findAll(Name("more.example.com"), target), OutOfZone);
 }
 
 TYPED_TEST(DatabaseClientTest, findDelegation) {
@@ -2281,10 +2518,169 @@ TYPED_TEST(DatabaseClientTest, wildcardNXRRSET_NSEC) {
                Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
 }
 
+// Subroutine for dnssecFlagCheck defined below.  It performs some simple
+// checks regarding DNSSEC related result flags for findAll().
+void
+dnssecFlagCheckForAny(ZoneFinder& finder, const Name& name,
+                      ZoneFinder::FindResultFlags sec_flag)
+{
+    std::vector<ConstRRsetPtr> target; // just for placeholder
+    ConstZoneFinderContextPtr all_result =
+        finder.findAll(name, target, ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0,
+              all_result->isNSECSigned());
+    EXPECT_EQ((sec_flag & ZoneFinder::RESULT_NSEC3_SIGNED) != 0,
+              all_result->isNSEC3Signed());
+}
+
+// Common tests about DNSSEC related result flags.  Shared for the NSEC
+// and NSEC3 cases.
+void
+dnssecFlagCheck(ZoneFinder& finder, ZoneFinder::FindResultFlags sec_flag) {
+    std::vector<std::string> expected_rdatas;
+    std::vector<std::string> expected_sig_rdatas;
+
+    // Check NXDOMAIN case in NSEC signed zone, and RESULT_NSEC_SIGNED flag
+    // should be returned to upper layer caller.
+    if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        expected_rdatas.push_back("www2.example.org. A AAAA NSEC RRSIG");
+        expected_sig_rdatas.push_back("NSEC 5 3 3600 20000101000000 "
+                                      "20000201000000 12345 example.org. "
+                                      "FAKEFAKEFAKE");
+    }
+    doFindTest(finder, Name("www1.example.org"), RRType::A(), RRType::NSEC(),
+               RRTTL(3600), ZoneFinder::NXDOMAIN, expected_rdatas,
+               expected_sig_rdatas, sec_flag, Name("www.example.org."),
+               ZoneFinder::FIND_DNSSEC);
+    dnssecFlagCheckForAny(finder, Name("www1.example.org"), sec_flag);
+
+    // Check NXRRSET case in NSEC signed zone, and RESULT_NSEC_SIGNED flag
+    // should be return.
+    // No "findAll" test case for this because NXRRSET shouldn't happen for it.
+    expected_rdatas.clear();
+    expected_sig_rdatas.clear();
+    if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        expected_rdatas.push_back("www2.example.org. A AAAA NSEC RRSIG");
+        expected_sig_rdatas.push_back("NSEC 5 3 3600 20000101000000 "
+                                      "20000201000000 12345 example.org. "
+                                      "FAKEFAKEFAKE");
+    }
+    doFindTest(finder, Name("www.example.org."), RRType::TXT(), RRType::NSEC(),
+               RRTTL(3600), ZoneFinder::NXRRSET, expected_rdatas,
+               expected_sig_rdatas, sec_flag, Name::ROOT_NAME(),
+               ZoneFinder::FIND_DNSSEC);
+
+    // Empty name, should result in NXRRSET (in this test setup the NSEC
+    // doesn't have RRSIG).
+    expected_rdatas.clear();
+    expected_sig_rdatas.clear();
+    if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        expected_rdatas.push_back("empty.nonterminal.example.org. NSEC");
+    }
+    doFindTest(finder, Name("nonterminal.example.org."), RRType::A(),
+               RRType::NSEC(), RRTTL(3600), ZoneFinder::NXRRSET,
+               expected_rdatas,expected_sig_rdatas, sec_flag,
+               Name("l.example.org."), ZoneFinder::FIND_DNSSEC);
+    dnssecFlagCheckForAny(finder, Name("nonterminal.example.org"), sec_flag);
+
+    // Wildcard match
+    expected_rdatas.clear();
+    expected_sig_rdatas.clear();
+    expected_rdatas.push_back("192.0.2.5");
+    expected_sig_rdatas.push_back("A 5 3 3600 20000101000000 "
+                                  "20000201000000 12345 example.org. "
+                                  "FAKEFAKEFAKE");
+    doFindTest(finder, Name("b.a.wild.example.org"), RRType::A(),
+               RRType::A(), RRTTL(3600), ZoneFinder::SUCCESS, expected_rdatas,
+               expected_sig_rdatas, (ZoneFinder::RESULT_WILDCARD | sec_flag),
+               Name("b.a.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+    dnssecFlagCheckForAny(finder, Name("b.a.wild.example.org"), sec_flag);
+
+    // Wildcard + NXRRSET (no "findAll" test for this case)
+    expected_rdatas.clear();
+    expected_sig_rdatas.clear();
+    if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        expected_rdatas.push_back("cancel.here.wild.example.org. "
+                                  "A NSEC RRSIG");
+        expected_sig_rdatas.push_back("NSEC 5 3 3600 20000101000000 "
+                                      "20000201000000 12345 example.org. "
+                                      "FAKEFAKEFAKE");
+    }
+    doFindTest(finder, Name("b.a.wild.example.org"),
+               RRType::TXT(), RRType::NSEC(), RRTTL(3600), ZoneFinder::NXRRSET,
+               expected_rdatas, expected_sig_rdatas,
+               (ZoneFinder::RESULT_WILDCARD | sec_flag),
+               Name("*.wild.example.org"), ZoneFinder::FIND_DNSSEC);
+
+    // Empty wildcard (this NSEC doesn't have RRSIG in our test data)
+    expected_rdatas.clear();
+    expected_sig_rdatas.clear();
+    if ((sec_flag & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        expected_rdatas.push_back("wild.*.foo.*.bar.example.org. NSEC");
+    }
+    doFindTest(finder, Name("foo.wild.bar.example.org"),
+               RRType::TXT(), RRType::NSEC(), RRTTL(3600), ZoneFinder::NXRRSET,
+               expected_rdatas, expected_sig_rdatas,
+               (ZoneFinder::RESULT_WILDCARD | sec_flag),
+               Name("bao.example.org"), ZoneFinder::FIND_DNSSEC);
+    dnssecFlagCheckForAny(finder, Name("foo.wild.bar.example.org"), sec_flag);
+}
+
+TYPED_TEST(DatabaseClientTest, dnssecResultFlags) {
+    // ZoneFinder::find() for negative cases and wildcard cases should check
+    // whether the zone is signed with NSEC or NSEC3.
+
+    // In the default test setup, the zone should be considered NSEC-signed
+    // (the apex node has an NSEC RR).
+    {
+        SCOPED_TRACE("NSEC only");
+        dnssecFlagCheck(*this->getFinder(), ZoneFinder::RESULT_NSEC_SIGNED);
+    }
+
+    // Then add an NSEC3PARAM RRset at the apex (it may look weird if the
+    // zone only has NSEC3PARM RRset (but no NSEC3s), but it is okay for the
+    // purpose of this test).  The zone should now be considered NSEC3-signed.
+    // Note that the apex NSEC still exists; NSEC3 should override NSEC.
+    this->updater_ = this->client_->getUpdater(this->zname_, false);
+    this->rrset_.reset(new RRset(this->zname_, this->qclass_,
+                                 RRType::NSEC3PARAM(), this->rrttl_));
+    this->rrset_->addRdata(rdata::createRdata(this->rrset_->getType(),
+                                              this->rrset_->getClass(),
+                                              "1 0 12 aabbccdd"));
+    this->updater_->addRRset(*this->rrset_);
+    {
+        SCOPED_TRACE("NSEC and NSEC3");
+        dnssecFlagCheck(this->updater_->getFinder(),
+                        ZoneFinder::RESULT_NSEC3_SIGNED);
+    }
+
+    // Next, delete the apex NSEC.  Since NSEC3PARAM remains, the zone should
+    // still be considered NSEC3-signed.
+    RRsetPtr nsec_rrset(new RRset(this->zname_, this->qclass_, RRType::NSEC(),
+                                  this->rrttl_));
+    nsec_rrset->addRdata(rdata::createRdata(RRType::NSEC(), this->qclass_,
+                                            "acnamesig1.example.org. NS A "
+                                            "NSEC RRSIG"));
+    this->updater_->deleteRRset(*nsec_rrset);
+    {
+        SCOPED_TRACE("NSEC3 only");
+        dnssecFlagCheck(this->updater_->getFinder(),
+                        ZoneFinder::RESULT_NSEC3_SIGNED);
+    }
+
+    // Finally, delete the NSEC3PARAM we just added above.  The zone should
+    // then be considered unsigned.
+    this->updater_->deleteRRset(*this->rrset_);
+    {
+        SCOPED_TRACE("unsigned");
+        dnssecFlagCheck(this->updater_->getFinder(),
+                        ZoneFinder::RESULT_DEFAULT);
+    }
+}
+
 TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
     // The domain doesn't exist, so we must get the right NSEC
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
-
     this->expected_rdatas_.push_back("www2.example.org. A AAAA NSEC RRSIG");
     this->expected_sig_rdatas_.push_back("NSEC 5 3 3600 20000101000000 "
                                          "20000201000000 12345 example.org. "
@@ -2311,14 +2707,13 @@ TYPED_TEST(DatabaseClientTest, NXDOMAIN_NSEC) {
     if (!this->is_mock_) {
         return; // We don't make the real DB to throw
     }
-    EXPECT_NO_THROW(doFindTest(*finder,
-                               isc::dns::Name("notimplnsec.example.org."),
-                               isc::dns::RRType::TXT(),
-                               isc::dns::RRType::NSEC(), this->rrttl_,
-                               ZoneFinder::NXDOMAIN, this->empty_rdatas_,
-                               this->empty_rdatas_,
-                               ZoneFinder::RESULT_DEFAULT,
-                               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
+    // In this case the accessor doesn't support findPreviousName(), but the
+    // zone apex has NSEC, and the zone itself is considered NSEC-signed.
+    doFindTest(*finder, Name("notimplnsec.example.org."),
+               RRType::TXT(), RRType::NSEC(), this->rrttl_,
+               ZoneFinder::NXDOMAIN, this->empty_rdatas_,
+               this->empty_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
 }
 
 TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
@@ -2338,14 +2733,12 @@ TYPED_TEST(DatabaseClientTest, emptyNonterminalNSEC) {
     if (!this->is_mock_) {
         return; // We don't make the real DB to throw
     }
-    EXPECT_NO_THROW(doFindTest(*finder,
-                               isc::dns::Name("here.wild.example.org."),
-                               isc::dns::RRType::TXT(),
-                               isc::dns::RRType::NSEC(),
-                               this->rrttl_, ZoneFinder::NXRRSET,
-                               this->empty_rdatas_, this->empty_rdatas_,
-                               ZoneFinder::RESULT_DEFAULT,
-                               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC));
+    // See the corresponding case of NXDOMAIN_NSEC.
+    doFindTest(*finder, Name("here.wild.example.org."),
+               RRType::TXT(), RRType::NSEC(), this->rrttl_,
+               ZoneFinder::NXRRSET, this->empty_rdatas_,
+               this->empty_rdatas_, ZoneFinder::RESULT_NSEC_SIGNED,
+               Name::ROOT_NAME(), ZoneFinder::FIND_DNSSEC);
 }
 
 TYPED_TEST(DatabaseClientTest, anyFromFind) {
@@ -2363,11 +2756,11 @@ TYPED_TEST(DatabaseClientTest, getAll) {
     std::vector<ConstRRsetPtr> target;
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               finder->findAll(isc::dns::Name("nothere.example.org."),
-                              target).code);
+                              target)->code);
     EXPECT_TRUE(target.empty());
     EXPECT_EQ(ZoneFinder::NXRRSET,
               finder->findAll(isc::dns::Name("here.wild.example.org."),
-                              target).code);
+                              target)->code);
     this->expected_rdatas_.push_back("ns.delegation.example.org.");
     this->expected_rdatas_.push_back("ns.example.com.");
     doFindAllTestResult(*finder, isc::dns::Name("xx.delegation.example.org."),
@@ -2388,7 +2781,7 @@ TYPED_TEST(DatabaseClientTest, getAll) {
     // It should get the data on success
     EXPECT_EQ(ZoneFinder::SUCCESS,
               finder->findAll(isc::dns::Name("www2.example.org."),
-                              target).code);
+                              target)->code);
     ASSERT_EQ(2, target.size());
     size_t a_idx(target[1]->getType() == RRType::A());
     EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2412,13 +2805,13 @@ TYPED_TEST(DatabaseClientTest, getAll) {
 
     // And on wildcard. Check the signatures as well.
     target.clear();
-    const ZoneFinder::FindResult result =
+    ConstZoneFinderContextPtr result =
         finder->findAll(Name("a.wild.example.org"), target,
                         ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
-    EXPECT_TRUE(result.isWildcard());
-    EXPECT_TRUE(result.isNSECSigned());
-    EXPECT_FALSE(result.isNSEC3Signed());
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    EXPECT_TRUE(result->isWildcard());
+    EXPECT_TRUE(result->isNSECSigned());
+    EXPECT_FALSE(result->isNSEC3Signed());
     ASSERT_EQ(2, target.size());
     a_idx = target[1]->getType() == RRType::A();
     EXPECT_EQ(RRType::A(), target[a_idx]->getType());
@@ -2496,22 +2889,22 @@ TYPED_TEST(DatabaseClientTest, flushZone) {
 
     // Before update, the name exists.
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
-                                                this->qtype_).code);
+                                                this->qtype_)->code);
 
     // start update in the replace mode.  the normal finder should still
     // be able to see the record, but the updater's finder shouldn't.
     this->updater_ = this->client_->getUpdater(this->zname_, true);
     this->setUpdateAccessor();
     EXPECT_EQ(ZoneFinder::SUCCESS,
-              finder->find(this->qname_, this->qtype_).code);
+              finder->find(this->qname_, this->qtype_)->code);
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               this->updater_->getFinder().find(this->qname_,
-                                               this->qtype_).code);
+                                               this->qtype_)->code);
 
     // commit the update.  now the normal finder shouldn't see it.
     this->updater_->commit();
     EXPECT_EQ(ZoneFinder::NXDOMAIN, finder->find(this->qname_,
-                                                 this->qtype_).code);
+                                                 this->qtype_)->code);
 
     // Check rollback wasn't accidentally performed.
     EXPECT_FALSE(this->isRollbacked());
@@ -2522,13 +2915,13 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
 
     ZoneFinderPtr finder = this->client_->findZone(this->zname_).zone_finder;
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
-                                                this->qtype_).code);
+                                                this->qtype_)->code);
 
     this->updater_ = this->client_->getUpdater(this->zname_, true);
     this->setUpdateAccessor();
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               this->updater_->getFinder().find(this->qname_,
-                                               this->qtype_).code);
+                                               this->qtype_)->code);
     // DB should not have been rolled back yet.
     EXPECT_FALSE(this->isRollbacked());
     this->updater_.reset();            // destruct without commit
@@ -2538,7 +2931,7 @@ TYPED_TEST(DatabaseClientTest, updateCancel) {
     // isRollbacked())
     EXPECT_TRUE(this->isRollbacked(true));
     EXPECT_EQ(ZoneFinder::SUCCESS, finder->find(this->qname_,
-                                                this->qtype_).code);
+                                                this->qtype_)->code);
 }
 
 TYPED_TEST(DatabaseClientTest, exceptionFromRollback) {
@@ -2609,6 +3002,96 @@ TYPED_TEST(DatabaseClientTest, addRRsetToNewZone) {
     this->checkLastAdded(rrset_added);
 }
 
+//
+// Below we define a set of NSEC3 update tests.
+//
+// Commonly used data for NSEC3 update tests below.
+const char* const nsec3_hash = "1BB7SO0452U1QHL98UISNDD9218GELR5";
+const char* const nsec3_rdata = "1 1 12 AABBCCDD "
+    "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG NSEC3PARAM";
+const char* const nsec3_rdata2 = "1 1 12 AABBCCDD "
+    "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA RRSIG"; // differ in bitmaps
+const char* const nsec3_sig_rdata = "NSEC3 5 3 3600 20000101000000 "
+    "20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const nsec3_sig_rdata2 = "NSEC3 5 3 3600 20000101000000 "
+    "20000201000000 12345 example.org. FAKEFAKE"; // differ in the signature
+
+// Commonly used subroutine that checks if we can get the expected record.
+// According to the API, implementations can skip filling in columns other
+// than those explicitly checked below, so we don't check them.
+void
+nsec3Check(const vector<ConstRRsetPtr>& expected_rrsets,
+           const Name& zone_name, const string& expected_hash,
+           DatabaseAccessor& accessor)
+{
+    const int zone_id = accessor.getZone(zone_name.toText()).second;
+    DatabaseAccessor::IteratorContextPtr itctx =
+        accessor.getNSEC3Records(expected_hash, zone_id);
+    ASSERT_TRUE(itctx);
+
+    // Build a list of matched RRsets and compare the both expected and built
+    // ones as sets.
+    string columns[DatabaseAccessor::COLUMN_COUNT];
+    vector<ConstRRsetPtr> actual_rrsets;
+    while (itctx->getNext(columns)) {
+        actual_rrsets.push_back(
+            textToRRset(expected_hash + "." + zone_name.toText() + " " +
+                        columns[DatabaseAccessor::TTL_COLUMN] + " IN " +
+                        columns[DatabaseAccessor::TYPE_COLUMN] + " " +
+                        columns[DatabaseAccessor::RDATA_COLUMN]));
+    }
+    rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                actual_rrsets.begin(), actual_rrsets.end());
+}
+
+TYPED_TEST(DatabaseClientTest, addDeleteNSEC3InZone) {
+    // Add one NSEC3 RR to the zone, delete it, and add another one.
+    this->updater_ = this->client_->getUpdater(this->zname_, true);
+    const ConstRRsetPtr nsec3_rrset =
+        textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+                    string(nsec3_rdata));
+    const ConstRRsetPtr nsec3_rrset2 =
+        textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+                    string(nsec3_rdata2));
+    this->updater_->addRRset(*nsec3_rrset);
+    this->updater_->deleteRRset(*nsec3_rrset);
+    this->updater_->addRRset(*nsec3_rrset2);
+    this->updater_->commit();
+
+    // Check if we can get the expected record.
+    vector<ConstRRsetPtr> expected_rrsets;
+    expected_rrsets.push_back(nsec3_rrset2);
+    nsec3Check(expected_rrsets, this->zname_, nsec3_hash,
+               *this->current_accessor_);
+}
+
+TYPED_TEST(DatabaseClientTest, addDeleteNSEC3AndRRSIGToZone) {
+    // Add one NSEC3 RR and its RRSIG to the zone, delete the RRSIG and add
+    // a new one.
+    this->updater_ = this->client_->getUpdater(this->zname_, true);
+    const ConstRRsetPtr nsec3_rrset =
+        textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+                    string(nsec3_rdata));
+    const ConstRRsetPtr nsec3_sig_rrset =
+        textToRRset(string(nsec3_hash) + ".example.org. 3600 IN RRSIG " +
+                    string(nsec3_sig_rdata));
+    const ConstRRsetPtr nsec3_sig_rrset2 =
+        textToRRset(string(nsec3_hash) + ".example.org. 3600 IN RRSIG " +
+                    string(nsec3_sig_rdata2));
+    this->updater_->addRRset(*nsec3_rrset);
+    this->updater_->addRRset(*nsec3_sig_rrset);
+    this->updater_->deleteRRset(*nsec3_sig_rrset);
+    this->updater_->addRRset(*nsec3_sig_rrset2);
+    this->updater_->commit();
+
+    // Check if we can get the expected record.
+    vector<ConstRRsetPtr> expected_rrsets;
+    expected_rrsets.push_back(nsec3_rrset);
+    expected_rrsets.push_back(nsec3_sig_rrset2);
+    nsec3Check(expected_rrsets, this->zname_, nsec3_hash,
+               *this->current_accessor_);
+}
+
 TYPED_TEST(DatabaseClientTest, addRRsetToCurrentZone) {
     // Similar to the previous test, but not replacing the existing data.
     boost::shared_ptr<DatabaseClient::Finder> finder(this->getFinder());
@@ -2738,14 +3221,13 @@ TYPED_TEST(DatabaseClientTest, addDeviantRR) {
     this->expected_rdatas_.clear();
     this->expected_rdatas_.push_back("192.0.2.100");
     {
-        // Note: find() rejects out-of-zone query name with NXDOMAIN
+        // Note: find() rejects out-of-zone query name with an exception
         // regardless of whether adding the RR succeeded, so this check
         // actually doesn't confirm it.
         SCOPED_TRACE("add out-of-zone RR");
-        doFindTest(this->updater_->getFinder(), Name("example.com"),
-                   this->qtype_, this->qtype_, this->rrttl_,
-                   ZoneFinder::NXDOMAIN, this->empty_rdatas_,
-                   this->empty_rdatas_);
+        EXPECT_THROW(this->updater_->getFinder().find(Name("example.com"),
+                                                      this->qtype_),
+                     OutOfZone);
     }
 }
 
@@ -3156,6 +3638,49 @@ TYPED_TEST(DatabaseClientTest, journal) {
     this->checkJournal(expected);
 }
 
+TYPED_TEST(DatabaseClientTest, journalForNSEC3) {
+    // Similar to the previous test, but adding/deleting NSEC3 RRs, just to
+    // confirm that NSEC3 is not special for managing diffs.
+    const ConstRRsetPtr nsec3_rrset =
+        textToRRset(string(nsec3_hash) + ".example.org. 3600 IN NSEC3 " +
+                    string(nsec3_rdata));
+
+    this->updater_ = this->client_->getUpdater(this->zname_, false, true);
+    this->updater_->deleteRRset(*this->soa_);
+    this->updater_->deleteRRset(*nsec3_rrset);
+
+    this->soa_.reset(new RRset(this->zname_, this->qclass_, RRType::SOA(),
+                               this->rrttl_));
+    this->soa_->addRdata(rdata::createRdata(this->soa_->getType(),
+                                            this->soa_->getClass(),
+                                            "ns1.example.org. "
+                                            "admin.example.org. "
+                                            "1235 3600 1800 2419200 7200"));
+    this->updater_->addRRset(*this->soa_);
+    this->updater_->addRRset(*nsec3_rrset);
+    this->updater_->commit();
+    std::vector<JournalEntry> expected;
+    expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234,
+                                    DatabaseAccessor::DIFF_DELETE,
+                                    "example.org.", "SOA", "3600",
+                                    "ns1.example.org. admin.example.org. "
+                                    "1234 3600 1800 2419200 7200"));
+    expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1234,
+                                    DatabaseAccessor::DIFF_DELETE,
+                                    string(nsec3_hash) + ".example.org.",
+                                    "NSEC3", "3600", nsec3_rdata));
+    expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1235,
+                                    DatabaseAccessor::DIFF_ADD,
+                                    "example.org.", "SOA", "3600",
+                                    "ns1.example.org. admin.example.org. "
+                                    "1235 3600 1800 2419200 7200"));
+    expected.push_back(JournalEntry(WRITABLE_ZONE_ID, 1235,
+                                    DatabaseAccessor::DIFF_ADD,
+                                    string(nsec3_hash) + ".example.org.",
+                                    "NSEC3", "3600", nsec3_rdata));
+    this->checkJournal(expected);
+}
+
 /*
  * Push multiple delete-add sequences. Checks it is allowed and all is
  * saved.
@@ -3337,10 +3862,10 @@ TYPED_TEST(DatabaseClientTest, journalReader) {
     ASSERT_TRUE(jnl_reader);
     ConstRRsetPtr rrset = jnl_reader->getNextDiff();
     ASSERT_TRUE(rrset);
-    isc::testutils::rrsetCheck(this->soa_, rrset);
+    rrsetCheck(this->soa_, rrset);
     rrset = jnl_reader->getNextDiff();
     ASSERT_TRUE(rrset);
-    isc::testutils::rrsetCheck(soa_end, rrset);
+    rrsetCheck(soa_end, rrset);
     rrset = jnl_reader->getNextDiff();
     ASSERT_FALSE(rrset);
 
@@ -3384,7 +3909,7 @@ TYPED_TEST(DatabaseClientTest, readLargeJournal) {
     ConstRRsetPtr actual;
     int i = 0;
     while ((actual = jnl_reader->getNextDiff()) != NULL) {
-        isc::testutils::rrsetCheck(expected.at(i++), actual);
+        rrsetCheck(expected.at(i++), actual);
     }
     EXPECT_EQ(expected.size(), i); // we should have eaten all expected data
 }
@@ -3449,4 +3974,25 @@ TEST_F(MockDatabaseClientTest, journalWithBadData) {
                  second->getNextDiff(), DataSourceError);
 }
 
+/// Let us test a little bit of NSEC3.
+TEST_F(MockDatabaseClientTest, findNSEC3) {
+    // Set up the faked hash calculator.
+    setNSEC3HashCreator(&test_nsec3_hash_creator_);
+
+    DataSourceClient::FindResult
+        zone(this->client_->findZone(Name("example.org")));
+    ASSERT_EQ(result::SUCCESS, zone.code);
+    boost::shared_ptr<DatabaseClient::Finder> finder(
+        dynamic_pointer_cast<DatabaseClient::Finder>(zone.zone_finder));
+
+    // It'll complain if there is no NSEC3PARAM yet
+    EXPECT_THROW(finder->findNSEC3(Name("example.org"), false),
+                 DataSourceError);
+    // And enable NSEC3 in the zone.
+    this->current_accessor_->enableNSEC3();
+
+    // The rest is in the function, it is shared with in-memory tests
+    performNSEC3Test(*finder);
+}
+
 }
diff --git a/src/lib/datasrc/tests/datasrc_unittest.cc b/src/lib/datasrc/tests/datasrc_unittest.cc
index cd1a40c..36bed1d 100644
--- a/src/lib/datasrc/tests/datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/datasrc_unittest.cc
@@ -58,7 +58,7 @@ ConstElementPtr SQLITE_DBFILE_EXAMPLE = Element::fromJSON(
 
 class DataSrcTest : public ::testing::Test {
 protected:
-    DataSrcTest() : obuffer(0), renderer(obuffer), msg(Message::PARSE),
+    DataSrcTest() : msg(Message::PARSE),
                     opcodeval(Opcode::QUERY().getCode()), qid(0)
     {
         DataSrcPtr sql3_source = DataSrcPtr(new Sqlite3DataSrc); 
@@ -76,7 +76,6 @@ protected:
 
     HotCache cache;
     MetaDataSrc meta_source;
-    OutputBuffer obuffer;
     MessageRenderer renderer;
     Message msg;
     const uint16_t opcodeval;
diff --git a/src/lib/datasrc/tests/faked_nsec3.cc b/src/lib/datasrc/tests/faked_nsec3.cc
new file mode 100644
index 0000000..0a1823b
--- /dev/null
+++ b/src/lib/datasrc/tests/faked_nsec3.cc
@@ -0,0 +1,209 @@
+// 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 "faked_nsec3.h"
+
+#include <dns/name.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <map>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::testutils;
+
+namespace isc {
+namespace datasrc {
+namespace test {
+
+// Constant data definitions
+
+const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
+    "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
+const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
+    "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
+const char* const apex_hash = "0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM";
+const char* const apex_hash_lower = "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom";
+const char* const ns1_hash = "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR";
+const char* const w_hash = "01UDEMVP1J2F7EG6JEBPS17VP3N8I58H";
+const char* const xyw_hash = "2vptu5timamqttgl4luu9kg21e0aor3s";
+const char* const zzz_hash = "R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN";
+
+class TestNSEC3HashCreator::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 rdata::generic::NSEC3PARAM&) const {
+        return (true);
+    }
+    virtual bool match(const rdata::generic::NSEC3&) const {
+        return (true);
+    }
+};
+
+NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3PARAM&)
+    const
+{
+    return (new TestNSEC3Hash);
+}
+
+NSEC3Hash* TestNSEC3HashCreator::create(const rdata::generic::NSEC3&) const {
+    return (new TestNSEC3Hash);
+}
+
+void
+findNSEC3Check(bool expected_matched, uint8_t expected_labels,
+               const string& expected_closest,
+               const string& expected_next,
+               const ZoneFinder::FindNSEC3Result& result,
+               bool expected_sig)
+{
+    EXPECT_EQ(expected_matched, result.matched);
+    // Convert to int so the error messages would be more readable:
+    EXPECT_EQ(static_cast<int>(expected_labels),
+              static_cast<int>(result.closest_labels));
+
+    vector<ConstRRsetPtr> actual_rrsets;
+    ASSERT_TRUE(result.closest_proof);
+    actual_rrsets.push_back(result.closest_proof);
+    if (expected_sig) {
+        actual_rrsets.push_back(result.closest_proof->getRRsig());
+    }
+    rrsetsCheck(expected_closest, actual_rrsets.begin(),
+                actual_rrsets.end());
+
+    actual_rrsets.clear();
+    if (expected_next.empty()) {
+        EXPECT_FALSE(result.next_proof);
+    } else {
+        ASSERT_TRUE(result.next_proof);
+        actual_rrsets.push_back(result.next_proof);
+        if (expected_sig) {
+            actual_rrsets.push_back(result.next_proof->getRRsig());
+        }
+        rrsetsCheck(expected_next, actual_rrsets.begin(),
+                    actual_rrsets.end());
+    }
+}
+
+void
+performNSEC3Test(ZoneFinder &finder) {
+    // Parameter validation: the query name must be in or below the zone
+    EXPECT_THROW(finder.findNSEC3(Name("example.com"), false), OutOfZone);
+    EXPECT_THROW(finder.findNSEC3(Name("org"), true), OutOfZone);
+
+    Name origin("example.org");
+    const string apex_nsec3_text = string(apex_hash) + ".example.org." +
+        string(nsec3_common);
+    const string ns1_nsec3_text = string(ns1_hash) + ".example.org." +
+        string(nsec3_common);
+    const string w_nsec3_text = string(w_hash) + ".example.org." +
+        string(nsec3_common);
+    const string zzz_nsec3_text = string(zzz_hash) + ".example.org." +
+        string(nsec3_common);
+
+    // Apex name.  It should have a matching NSEC3.
+    {
+        SCOPED_TRACE("apex, non recursive mode");
+        findNSEC3Check(true, origin.getLabelCount(), apex_nsec3_text, "",
+                       finder.findNSEC3(origin, false));
+    }
+
+    // Recursive mode doesn't change the result in this case.
+    {
+        SCOPED_TRACE("apex, recursive mode");
+        findNSEC3Check(true, origin.getLabelCount(), apex_nsec3_text, "",
+                       finder.findNSEC3(origin, true));
+    }
+
+    // Non existent name (in the NSEC3 namespace -- the findNSEC3 does
+    // not look into the normal data).  Disabling recursion, a covering
+    // NSEC3 should be returned.
+    const Name www_name("www.example.org");
+    {
+        SCOPED_TRACE("non existent name, non recursive mode");
+        findNSEC3Check(false, www_name.getLabelCount(), apex_nsec3_text, "",
+                       finder.findNSEC3(www_name, false));
+    }
+
+    // Non existent name.  The closest provable encloser is the apex,
+    // and next closer is the query name itself (which NSEC3 for ns1
+    // covers)
+    // H(ns1) = 2T... < H(xxx) = Q0... < H(zzz) = R5...
+    {
+        SCOPED_TRACE("non existent name, recursive mode");
+        findNSEC3Check(true, origin.getLabelCount(), apex_nsec3_text,
+                       ns1_nsec3_text,
+                       finder.findNSEC3(Name("xxx.example.org"), true));
+    }
+
+    // Similar to the previous case, but next closer name is different
+    // from the query name.  The closet encloser is w.example.org, and
+    // next closer is y.w.example.org.
+    // H(ns1) = 2T.. < H(y.w) = K8.. < H(zzz) = R5
+    {
+        SCOPED_TRACE("non existent name, non qname next closer");
+        findNSEC3Check(true, Name("w.example.org").getLabelCount(),
+                       w_nsec3_text, ns1_nsec3_text,
+                       finder.findNSEC3(Name("x.y.w.example.org"),
+                                         true));
+    }
+
+    // In the rest of test we check hash comparison for wrap around cases.
+    {
+        SCOPED_TRACE("very small hash");
+        const Name smallest_name("smallest.example.org");
+        findNSEC3Check(false, smallest_name.getLabelCount(),
+                       zzz_nsec3_text, "",
+                       finder.findNSEC3(smallest_name, false));
+    }
+    {
+        SCOPED_TRACE("very large hash");
+        const Name largest_name("largest.example.org");
+        findNSEC3Check(false, largest_name.getLabelCount(),
+                       zzz_nsec3_text, "",
+                       finder.findNSEC3(largest_name, false));
+    }
+}
+
+}
+}
+}
diff --git a/src/lib/datasrc/tests/faked_nsec3.h b/src/lib/datasrc/tests/faked_nsec3.h
new file mode 100644
index 0000000..10d9444
--- /dev/null
+++ b/src/lib/datasrc/tests/faked_nsec3.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef FAKED_NSEC3_H
+#define FAKED_NSEC3_H
+
+#include <datasrc/zone.h>
+
+#include <dns/nsec3hash.h>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace datasrc {
+namespace test {
+
+//
+// (Faked) NSEC3 hash data.  Arbitrarily borrowed from RFC515 examples.
+//
+// Commonly used NSEC3 suffix.  It's incorrect to use it for all NSEC3s, but
+// doesn't matter for the purpose of our tests.
+extern const char* const nsec3_common;
+// Likewise, common RRSIG suffix for NSEC3s.
+extern const char* const nsec3_rrsig_common;
+
+// Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
+// object.
+//
+// For apex (example.org)
+extern const char* const apex_hash;
+extern const char* const apex_hash_lower;
+// For ns1.example.org
+extern const char* const ns1_hash;
+// For w.example.org
+extern const char* const w_hash;
+// For x.y.w.example.org (lower-cased)
+extern const char* const xyw_hash;
+// For zzz.example.org.
+extern const char* const zzz_hash;
+
+// A simple faked NSEC3 hash calculator with a dedicated creator for it.
+//
+// This is used in some NSEC3-related tests below.
+class TestNSEC3HashCreator : public isc::dns::NSEC3HashCreator {
+private:
+    class TestNSEC3Hash;
+public:
+    virtual isc::dns::NSEC3Hash* create(const
+                                        isc::dns::rdata::generic::NSEC3PARAM&)
+        const;
+    virtual isc::dns::NSEC3Hash* create(const isc::dns::rdata::generic::NSEC3&)
+        const;
+};
+
+// Check the result against expected values. It directly calls EXPECT_ macros
+void
+findNSEC3Check(bool expected_matched, uint8_t expected_labels,
+               const std::string& expected_closest,
+               const std::string& expected_next,
+               const isc::datasrc::ZoneFinder::FindNSEC3Result& result,
+               bool expected_sig = false);
+
+// Perform the shared part of NSEC3 test (shared between in-memory and database
+// tests).
+void
+performNSEC3Test(ZoneFinder &finder);
+
+}
+}
+}
+
+#endif  // FAKED_NSEC3_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/tests/memory_datasrc_unittest.cc b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
index abf6090..07d1fb9 100644
--- a/src/lib/datasrc/tests/memory_datasrc_unittest.cc
+++ b/src/lib/datasrc/tests/memory_datasrc_unittest.cc
@@ -12,11 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <sstream>
-#include <vector>
-
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
+#include "faked_nsec3.h"
 
 #include <exceptions/exceptions.h>
 
@@ -30,19 +26,31 @@
 #include <dns/rrttl.h>
 #include <dns/masterload.h>
 
+#include <datasrc/client.h>
 #include <datasrc/memory_datasrc.h>
 #include <datasrc/data_source.h>
 #include <datasrc/iterator.h>
 
+#include "test_client.h"
+
 #include <testutils/dnsmessage_test.h>
 
 #include <gtest/gtest.h>
 
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <sstream>
+#include <vector>
+
 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;
 
 namespace {
 // Commonly used result codes (Who should write the prefix all the time)
@@ -172,14 +180,23 @@ TEST_F(InMemoryClientTest, iterator) {
     EXPECT_EQ(result::SUCCESS, zone->add(aRRsetA));
     EXPECT_EQ(result::SUCCESS, zone->add(aRRsetAAAA));
     EXPECT_EQ(result::SUCCESS, zone->add(subRRsetA));
-    // Check it with full zone, one by one.
-    // It should be in ascending order in case of InMemory data source
-    // (isn't guaranteed in general)
+
+    // Check it with full zone.
+    vector<ConstRRsetPtr> expected_rrsets;
+    expected_rrsets.push_back(aRRsetA);
+    expected_rrsets.push_back(aRRsetAAAA);
+    expected_rrsets.push_back(subRRsetA);
+
     iterator = memory_client.getIterator(Name("a"));
-    EXPECT_EQ(aRRsetA, iterator->getNextRRset());
-    EXPECT_EQ(aRRsetAAAA, iterator->getNextRRset());
-    EXPECT_EQ(subRRsetA, iterator->getNextRRset());
-    EXPECT_EQ(ConstRRsetPtr(), iterator->getNextRRset());
+    vector<ConstRRsetPtr> actual_rrsets;
+    ConstRRsetPtr actual;
+    while ((actual = iterator->getNextRRset()) != NULL) {
+        actual_rrsets.push_back(actual);
+    }
+
+    rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
+                actual_rrsets.begin(), actual_rrsets.end());
+
 }
 
 TEST_F(InMemoryClientTest, iterator_separate_rrs) {
@@ -276,17 +293,6 @@ setRRset(RRsetPtr rrset, vector<RRsetPtr*>::iterator& it) {
     ++it;
 }
 
-ConstRRsetPtr
-textToRRset(const string& text_rrset, const RRClass& rrclass = RRClass::IN()) {
-    stringstream ss(text_rrset);
-    RRsetPtr rrset;
-    vector<RRsetPtr*> rrsets;
-    rrsets.push_back(&rrset);
-    masterLoad(ss, Name::ROOT_NAME(), rrclass,
-               boost::bind(setRRset, _1, rrsets.begin()));
-    return (rrset);
-}
-
 // Some faked NSEC3 hash values commonly used in tests and the faked NSEC3Hash
 // object.
 //
@@ -389,6 +395,8 @@ public:
         // 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_},
@@ -433,6 +441,8 @@ public:
             {"0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM.example.org. 300 IN "
              "NSEC3 1 1 12 aabbccdd 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG",
              &rr_nsec3_},
+            {"example.org. 300 IN NSEC cname.example.org. A NS NSEC",
+             &rr_nsec_},
             {NULL, NULL}
         };
 
@@ -497,6 +507,7 @@ public:
     RRsetPtr rr_not_wild_;
     RRsetPtr rr_not_wild_another_;
     RRsetPtr rr_nsec3_;
+    RRsetPtr rr_nsec_;
 
     // A faked NSEC3 hash calculator for convenience.
     // Tests that need to use the faked hashed values should call
@@ -536,34 +547,43 @@ public:
                   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({
-                ZoneFinder::FindResult find_result(zone_finder->find(
-                                                       name, rrtype, options));
+                ZoneFinderContextPtr find_result(zone_finder->find(
+                                                     name, rrtype, options));
                 // Check it returns correct answers
-                EXPECT_EQ(result, find_result.code);
+                EXPECT_EQ(result, find_result->code);
                 EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-                          find_result.isWildcard());
+                          find_result->isWildcard());
                 EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
-                          != 0, find_result.isNSECSigned());
+                          != 0, find_result->isNSECSigned());
                 EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
-                          != 0, find_result.isNSEC3Signed());
+                          != 0, find_result->isNSEC3Signed());
                 if (check_answer) {
                     if (!answer) {
-                        ASSERT_FALSE(find_result.rrset);
+                        ASSERT_FALSE(find_result->rrset);
                     } else {
-                        ASSERT_TRUE(find_result.rrset);
-                        rrsetCheck(answer, find_result.rrset);
+                        ASSERT_TRUE(find_result->rrset);
+                        rrsetCheck(answer, find_result->rrset);
+                        if (answer_sig) {
+                            ASSERT_TRUE(find_result->rrset->getRRsig());
+                            rrsetCheck(answer_sig,
+                                       find_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) <<
+                    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.
@@ -574,7 +594,23 @@ public:
                     for (; !expectedIt->isLast(); expectedIt->next()) {
                         wildanswer->addRdata(expectedIt->getCurrent());
                     }
-                    rrsetCheck(wildanswer, find_result.rrset);
+                    rrsetCheck(wildanswer, find_result->rrset);
+
+                    // Same for the RRSIG, if any.
+                    if (answer_sig) {
+                        ASSERT_TRUE(find_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, find_result->rrset->getRRsig());
+                    }
                 }
             });
     }
@@ -594,21 +630,21 @@ public:
             finder = &zone_finder_;
         }
         std::vector<ConstRRsetPtr> target;
-        ZoneFinder::FindResult find_result(finder->findAll(name, target,
-                                                           options));
-        EXPECT_EQ(result, find_result.code);
+        ZoneFinderContextPtr find_result(finder->findAll(name, target,
+                                                         options));
+        EXPECT_EQ(result, find_result->code);
         if (!rrset_result) {
-            EXPECT_FALSE(find_result.rrset);
+            EXPECT_FALSE(find_result->rrset);
         } else {
-            ASSERT_TRUE(find_result.rrset);
-            rrsetCheck(rrset_result, find_result.rrset);
+            ASSERT_TRUE(find_result->rrset);
+            rrsetCheck(rrset_result, find_result->rrset);
         }
         EXPECT_EQ((expected_flags & ZoneFinder::RESULT_WILDCARD) != 0,
-                  find_result.isWildcard());
+                  find_result->isWildcard());
         EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED)
-                  != 0, find_result.isNSECSigned());
+                  != 0, find_result->isNSECSigned());
         EXPECT_EQ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED)
-                  != 0, find_result.isNSEC3Signed());
+                  != 0, find_result->isNSEC3Signed());
         rrsetsCheck(expected_rrsets.begin(), expected_rrsets.end(),
                     target.begin(), target.end());
     }
@@ -640,7 +676,7 @@ TEST_F(InMemoryZoneFinderTest, constructor) {
  */
 TEST_F(InMemoryZoneFinderTest, add) {
     // This one does not belong to this zone
-    EXPECT_THROW(zone_finder_.add(rr_out_), InMemoryZoneFinder::OutOfZone);
+    EXPECT_THROW(zone_finder_.add(rr_out_), OutOfZone);
     // Test null pointer
     EXPECT_THROW(zone_finder_.add(ConstRRsetPtr()),
                  InMemoryZoneFinder::NullRRset);
@@ -867,8 +903,9 @@ TEST_F(InMemoryZoneFinderTest, findAny) {
     findAllTest(origin_, ZoneFinder::SUCCESS, expected_sets);
 
     // out zone name
-    findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
-                vector<ConstRRsetPtr>());
+    EXPECT_THROW(findAllTest(Name("example.com"), ZoneFinder::NXDOMAIN,
+                             vector<ConstRRsetPtr>()),
+                 OutOfZone);
 
     expected_sets.clear();
     expected_sets.push_back(rr_child_glue_);
@@ -950,6 +987,9 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags) {
     if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
         EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
     }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+    }
 
     // These two should be successful
     findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, true, rr_ns_);
@@ -965,8 +1005,8 @@ InMemoryZoneFinderTest::findCheck(ZoneFinder::FindResultFlags expected_flags) {
     // These domains don't exist (and one is out of the zone)
     findTest(Name("nothere.example.org"), RRType::A(), ZoneFinder::NXDOMAIN,
              true, ConstRRsetPtr(), expected_flags);
-    findTest(Name("example.net"), RRType::A(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), expected_flags);
+    EXPECT_THROW(zone_finder_.find(Name("example.net"), RRType::A()),
+                 OutOfZone);
 }
 
 TEST_F(InMemoryZoneFinderTest, find) {
@@ -977,6 +1017,10 @@ TEST_F(InMemoryZoneFinderTest, findNSEC3Signed) {
     findCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
 }
 
+TEST_F(InMemoryZoneFinderTest, findNSECSigned) {
+    findCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
 void
 InMemoryZoneFinderTest::emptyNodeCheck(
     ZoneFinder::FindResultFlags expected_flags)
@@ -1005,6 +1049,9 @@ InMemoryZoneFinderTest::emptyNodeCheck(
     if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
         EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
     }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+    }
 
     // empty node matching, easy case: the node for 'baz' exists with
     // no data.
@@ -1021,8 +1068,7 @@ InMemoryZoneFinderTest::emptyNodeCheck(
     // 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.
-    findTest(Name("org"), RRType::A(), ZoneFinder::NXDOMAIN, true,
-             ConstRRsetPtr(), expected_flags);
+    EXPECT_THROW(zone_finder_.find(Name("org"), RRType::A()), OutOfZone);
 }
 
 TEST_F(InMemoryZoneFinderTest, emptyNode) {
@@ -1033,6 +1079,10 @@ TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC3) {
     emptyNodeCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
 }
 
+TEST_F(InMemoryZoneFinderTest, emptyNodeNSEC) {
+    emptyNodeCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
 TEST_F(InMemoryZoneFinderTest, load) {
     // Put some data inside the zone
     EXPECT_NO_THROW(EXPECT_EQ(result::SUCCESS, zone_finder_.add(rr_ns_)));
@@ -1061,7 +1111,70 @@ TEST_F(InMemoryZoneFinderTest, load) {
 
     // Try loading zone that is wrong in a different way
     EXPECT_THROW(zone_finder_.load(TEST_DATA_DIR "/duplicate_rrset.zone"),
-        MasterLoadError);
+                 MasterLoadError);
+}
+
+TEST_F(InMemoryZoneFinderTest, loadFromIterator) {
+    // The initial test set doesn't have SOA at the apex.
+    findTest(origin_, RRType::SOA(), ZoneFinder::NXRRSET, false,
+             ConstRRsetPtr());
+
+    // The content of the new version of zone to be first installed to
+    // the SQLite3 data source, then to in-memory via SQLite3.  The data are
+    // chosen to cover major check points of the implementation:
+    // - the previously non-existent record is added (SOA)
+    // - An RRSIG is given from the iterator before the RRset it covers
+    //   (RRSIG for SOA, because they are sorted by name then rrtype as text)
+    // - An RRset containing multiple RRs (ns1/A)
+    // - RRSIGs for different owner names
+    stringstream ss;
+    const char* const soa_txt = "example.org. 300 IN SOA . . 0 0 0 0 0\n";
+    const char* const soa_sig_txt = "example.org. 300 IN RRSIG SOA 5 3 300 "
+        "20000101000000 20000201000000 12345 example.org. FAKEFAKE\n";
+    const char* const a_txt =
+        "ns1.example.org. 300 IN A 192.0.2.1\n"
+        "ns1.example.org. 300 IN A 192.0.2.2\n";
+    const char* const a_sig_txt = "ns1.example.org. 300 IN RRSIG A 5 3 300 "
+        "20000101000000 20000201000000 12345 example.org. FAKEFAKE\n";
+    ss << soa_txt << soa_sig_txt << a_txt << a_sig_txt;
+    shared_ptr<DataSourceClient> db_client = unittest::createSQLite3Client(
+        class_, origin_, TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied", ss);
+    zone_finder_.load(*db_client->getIterator(origin_));
+
+    // The new content should be visible, including the previously-nonexistent
+    // SOA.
+    RRsetPtr expected_answer = textToRRset(soa_txt, RRClass::IN(), origin_);
+    expected_answer->addRRsig(textToRRset(soa_sig_txt));
+    findTest(origin_, RRType::SOA(), ZoneFinder::SUCCESS, true,
+             expected_answer);
+
+    expected_answer = textToRRset(a_txt);
+    expected_answer->addRRsig(textToRRset(a_sig_txt));
+    findTest(Name("ns1.example.org"), RRType::A(), ZoneFinder::SUCCESS, true,
+             expected_answer);
+
+    // File name should be (re)set to empty.
+    EXPECT_TRUE(zone_finder_.getFileName().empty());
+
+    // 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(zone_finder_.load(*db_client->getIterator(origin_, true)),
+                 MasterLoadError);
+
+    // Load the zone from a file that contains more realistic data (borrowed
+    // from a different test).  There's nothing special in this case for the
+    // purpose of this test, so it should just succeed.
+    db_client = unittest::createSQLite3Client(
+        class_, origin_, TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied",
+        TEST_DATA_DIR "/contexttest.zone");
+    zone_finder_.load(*db_client->getIterator(origin_));
+
+    // just checking a couple of RRs in the new version of zone.
+    findTest(Name("mx1.example.org"), RRType::A(), ZoneFinder::SUCCESS, true,
+             textToRRset("mx1.example.org. 3600 IN A 192.0.2.10"));
+    findTest(Name("ns1.example.org"), RRType::AAAA(), ZoneFinder::SUCCESS,
+             true, textToRRset("ns1.example.org. 3600 IN AAAA 2001:db8::1"));
 }
 
 /*
@@ -1079,6 +1192,24 @@ InMemoryZoneFinderTest::wildcardCheck(
      *                 |
      *                 *
      */
+
+    // If the zone is "signed" (detecting it by the NSEC/NSEC3 signed flags),
+    // add RRSIGs to the records.
+    ZoneFinder::FindOptions find_options = ZoneFinder::FIND_DEFAULT;
+    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)));
+    }
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_wild_));
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_cnamewild_));
     // If the zone is expected to be "signed" with NSEC3, add an NSEC3.
@@ -1086,20 +1217,24 @@ InMemoryZoneFinderTest::wildcardCheck(
     if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
         EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
     }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(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");
         findTest(Name("wild.example.org"), RRType::A(), ZoneFinder::NXRRSET,
-                 true, ConstRRsetPtr(), expected_flags);
+                 true, ConstRRsetPtr(), expected_flags, NULL, find_options);
     }
 
     // Search the original name of wildcard
     {
         SCOPED_TRACE("Search directly at *");
         findTest(Name("*.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
-                 true, rr_wild_);
+                 true, rr_wild_, ZoneFinder::RESULT_DEFAULT, NULL,
+                 find_options);
     }
     // Search "created" name.
     {
@@ -1107,11 +1242,12 @@ InMemoryZoneFinderTest::wildcardCheck(
         findTest(Name("a.wild.example.org"), RRType::A(), ZoneFinder::SUCCESS,
                  false, rr_wild_,
                  ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                 ZoneFinder::FIND_DEFAULT, true);
+                 find_options, true);
         // Wildcard match, but no data
         findTest(Name("a.wild.example.org"), RRType::AAAA(),
                  ZoneFinder::NXRRSET, true, ConstRRsetPtr(),
-                 ZoneFinder::RESULT_WILDCARD | expected_flags);
+                 ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
+                 find_options);
     }
 
     // Search name that has CNAME.
@@ -1120,7 +1256,7 @@ InMemoryZoneFinderTest::wildcardCheck(
         findTest(Name("a.cnamewild.example.org"), RRType::A(),
                  ZoneFinder::CNAME, false, rr_cnamewild_,
                  ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                 ZoneFinder::FIND_DEFAULT, true);
+                 find_options, true);
     }
 
     // Search another created name, this time little bit lower
@@ -1129,14 +1265,15 @@ InMemoryZoneFinderTest::wildcardCheck(
         findTest(Name("a.b.wild.example.org"), RRType::A(),
                  ZoneFinder::SUCCESS, false, rr_wild_,
                  ZoneFinder::RESULT_WILDCARD | expected_flags, NULL,
-                 ZoneFinder::FIND_DEFAULT, true);
+                 find_options, true);
     }
 
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_under_wild_));
     {
         SCOPED_TRACE("Search under non-wildcard");
         findTest(Name("bar.foo.wild.example.org"), RRType::A(),
-                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags);
+                 ZoneFinder::NXDOMAIN, true, ConstRRsetPtr(), expected_flags,
+                 NULL, find_options);
     }
 }
 
@@ -1150,6 +1287,11 @@ TEST_F(InMemoryZoneFinderTest, wildcardNSEC3) {
     wildcardCheck(ZoneFinder::RESULT_NSEC3_SIGNED);
 }
 
+TEST_F(InMemoryZoneFinderTest, wildcardNSEC) {
+    // Similar to the previous one, but the zone signed with NSEC
+    wildcardCheck(ZoneFinder::RESULT_NSEC_SIGNED);
+}
+
 /*
  * Test that we don't match a wildcard if we get under delegation.
  * By 4.3.3 of RFC1034:
@@ -1184,6 +1326,9 @@ InMemoryZoneFinderTest::anyWildcardCheck(
     if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
         EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
     }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+    }
 
     vector<ConstRRsetPtr> expected_sets;
 
@@ -1218,6 +1363,10 @@ 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
@@ -1234,6 +1383,9 @@ InMemoryZoneFinderTest::emptyWildcardCheck(
     if ((expected_flags & ZoneFinder::RESULT_NSEC3_SIGNED) != 0) {
         EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec3_));
     }
+    if ((expected_flags & ZoneFinder::RESULT_NSEC_SIGNED) != 0) {
+        EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nsec_));
+    }
 
     {
         SCOPED_TRACE("Asking for the original record under wildcard");
@@ -1278,6 +1430,10 @@ 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) {
     EXPECT_EQ(SUCCESS, zone_finder_.add(rr_nested_emptywild_));
@@ -1459,14 +1615,12 @@ TEST_F(InMemoryZoneFinderTest, swap) {
     EXPECT_EQ(RRClass::CH(), finder1.getClass());
     EXPECT_EQ(RRClass::IN(), finder2.getClass());
     // make sure the zone data is swapped, too
-    findTest(origin_, RRType::NS(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder1);
+    EXPECT_THROW(finder1.find(origin_, RRType::NS()), OutOfZone);
     findTest(other_origin, RRType::TXT(), ZoneFinder::SUCCESS, false,
              ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder1);
     findTest(origin_, RRType::NS(), ZoneFinder::SUCCESS, false,
              ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder2);
-    findTest(other_origin, RRType::TXT(), ZoneFinder::NXDOMAIN, false,
-             ConstRRsetPtr(), ZoneFinder::RESULT_DEFAULT, &finder2);
+    EXPECT_THROW(finder2.find(other_origin, RRType::TXT()), OutOfZone);
 }
 
 TEST_F(InMemoryZoneFinderTest, getFileName) {
@@ -1499,37 +1653,35 @@ TEST_F(InMemoryZoneFinderTest, addRRsig) {
     // that covers the first RRset
     zone_finder_.add(rr_a_);
     zone_finder_.add(textToRRset(rrsig_a_txt));
-    ZoneFinder::FindResult result = zone_finder_.find(origin_, RRType::A(),
-                                                      ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result.code);
-    ASSERT_TRUE(result.rrset);
-    ASSERT_TRUE(result.rrset->getRRsig());
-    actual_rrsets_.push_back(result.rrset->getRRsig());
+    ZoneFinderContextPtr result = zone_finder_.find(origin_, RRType::A(),
+                                                    ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    ASSERT_TRUE(result->rrset);
+    ASSERT_TRUE(result->rrset->getRRsig());
+    actual_rrsets_.push_back(result->rrset->getRRsig());
     rrsetsCheck(rrsig_a_txt, actual_rrsets_.begin(), actual_rrsets_.end());
 
     // Confirm a separate RRISG for a different type can be added
     actual_rrsets_.clear();
     zone_finder_.add(rr_ns_);
     zone_finder_.add(textToRRset(rrsig_ns_txt));
-    ZoneFinder::FindResult result2 =
-        zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result2.code);
-    ASSERT_TRUE(result2.rrset);
-    ASSERT_TRUE(result2.rrset->getRRsig());
-    actual_rrsets_.push_back(result2.rrset->getRRsig());
+    result = zone_finder_.find(origin_, RRType::NS(), ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    ASSERT_TRUE(result->rrset);
+    ASSERT_TRUE(result->rrset->getRRsig());
+    actual_rrsets_.push_back(result->rrset->getRRsig());
     rrsetsCheck(rrsig_ns_txt, actual_rrsets_.begin(), actual_rrsets_.end());
 
     // Check a case with multiple RRSIGs
     actual_rrsets_.clear();
     zone_finder_.add(rr_ns_aaaa_);
     zone_finder_.add(textToRRset(rrsig_aaaa_txt));
-    ZoneFinder::FindResult result3 =
-        zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
-                          ZoneFinder::FIND_DNSSEC);
-    EXPECT_EQ(ZoneFinder::SUCCESS, result3.code);
-    ASSERT_TRUE(result3.rrset);
-    ASSERT_TRUE(result3.rrset->getRRsig());
-    actual_rrsets_.push_back(result3.rrset->getRRsig());
+    result = zone_finder_.find(Name("ns.example.org"), RRType::AAAA(),
+                               ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, result->code);
+    ASSERT_TRUE(result->rrset);
+    ASSERT_TRUE(result->rrset->getRRsig());
+    actual_rrsets_.push_back(result->rrset->getRRsig());
     rrsetsCheck(rrsig_aaaa_txt, actual_rrsets_.begin(), actual_rrsets_.end());
 }
 
@@ -1585,52 +1737,6 @@ TEST_F(InMemoryZoneFinderTest, addbadRRsig) {
                  InMemoryZoneFinder::AddError);
 }
 
-//
-// (Faked) NSEC3 hash data.  Arbitrarily borrowed from RFC515 examples.
-//
-// Commonly used NSEC3 suffix.  It's incorrect to use it for all NSEC3s, but
-// doesn't matter for the purpose of our tests.
-const char* const nsec3_common = " 300 IN NSEC3 1 1 12 aabbccdd "
-    "2T7B4G4VSA5SMI47K61MV5BV1A22BOJR A RRSIG";
-// Likewise, common RRSIG suffix for NSEC3s.
-const char* const nsec3_rrsig_common = " 300 IN RRSIG NSEC3 5 3 3600 "
-    "20000101000000 20000201000000 12345 example.org. FAKEFAKEFAKE";
-
-void
-findNSEC3Check(bool expected_matched, uint8_t expected_labels,
-               const string& expected_closest,
-               const string& expected_next,
-               const ZoneFinder::FindNSEC3Result& result,
-               bool expected_sig = false)
-{
-    EXPECT_EQ(expected_matched, result.matched);
-    // Convert to int so the error messages would be more readable:
-    EXPECT_EQ(static_cast<int>(expected_labels),
-              static_cast<int>(result.closest_labels));
-
-    vector<ConstRRsetPtr> actual_rrsets;
-    ASSERT_TRUE(result.closest_proof);
-    actual_rrsets.push_back(result.closest_proof);
-    if (expected_sig) {
-        actual_rrsets.push_back(result.closest_proof->getRRsig());
-    }
-    rrsetsCheck(expected_closest, actual_rrsets.begin(),
-                actual_rrsets.end());
-
-    actual_rrsets.clear();
-    if (expected_next.empty()) {
-        EXPECT_FALSE(result.next_proof);
-    } else {
-        ASSERT_TRUE(result.next_proof);
-        actual_rrsets.push_back(result.next_proof);
-        if (expected_sig) {
-            actual_rrsets.push_back(result.next_proof->getRRsig());
-        }
-        rrsetsCheck(expected_next, actual_rrsets.begin(),
-                    actual_rrsets.end());
-    }
-}
-
 TEST_F(InMemoryZoneFinderTest, addNSEC3) {
     // Set up the faked hash calculator.
     setNSEC3HashCreator(&nsec3_hash_creator_);
@@ -1641,7 +1747,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
     EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nsec3_text)));
     EXPECT_EQ(ZoneFinder::NXDOMAIN,
               zone_finder_.find(Name(string(apex_hash) + ".example.org"),
-                                RRType::NSEC3()).code);
+                                RRType::NSEC3())->code);
     // Dedicated NSEC3 find should be able to find it.
     findNSEC3Check(true, origin_.getLabelCount(), nsec3_text, "",
                    zone_finder_.findNSEC3(Name("example.org"), false));
@@ -1661,7 +1767,7 @@ TEST_F(InMemoryZoneFinderTest, addNSEC3) {
     EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(nonsec3_text)));
     EXPECT_EQ(ZoneFinder::SUCCESS,
               zone_finder_.find(Name(string(apex_hash) + ".example.org"),
-                                RRType::A()).code);
+                                RRType::A())->code);
 }
 
 TEST_F(InMemoryZoneFinderTest, addNSEC3Lower) {
@@ -1919,73 +2025,7 @@ TEST_F(InMemoryZoneFinderTest, findNSEC3) {
         string(nsec3_common);
     EXPECT_EQ(result::SUCCESS, zone_finder_.add(textToRRset(zzz_nsec3_text)));
 
-    // Parameter validation: the query name must be in or below the zone
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("example.com"), false),
-                 isc::InvalidParameter);
-    EXPECT_THROW(zone_finder_.findNSEC3(Name("org"), true),
-                 isc::InvalidParameter);
-
-    // Apex name.  It should have a matching NSEC3.
-    {
-        SCOPED_TRACE("apex, non recursive mode");
-        findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "",
-                       zone_finder_.findNSEC3(origin_, false));
-    }
-
-    // Recursive mode doesn't change the result in this case.
-    {
-        SCOPED_TRACE("apex, recursive mode");
-        findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text, "",
-                       zone_finder_.findNSEC3(origin_, true));
-    }
-
-    // Non existent name.  Disabling recursion, a covering NSEC3 should be
-    // returned.
-    const Name www_name("www.example.org");
-    {
-        SCOPED_TRACE("non existent name, non recursive mode");
-        findNSEC3Check(false, www_name.getLabelCount(), apex_nsec3_text, "",
-                       zone_finder_.findNSEC3(www_name, false));
-    }
-
-    // Non existent name.  The closest provable encloser is the apex,
-    // and next closer is the query name itself (which NSEC3 for ns1
-    // covers)
-    // H(ns1) = 2T... < H(xxx) = Q0... < H(zzz) = R5...
-    {
-        SCOPED_TRACE("non existent name, recursive mode");
-        findNSEC3Check(true, origin_.getLabelCount(), apex_nsec3_text,
-                       ns1_nsec3_text,
-                       zone_finder_.findNSEC3(Name("xxx.example.org"), true));
-    }
-
-    // Similar to the previous case, but next closer name is different
-    // from the query name.  The closet encloser is w.example.org, and
-    // next closer is y.w.example.org.
-    // H(ns1) = 2T.. < H(y.w) = K8.. < H(zzz) = R5
-    {
-        SCOPED_TRACE("non existent name, non qname next closer");
-        findNSEC3Check(true, Name("w.example.org").getLabelCount(),
-                       w_nsec3_text, ns1_nsec3_text,
-                       zone_finder_.findNSEC3(Name("x.y.w.example.org"),
-                                              true));
-    }
-
-    // In the rest of test we check hash comparison for wrap around cases.
-    {
-        SCOPED_TRACE("very small hash");
-        const Name smallest_name("smallest.example.org");
-        findNSEC3Check(false, smallest_name.getLabelCount(),
-                       zzz_nsec3_text, "",
-                       zone_finder_.findNSEC3(smallest_name, false));
-    }
-    {
-        SCOPED_TRACE("very large hash");
-        const Name largest_name("largest.example.org");
-        findNSEC3Check(false, largest_name.getLabelCount(),
-                       zzz_nsec3_text, "",
-                       zone_finder_.findNSEC3(largest_name, false));
-    }
+    performNSEC3Test(zone_finder_);
 }
 
 TEST_F(InMemoryZoneFinderTest, findNSEC3ForBadZone) {
diff --git a/src/lib/datasrc/tests/rbnode_rrset_unittest.cc b/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
new file mode 100644
index 0000000..57e8dbd
--- /dev/null
+++ b/src/lib/datasrc/tests/rbnode_rrset_unittest.cc
@@ -0,0 +1,276 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <dns/rdataclass.h>
+#include <datasrc/rbnode_rrset.h>
+#include <testutils/dnsmessage_test.h>
+
+#include <dns/tests/unittest_util.h>
+
+#include <gtest/gtest.h>
+
+#include <sstream>
+#include <stdexcept>
+
+using isc::UnitTestUtil;
+
+using namespace isc;
+using namespace isc::datasrc;
+using namespace isc::datasrc::internal;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::testutils;
+using namespace isc::util;
+using namespace std;
+
+// These tests are very similar to those for RRset - indeed, this file was
+// created from those tests.  However, the significant difference in behaviour
+// between RRset and RBNodeRRset - that the "set" methods in the latter mostly
+// result in exceptions being thrown - preclude use of full type
+// parameterisation of the tests.
+
+namespace {
+const char* const RRSIG_TXT =
+    "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. "
+    "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz"
+    "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/"
+    "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU"
+    "f49t+sXKPzbipN9g+s1ZPiIyofc=";
+
+class RBNodeRRsetTest : public ::testing::Test {
+protected:
+    RBNodeRRsetTest() :
+        test_name("test.example.com"),
+        test_domain("example.com"),
+        test_nsname("ns.example.com"),
+        rrset_a(ConstRRsetPtr(new RRset(
+                test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+        rrset_a_empty(ConstRRsetPtr(new RRset(
+                      test_name, RRClass::IN(), RRType::A(), RRTTL(3600)))),
+        rrset_ns(ConstRRsetPtr(new RRset(
+                 test_domain, RRClass::IN(), RRType::NS(), RRTTL(86400)))),
+        rrset_ch_txt(ConstRRsetPtr(new RRset(
+                     test_domain, RRClass::CH(), RRType::TXT(), RRTTL(0)))),
+        rrset_siga(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+                   RRTTL(3600)))
+
+    {
+        // Add a couple of Rdata elements to the A RRset.  The easiest way to
+        // do this is to override the "const" restrictions.  As this is a test,
+        // we don't feel too bad about doing so.
+        AbstractRRset* a_rrset =
+            const_cast<AbstractRRset*>(rrset_a.getUnderlyingRRset().get());
+        a_rrset->addRdata(in::A("192.0.2.1"));
+        a_rrset->addRdata(in::A("192.0.2.2"));
+
+        // Create the RRSIG corresponding to the rrset_a record.  The RDATA
+        // won't match the A record it covers, although it is internally
+        // self-consistent.
+        AbstractRRset* sig_rrset =
+            const_cast<AbstractRRset*>(rrset_siga.get());
+        sig_rrset->addRdata(generic::RRSIG(RRSIG_TXT));
+    }
+
+    const Name test_name;
+    const Name test_domain;
+    const Name test_nsname;
+
+    RBNodeRRset rrset_a;
+    RBNodeRRset rrset_a_empty;
+    const RBNodeRRset rrset_ns;
+    const RBNodeRRset rrset_ch_txt;
+
+    ConstRRsetPtr rrset_siga;
+};
+
+TEST_F(RBNodeRRsetTest, getRdataCount) {
+    EXPECT_EQ(0, rrset_a_empty.getRdataCount());
+    EXPECT_EQ(2, rrset_a.getRdataCount());
+}
+
+TEST_F(RBNodeRRsetTest, getName) {
+    EXPECT_EQ(test_name, rrset_a.getName());
+    EXPECT_EQ(test_domain, rrset_ns.getName());
+}
+
+TEST_F(RBNodeRRsetTest, getClass) {
+    EXPECT_EQ(RRClass("IN"), rrset_a.getClass());
+    EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass());
+}
+
+TEST_F(RBNodeRRsetTest, getType) {
+    EXPECT_EQ(RRType("A"), rrset_a.getType());
+    EXPECT_EQ(RRType("NS"), rrset_ns.getType());
+    EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType());
+}
+
+TEST_F(RBNodeRRsetTest, getTTL) {
+    EXPECT_EQ(RRTTL(3600), rrset_a.getTTL());
+    EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL());
+    EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL());
+}
+
+TEST_F(RBNodeRRsetTest, setName) {
+    EXPECT_THROW(rrset_a.setName(test_nsname), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, setTTL) {
+    EXPECT_THROW(rrset_a.setTTL(RRTTL(86400)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, toText) {
+    EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+              "test.example.com. 3600 IN A 192.0.2.2\n",
+              rrset_a.toText());
+
+    // toText() cannot be performed for an empty RRset.
+    EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset);
+}
+
+TEST_F(RBNodeRRsetTest, isSameKind) {
+    RBNodeRRset rrset_p(ConstRRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))));
+    RBNodeRRset rrset_q(ConstRRsetPtr(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(3600))));
+    RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
+    RRset rrset_x(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600));
+    RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600));
+    RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600));
+
+    EXPECT_TRUE(rrset_p.isSameKind(rrset_p));
+    EXPECT_FALSE(rrset_p.isSameKind(rrset_q));
+
+    EXPECT_TRUE(rrset_p.isSameKind(rrset_w));
+    EXPECT_FALSE(rrset_p.isSameKind(rrset_x));
+    EXPECT_FALSE(rrset_p.isSameKind(rrset_y));
+    EXPECT_FALSE(rrset_p.isSameKind(rrset_z));
+}
+
+// Note: although the next two tests are essentially the same and used common
+// test code, they use different test data: the MessageRenderer produces
+// compressed wire data whereas the OutputBuffer does not.
+
+template <typename T>
+void
+performToWireTest(T& dataHolder, const RBNodeRRset& rrset,
+                  const RBNodeRRset& rrset_empty, const char* testdata)
+{
+    rrset.toWire(dataHolder);
+
+    std::vector<unsigned char> wiredata;
+    UnitTestUtil::readWireData(testdata, wiredata);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, dataHolder.getData(),
+                        dataHolder.getLength(), &wiredata[0], wiredata.size());
+
+    // toWire() cannot be performed for an empty RRset.
+    dataHolder.clear();
+    EXPECT_THROW(rrset_empty.toWire(dataHolder), EmptyRRset);
+}
+
+TEST_F(RBNodeRRsetTest, toWireRenderer) {
+    MessageRenderer renderer;
+    performToWireTest(renderer, rrset_a, rrset_a_empty, "rrset_toWire2");
+}
+
+TEST_F(RBNodeRRsetTest, toWireBuffer) {
+    OutputBuffer buffer(0);
+    performToWireTest(buffer, rrset_a, rrset_a_empty, "rrset_toWire1");
+}
+
+TEST_F(RBNodeRRsetTest, addRdata) {
+    EXPECT_THROW(rrset_a.addRdata(in::A("192.0.2.3")), NotImplemented);
+
+    // Check the same goes for trying to add the wrong type of data
+    EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, addRdataPtr) {
+    EXPECT_THROW(rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(),
+                                                    rrset_a_empty.getClass(),
+                                                    "192.0.2.1")),
+                 NotImplemented);
+}
+
+TEST_F(RBNodeRRsetTest, getRDataIterator) {
+    RdataIteratorPtr it = rrset_a.getRdataIterator();
+    for (int i = 0; i < 2; ++i) {
+        ASSERT_FALSE(it->isLast());
+        ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1")));
+
+        it->next();
+        ASSERT_FALSE(it->isLast());
+        ASSERT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2")));
+
+        it->next();
+        ASSERT_TRUE(it->isLast());
+
+        // Should be able repeat the iteration by calling first().
+        it->first();
+    }
+}
+
+// test operator<<.  We simply confirm it appends the result of toText().
+TEST_F(RBNodeRRsetTest, LeftShiftOperator) {
+    ostringstream oss;
+    oss << rrset_a;
+    EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n"
+              "test.example.com. 3600 IN A 192.0.2.2\n", oss.str());
+}
+
+// addRRSIG tests.
+TEST_F(RBNodeRRsetTest, addRRsigConstRdataPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    ConstRdataPtr data = createRdata(rrset_siga->getType(),
+                                     rrset_siga->getClass(), RRSIG_TXT);
+    rrset_a.addRRsig(data);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRdataPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    RdataPtr data = createRdata(rrset_siga->getType(), rrset_siga->getClass(),
+                                RRSIG_TXT);
+    rrset_a.addRRsig(data);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigAbstractRRset) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(*(rrset_siga.get()));
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigConstantRRsetPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(rrset_siga);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, addRRsigRRsetPointer) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    RRsetPtr rrsig(new RRset(test_name, RRClass::IN(), RRType::RRSIG(),
+                   RRTTL(3600)));
+    rrsig->addRdata(generic::RRSIG(RRSIG_TXT));
+    rrset_a.addRRsig(rrsig);
+    rrsetCheck(rrset_siga, rrset_a.getRRsig());
+}
+
+TEST_F(RBNodeRRsetTest, removeRRsig) {
+    EXPECT_FALSE(rrset_a.getRRsig());
+    rrset_a.addRRsig(*(rrset_siga.get()));
+    EXPECT_TRUE(rrset_a.getRRsig());
+    rrset_a.removeRRsig();
+    EXPECT_FALSE(rrset_a.getRRsig());
+}
+
+}   // Anonymous namespace
diff --git a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
index 5122136..718d29b 100644
--- a/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_accessor_unittest.cc
@@ -12,8 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <algorithm>
-#include <vector>
+#include "faked_nsec3.h"
 
 #include <datasrc/sqlite3_accessor.h>
 
@@ -21,14 +20,20 @@
 
 #include <dns/rrclass.h>
 
+#include <sqlite3.h>
+
 #include <gtest/gtest.h>
+
 #include <boost/lexical_cast.hpp>
 #include <boost/scoped_ptr.hpp>
+
+#include <algorithm>
+#include <vector>
 #include <fstream>
-#include <sqlite3.h>
 
 using namespace std;
 using namespace isc::datasrc;
+using namespace isc::datasrc::test;
 using boost::lexical_cast;
 using isc::data::ConstElementPtr;
 using isc::data::Element;
@@ -37,15 +42,22 @@ using isc::dns::Name;
 
 namespace {
 // Some test data
-std::string SQLITE_DBFILE_EXAMPLE = TEST_DATA_DIR "/test.sqlite3";
-std::string SQLITE_DBFILE_EXAMPLE2 = TEST_DATA_DIR "/example2.com.sqlite3";
-std::string SQLITE_DBNAME_EXAMPLE2 = "sqlite3_example2.com.sqlite3";
-std::string SQLITE_DBFILE_EXAMPLE_ROOT = TEST_DATA_DIR "/test-root.sqlite3";
-std::string SQLITE_DBNAME_EXAMPLE_ROOT = "sqlite3_test-root.sqlite3";
-std::string SQLITE_DBFILE_BROKENDB = TEST_DATA_DIR "/brokendb.sqlite3";
-std::string SQLITE_DBFILE_MEMORY = ":memory:";
-std::string SQLITE_DBFILE_EXAMPLE_ORG = TEST_DATA_DIR "/example.org.sqlite3";
-std::string SQLITE_DBFILE_DIFFS = TEST_DATA_DIR "/diffs.sqlite3";
+const char* const SQLITE_DBFILE_EXAMPLE = TEST_DATA_DIR "/test.sqlite3";
+const char* const SQLITE_DBFILE_EXAMPLE2 =
+    TEST_DATA_DIR "/example2.com.sqlite3";
+const char* const SQLITE_DBNAME_EXAMPLE2 = "sqlite3_example2.com.sqlite3";
+const char* const SQLITE_DBFILE_EXAMPLE_ROOT =
+    TEST_DATA_DIR "/test-root.sqlite3";
+const char* const SQLITE_DBNAME_EXAMPLE_ROOT = "sqlite3_test-root.sqlite3";
+const char* const SQLITE_DBFILE_BROKENDB = TEST_DATA_DIR "/brokendb.sqlite3";
+const char* const SQLITE_DBFILE_MEMORY = ":memory:";
+const char* const SQLITE_DBFILE_EXAMPLE_ORG =
+    TEST_DATA_DIR "/example.org.sqlite3";
+const char* const SQLITE_DBFILE_DIFFS = TEST_DATA_DIR "/diffs.sqlite3";
+const char* const SQLITE_DBFILE_NEWSCHEMA = TEST_DATA_DIR "/newschema.sqlite3";
+const char* const SQLITE_DBFILE_OLDSCHEMA = TEST_DATA_DIR "/oldschema.sqlite3";
+const char* const SQLITE_DBFILE_NEW_MINOR_SCHEMA =
+    TEST_DATA_DIR "/new_minor_schema.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,
@@ -74,6 +86,20 @@ TEST(SQLite3Open, brokenDB) {
                  SQLite3Error);
 }
 
+// Different schema versions
+TEST(SQLite3Open, differentSchemaVersions) {
+    // If the major version is different from the current one, it should fail.
+    EXPECT_THROW(SQLite3Accessor(SQLITE_DBFILE_NEWSCHEMA, "IN"),
+                 IncompatibleDbVersion);
+    EXPECT_THROW(SQLite3Accessor(SQLITE_DBFILE_OLDSCHEMA, "IN"),
+                 IncompatibleDbVersion);
+
+    // Difference in the minor version is okay (as of this test written
+    // the current minor version is 0, so we can only test the case with a
+    // higher minor version).
+    EXPECT_NO_THROW(SQLite3Accessor(SQLITE_DBFILE_NEW_MINOR_SCHEMA, "IN"));
+}
+
 // Test we can create the schema on the fly
 TEST(SQLite3Open, memoryDB) {
     EXPECT_NO_THROW(SQLite3Accessor accessor(SQLITE_DBFILE_MEMORY, "IN"));
@@ -173,6 +199,151 @@ TEST_F(SQLite3AccessorTest, iterator) {
     EXPECT_FALSE(context->getNext(data));
 }
 
+// This tests the iterator through the whole zone returns NSEC3 records as
+// well. We test this specifically, as it lives in separate table and needs
+// extra handling.
+TEST_F(SQLite3AccessorTest, nsec3Iterator) {
+    // Get the zone
+    const std::pair<bool, int>
+        zone_info(accessor->getZone("sql2.example.com."));
+    ASSERT_TRUE(zone_info.first);
+
+    // Iterate through it
+    DatabaseAccessor::IteratorContextPtr
+        context(accessor->getAllRecords(zone_info.second));
+
+    // We just pick a random NSEC3 to check, the check of complete iterator
+    // is in the above test. In addition, we count the number of NSEC3, RRSIG
+    // and all records, as some kind of check it returns all the data.
+    std::string data[DatabaseAccessor::COLUMN_COUNT];
+
+    size_t nsec3count(0), rrsigcount(0), recordcount(0);
+    bool nsec3match(false);
+    while (context->getNext(data)) {
+        if (data[DatabaseAccessor::TYPE_COLUMN] == "NSEC3") {
+            nsec3count ++;
+            if (data[DatabaseAccessor::NAME_COLUMN] ==
+                "1BB7SO0452U1QHL98UISNDD9218GELR5.sql2.example.com.") {
+                nsec3match = true;
+                EXPECT_EQ("7200", data[DatabaseAccessor::TTL_COLUMN]);
+                EXPECT_EQ("1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J",
+                          data[DatabaseAccessor::RDATA_COLUMN]);
+            }
+        } else if (data[DatabaseAccessor::TYPE_COLUMN] == "RRSIG") {
+            rrsigcount ++;
+        }
+        recordcount ++;
+    }
+
+    // We counted everything now, so check there's nothing else to count
+    EXPECT_EQ(11, nsec3count);
+    EXPECT_EQ(22, rrsigcount);
+    EXPECT_EQ(46, recordcount);
+    EXPECT_TRUE(nsec3match) << "No NSEC3 found when iterating the zone";
+}
+
+// This tests getting NSEC3 records
+TEST_F(SQLite3AccessorTest, nsec3) {
+    const std::pair<bool, int>
+        zone_info(accessor->getZone("sql2.example.com."));
+    ASSERT_TRUE(zone_info.first);
+
+    DatabaseAccessor::IteratorContextPtr
+        context(accessor->getNSEC3Records("1BB7SO0452U1QHL98UISNDD9218GELR5",
+                                          zone_info.second));
+    // This relies on specific ordering in the DB. Is it OK?
+    // The name field is empty, as well as the sigtype one. This is OK, as
+    // both are not needed and the interface allows it.
+    checkRR(context, "", "7200", "NSEC3",
+            "1 0 10 FEEDABEE 4KLSVDE8KH8G95VU68R7AHBE1CPQN38J");
+    checkRR(context, "", "7200", "RRSIG",
+            "NSEC3 5 4 7200 20100410172647 20100311172647 63192 "
+            "sql2.example.com. gNIVj4T8t51fEU6kOPpvK7HOGBFZGbalN5ZK "
+            "mInyrww6UWZsUNdw07ge6/U6HfG+/s61RZ/L is2M6yUWHyXbNbj/"
+            "QqwqgadG5dhxTArfuR02 xP600x0fWX8LXzW4yLMdKVxGbzYT+vvGz71o "
+            "8gHSY5vYTtothcZQa4BMKhmGQEk=");
+
+    // And that's all
+    std::string data[DatabaseAccessor::COLUMN_COUNT];
+    EXPECT_FALSE(context->getNext(data));
+
+    // Calling again won't hurt
+    EXPECT_FALSE(context->getNext(data));
+
+    // This one should be empty ‒ no data here
+    context = accessor->getNSEC3Records("NO_SUCH_HASH", zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    // Still nothing? ;-)
+    EXPECT_FALSE(context->getNext(data));
+}
+
+// This tests getting a previoeus hash in the NSEC3 namespace of a zone,
+// including a wrap-around and asking for a hash that does not exist in the.
+// zone at all.
+TEST_F(SQLite3AccessorTest, nsec3Previous) {
+    // Get the zone
+    const std::pair<bool, int>
+        zone_info(accessor->getZone("sql2.example.com."));
+    ASSERT_TRUE(zone_info.first);
+
+    std::string data[DatabaseAccessor::COLUMN_COUNT];
+
+    // Test a previous hash for something that is in the zone
+    // (ensuring it is really there)
+    DatabaseAccessor::IteratorContextPtr
+        context(accessor->getNSEC3Records("703OOGCKF8VEV1N7U64D1JG19URETN8N",
+                                          zone_info.second));
+    EXPECT_TRUE(context->getNext(data));
+    EXPECT_EQ("56IEQ664LHDAKVPE2FL179MSM3QAOFVC", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "703OOGCKF8VEV1N7U64D1JG19URETN8N"));
+
+    // Test a previous hash for something that is not in the
+    // zone
+    context = accessor->getNSEC3Records("702OOGCKF8VEV1N7U64D1JG19URETN8N",
+                                        zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    EXPECT_EQ("56IEQ664LHDAKVPE2FL179MSM3QAOFVC", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "702OOGCKF8VEV1N7U64D1JG19URETN8N"));
+
+    // Search at the first item, should wrap around
+    context = accessor->getNSEC3Records("1BB7SO0452U1QHL98UISNDD9218GELR5",
+                                        zone_info.second);
+    EXPECT_TRUE(context->getNext(data));
+    EXPECT_EQ("RKBUCQT8T78GV6QBCGBHCHC019LG73SJ", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "1BB7SO0452U1QHL98UISNDD9218GELR5"));
+
+    // Search before the first item, should wrap around
+    context = accessor->getNSEC3Records("0BB7SO0452U1QHL98UISNDD9218GELR5",
+                                        zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    EXPECT_EQ("RKBUCQT8T78GV6QBCGBHCHC019LG73SJ", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "0BB7SO0452U1QHL98UISNDD9218GELR5"));
+
+    // Search after the last item (should return the last one)
+    context = accessor->getNSEC3Records("RRBUCQT8T78GV6QBCGBHCHC019LG73SJ",
+                                        zone_info.second);
+    EXPECT_FALSE(context->getNext(data));
+    EXPECT_EQ("RKBUCQT8T78GV6QBCGBHCHC019LG73SJ", accessor->
+              findPreviousNSEC3Hash(zone_info.second,
+                                    "RRBUCQT8T78GV6QBCGBHCHC019LG73SJ"));
+}
+
+// Check it throws when we want a previous NSEC3 hash in an unsigned zone
+TEST_F(SQLite3AccessorTest, nsec3PreviousUnsigned) {
+    // This zone did not look signed in the test file.
+    const std::pair<bool, int>
+        unsigned_zone_info(accessor->getZone("example.com."));
+
+    EXPECT_THROW(accessor->
+                 findPreviousNSEC3Hash(unsigned_zone_info.second,
+                                       "0BB7SO0452U1QHL98UISNDD9218GELR5"),
+                 DataSourceError);
+}
+
 // This tests the difference iterator context
 
 // Test that at attempt to create a difference iterator for a serial number
@@ -297,11 +468,11 @@ TEST(SQLite3Open, getDBNameExampleROOT) {
 // Simple function to match records
 void
 checkRecordRow(const std::string columns[],
-               const std::string& field0,
-               const std::string& field1,
-               const std::string& field2,
-               const std::string& field3,
-               const std::string& field4)
+               const std::string& field0, // for type
+               const std::string& field1, // for TTL
+               const std::string& field2, // for "sigtype"
+               const std::string& field3, // for rdata
+               const std::string& field4) // for name
 {
     EXPECT_EQ(field0, columns[DatabaseAccessor::TYPE_COLUMN]);
     EXPECT_EQ(field1, columns[DatabaseAccessor::TTL_COLUMN]);
@@ -570,13 +741,30 @@ const char* const deleted_data[] = {
     // Existing data to be removed commonly used by some of the tests below
     "foo.bar.example.com.", "A", "192.0.2.1"
 };
+const char* const nsec3_data[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT] = {
+    // example NSEC3 parameters.  Using "apex_hash" just as a convenient
+    // shortcut; otherwise it has nothing to do with the zone apex for the
+    // purpose of this test.
+    apex_hash, "3600", "NSEC3",
+    "1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA"
+};
+const char* const nsec3_sig_data[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT] = {
+    ns1_hash, "3600", "RRSIG",
+    "NSEC3 5 3 3600 20000101000000 20000201000000 12345 "
+    "example.com. FAKEFAKEFAKE"
+};
+const char* const nsec3_deleted_data[] = {
+    // Delete parameters for nsec3_data
+    apex_hash, nsec3_data[DatabaseAccessor::ADD_NSEC3_TYPE],
+    nsec3_data[DatabaseAccessor::ADD_NSEC3_RDATA]
+};
 
 class SQLite3Update : public SQLite3AccessorTest {
 protected:
     SQLite3Update() {
         // Note: if "installing" the test file fails some of the subsequent
         // tests would fail.
-        const char *install_cmd = INSTALL_PROG " " TEST_DATA_DIR
+        const char *install_cmd = INSTALL_PROG " -c " TEST_DATA_DIR
                                   "/test.sqlite3 " TEST_DATA_BUILDDIR
                                   "/test.sqlite3.copied";
         if (system(install_cmd) != 0) {
@@ -596,6 +784,7 @@ protected:
     int zone_id;
     std::string get_columns[DatabaseAccessor::COLUMN_COUNT];
     std::string add_columns[DatabaseAccessor::ADD_COLUMN_COUNT];
+    std::string add_nsec3_columns[DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT];
     std::string del_params[DatabaseAccessor::DEL_PARAM_COUNT];
     std::string diff_params[DatabaseAccessor::DIFF_PARAM_COUNT];
 
@@ -623,6 +812,28 @@ checkRecords(SQLite3Accessor& accessor, int zone_id, const std::string& name,
     EXPECT_TRUE(it == expected_rows.end());
 }
 
+// Similar to the previous one, but checking transactions on the nsec3 table.
+void
+checkNSEC3Records(SQLite3Accessor& accessor, int zone_id,
+                  const std::string& hash,
+                  vector<const char* const*> expected_rows)
+{
+    DatabaseAccessor::IteratorContextPtr iterator =
+        accessor.getNSEC3Records(hash, zone_id);
+    std::string columns[DatabaseAccessor::COLUMN_COUNT];
+    vector<const char* const*>::const_iterator it = expected_rows.begin();
+    while (iterator->getNext(columns)) {
+        ASSERT_TRUE(it != expected_rows.end());
+        checkRecordRow(columns, (*it)[DatabaseAccessor::ADD_NSEC3_TYPE],
+                       (*it)[DatabaseAccessor::ADD_NSEC3_TTL],
+                       "",      // sigtype, should always be empty
+                       (*it)[DatabaseAccessor::ADD_NSEC3_RDATA],
+                       "");     // name, always empty
+        ++it;
+    }
+    EXPECT_TRUE(it == expected_rows.end());
+}
+
 TEST_F(SQLite3Update, emptyUpdate) {
     // If we do nothing between start and commit, the zone content
     // should be intact.
@@ -645,6 +856,26 @@ TEST_F(SQLite3Update, flushZone) {
     checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
 }
 
+TEST_F(SQLite3Update, flushZoneWithNSEC3) {
+    // Similar to the previous case, but make sure the separate nsec3 table
+    // is also cleared.  We first need to add something to the table.
+    zone_id = accessor->startUpdateZone("example.com.", false).second;
+    copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+         add_nsec3_columns);
+    accessor->addNSEC3RecordToZone(add_nsec3_columns);
+    accessor->commit();
+
+    // Confirm it surely exists.
+    expected_stored.clear();
+    expected_stored.push_back(nsec3_data);
+    checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
+
+    // Then starting zone replacement.  the NSEC3 record should have been
+    // removed.
+    zone_id = accessor->startUpdateZone("example.com.", true).second;
+    checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+}
+
 TEST_F(SQLite3Update, readWhileUpdate) {
     zone_id = accessor->startUpdateZone("example.com.", true).second;
     checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
@@ -669,17 +900,27 @@ TEST_F(SQLite3Update, rollback) {
     checkRecords(*accessor, zone_id, "foo.bar.example.com.", expected_stored);
 }
 
-TEST_F(SQLite3Update, rollbackFailure) {
+TEST_F(SQLite3Update,  rollbackFailure) {
     // This test emulates a rare scenario of making rollback attempt fail.
     // The iterator is paused in the middle of getting records, which prevents
     // the rollback operation at the end of the test.
 
+    // Since SQLite3 version 3.7.11, rollbacks do not fail on pending
+    // transactions anymore, making this test fail (and moot), but the
+    // transactions will fail after it, so, depending on version,
+    // we test whether that happens and is caught
     string columns[DatabaseAccessor::COLUMN_COUNT];
     iterator = accessor->getRecords("example.com.", zone_id);
     EXPECT_TRUE(iterator->getNext(columns));
 
     accessor->startUpdateZone("example.com.", true);
+#if SQLITE_VERSION_NUMBER < 3007011
     EXPECT_THROW(accessor->rollback(), DataSourceError);
+    EXPECT_NO_THROW(iterator->getNext(columns));
+#else
+    EXPECT_NO_THROW(accessor->rollback());
+    EXPECT_THROW(iterator->getNext(columns), DataSourceError);
+#endif
 }
 
 TEST_F(SQLite3Update, commitConflict) {
@@ -748,6 +989,62 @@ TEST_F(SQLite3Update, addRecord) {
     checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored);
 }
 
+TEST_F(SQLite3Update, addNSEC3Record) {
+    // Similar to the previous test, but for NSEC3-related records
+    checkRecords(*accessor, zone_id, apex_hash, empty_stored);
+    checkRecords(*accessor, zone_id, ns1_hash, empty_stored);
+
+    zone_id = accessor->startUpdateZone("example.com.", false).second;
+    // Add an NSEC3
+    copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+         add_nsec3_columns);
+    accessor->addNSEC3RecordToZone(add_nsec3_columns);
+
+    // Add an RRSIG for NSEC3
+    copy(nsec3_sig_data,
+         nsec3_sig_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+         add_nsec3_columns);
+    accessor->addNSEC3RecordToZone(add_nsec3_columns);
+
+    // Check the stored data, before and after commit().
+    for (size_t i = 0; i < 2; ++i) {
+        expected_stored.clear();
+        expected_stored.push_back(nsec3_data);
+        checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
+
+        expected_stored.clear();
+        expected_stored.push_back(nsec3_sig_data);
+        checkNSEC3Records(*accessor, zone_id, ns1_hash, expected_stored);
+
+        if (i == 0) {          // make sure commit() happens only once
+            accessor->commit();
+        }
+    }
+}
+
+TEST_F(SQLite3Update, nsec3IteratorOnAdd) {
+    // This test checks if an added NSEC3 record will appear in the iterator
+    // result, meeting the expectation of addNSEC3RecordToZone.
+    // Specifically, it checks if the name column is filled with the complete
+    // owner name.
+
+    // We'll replace the zone, and add one NSEC3 record, and only that one.
+    zone_id = accessor->startUpdateZone("example.com.", true).second;
+    copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+         add_nsec3_columns);
+    accessor->addNSEC3RecordToZone(add_nsec3_columns);
+    accessor->commit();
+
+    // the zone should contain only one record we just added.
+    DatabaseAccessor::IteratorContextPtr context =
+        accessor->getAllRecords(zone_id);
+    string data[DatabaseAccessor::COLUMN_COUNT];
+    EXPECT_TRUE(context->getNext(data));
+    EXPECT_EQ(string(apex_hash) + ".example.com.",
+              data[DatabaseAccessor::NAME_COLUMN]);
+    EXPECT_FALSE(context->getNext(data));
+}
+
 TEST_F(SQLite3Update, addThenRollback) {
     zone_id = accessor->startUpdateZone("example.com.", false).second;
     copy(new_data, new_data + DatabaseAccessor::ADD_COLUMN_COUNT,
@@ -758,7 +1055,11 @@ TEST_F(SQLite3Update, addThenRollback) {
     expected_stored.push_back(new_data);
     checkRecords(*accessor, zone_id, "newdata.example.com.", expected_stored);
 
+    // Rollback the transaction, and confirm the zone reverts to the previous
+    // state.  We also start another update to check if the accessor can be
+    // reused for a new update after rollback.
     accessor->rollback();
+    zone_id = accessor->startUpdateZone("example.com.", false).second;
     checkRecords(*accessor, zone_id, "newdata.example.com.", empty_stored);
 }
 
@@ -784,6 +1085,12 @@ TEST_F(SQLite3Update, duplicateAdd) {
 TEST_F(SQLite3Update, invalidAdd) {
     // An attempt of add before an explicit start of transaction
     EXPECT_THROW(accessor->addRecordToZone(add_columns), DataSourceError);
+
+    // Same for addNSEC3.
+    copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+         add_nsec3_columns);
+    EXPECT_THROW(accessor->addNSEC3RecordToZone(add_nsec3_columns),
+                 DataSourceError);
 }
 
 TEST_F(SQLite3Update, deleteRecord) {
@@ -801,6 +1108,32 @@ TEST_F(SQLite3Update, deleteRecord) {
     checkRecords(*accessor, zone_id, "foo.bar.example.com.", empty_stored);
 }
 
+TEST_F(SQLite3Update, deleteNSEC3Record) {
+    // Similar to the previous test, but for NSEC3.
+    zone_id = accessor->startUpdateZone("example.com.", false).second;
+    checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+
+    // We first need to add some record.
+    copy(nsec3_data, nsec3_data + DatabaseAccessor::ADD_NSEC3_COLUMN_COUNT,
+         add_nsec3_columns);
+    accessor->addNSEC3RecordToZone(add_nsec3_columns);
+
+    // Now it should exist.
+    expected_stored.clear();
+    expected_stored.push_back(nsec3_data);
+    checkNSEC3Records(*accessor, zone_id, apex_hash, expected_stored);
+
+    // Delete it, and confirm that.
+    copy(nsec3_deleted_data,
+         nsec3_deleted_data + DatabaseAccessor::DEL_PARAM_COUNT, del_params);
+    accessor->deleteNSEC3RecordInZone(del_params);
+    checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+
+    // Commit the change, and confirm the deleted data still isn't there.
+    accessor->commit();
+    checkNSEC3Records(*accessor, zone_id, apex_hash, empty_stored);
+}
+
 TEST_F(SQLite3Update, deleteThenRollback) {
     zone_id = accessor->startUpdateZone("example.com.", false).second;
 
@@ -847,6 +1180,10 @@ TEST_F(SQLite3Update, deleteNonexistent) {
 TEST_F(SQLite3Update, invalidDelete) {
     // An attempt of delete before an explicit start of transaction
     EXPECT_THROW(accessor->deleteRecordInZone(del_params), DataSourceError);
+
+    // Same for NSEC3.
+    EXPECT_THROW(accessor->deleteNSEC3RecordInZone(del_params),
+                 DataSourceError);
 }
 
 TEST_F(SQLite3Update, emptyTransaction) {
@@ -947,7 +1284,7 @@ const char* const diff_end_data[] = {
     "1300", DIFF_ADD_TEXT
 };
 const char* const diff_add_a_data[] = {
-    "dns01.example.com.", "A", "3600", "192.0.2.10", "1234", DIFF_ADD_TEXT
+    "dns01.example.com.", "A", "3600", "192.0.2.10", "1300", DIFF_ADD_TEXT
 };
 
 // The following two are helper functions to convert textual test data
@@ -968,8 +1305,19 @@ getOperation(const char* const diff_data[]) {
 // diffs.
 void
 checkDiffs(const vector<const char* const*>& expected,
-           const vector<vector<string> >& actual)
+           DatabaseAccessor::IteratorContextPtr rr_iterator)
 {
+    vector<vector<string> > actual;
+    string columns_holder[DatabaseAccessor::COLUMN_COUNT];
+    while (rr_iterator->getNext(columns_holder)) {
+        // Reorder the 'actual' vector to be compatible with the expected one.
+        vector<string> columns;
+        columns.push_back(columns_holder[DatabaseAccessor::NAME_COLUMN]);
+        columns.push_back(columns_holder[DatabaseAccessor::TYPE_COLUMN]);
+        columns.push_back(columns_holder[DatabaseAccessor::TTL_COLUMN]);
+        columns.push_back(columns_holder[DatabaseAccessor::RDATA_COLUMN]);
+        actual.push_back(columns);
+    }
     EXPECT_EQ(expected.size(), actual.size());
     const size_t n_diffs = std::min(expected.size(), actual.size());
     for (size_t i = 0; i < n_diffs; ++i) {
@@ -995,16 +1343,18 @@ TEST_F(SQLite3Update, addRecordDiff) {
                             getOperation(diff_end_data), diff_params);
 
     // Until the diffs are committed, they are not visible to other accessors.
-    EXPECT_TRUE(another_accessor->getRecordDiff(zone_id).empty());
+    EXPECT_THROW(another_accessor->getDiffs(zone_id, 1234, 1300),
+                 NoSuchSerial);
 
     accessor->commit();
 
     expected_stored.clear();
     expected_stored.push_back(diff_begin_data);
     expected_stored.push_back(diff_end_data);
-    checkDiffs(expected_stored, accessor->getRecordDiff(zone_id));
+    checkDiffs(expected_stored, accessor->getDiffs(zone_id, 1234, 1300));
     // Now it should be visible to others, too.
-    checkDiffs(expected_stored, another_accessor->getRecordDiff(zone_id));
+    checkDiffs(expected_stored, another_accessor->getDiffs(zone_id, 1234,
+                                                           1300));
 }
 
 TEST_F(SQLite3Update, addRecordOfLargeSerial) {
@@ -1036,7 +1386,7 @@ TEST_F(SQLite3Update, addRecordOfLargeSerial) {
     expected_stored.clear();
     expected_stored.push_back(begin_data);
     expected_stored.push_back(diff_end_data);
-    checkDiffs(expected_stored, accessor->getRecordDiff(zone_id));
+    checkDiffs(expected_stored, accessor->getDiffs(zone_id, 4294967295U, 1300));
 }
 
 TEST_F(SQLite3Update, addDiffWithoutUpdate) {
@@ -1081,7 +1431,7 @@ TEST_F(SQLite3Update, addDiffRollback) {
                             getOperation(diff_begin_data), diff_params);
     accessor->rollback();
 
-    EXPECT_TRUE(accessor->getRecordDiff(zone_id).empty());
+    EXPECT_THROW(accessor->getDiffs(zone_id, 1234, 1234), NoSuchSerial);
 }
 
 TEST_F(SQLite3Update, addDiffInBadOrder) {
@@ -1093,19 +1443,23 @@ TEST_F(SQLite3Update, addDiffInBadOrder) {
     copy(diff_end_data, diff_end_data + DatabaseAccessor::DIFF_PARAM_COUNT,
          diff_params);
     accessor->addRecordDiff(zone_id, getVersion(diff_end_data),
-                            getOperation(diff_end_data), diff_params);
+                            static_cast<DatabaseAccessor::DiffOperation>(
+                                lexical_cast<int>(DIFF_DELETE_TEXT)),
+                            diff_params);
 
     copy(diff_begin_data, diff_begin_data + DatabaseAccessor::DIFF_PARAM_COUNT,
          diff_params);
     accessor->addRecordDiff(zone_id, getVersion(diff_begin_data),
-                            getOperation(diff_begin_data), diff_params);
+                            static_cast<DatabaseAccessor::DiffOperation>(
+                                lexical_cast<int>(DIFF_ADD_TEXT)),
+                            diff_params);
 
     accessor->commit();
 
     expected_stored.clear();
     expected_stored.push_back(diff_end_data);
     expected_stored.push_back(diff_begin_data);
-    checkDiffs(expected_stored, accessor->getRecordDiff(zone_id));
+    checkDiffs(expected_stored, accessor->getDiffs(zone_id, 1300, 1234));
 }
 
 TEST_F(SQLite3Update, addDiffWithUpdate) {
@@ -1175,19 +1529,6 @@ TEST_F(SQLite3Update, addDiffWithUpdate) {
     expected_stored.push_back(diff_end_data);
     expected_stored.push_back(diff_add_a_data);
 
-    checkDiffs(expected_stored, accessor->getRecordDiff(zone_id));
-}
-
-TEST_F(SQLite3Update, addDiffWithNoTable) {
-    // An attempt of adding diffs to an old version of database that doesn't
-    // have a diffs table.  This will fail in preparing the statement.
-    initAccessor(SQLITE_DBFILE_EXAMPLE + ".nodiffs", "IN");
-    zone_id = accessor->startUpdateZone("example.com.", false).second;
-    copy(diff_begin_data, diff_begin_data + DatabaseAccessor::DIFF_PARAM_COUNT,
-         diff_params);
-    EXPECT_THROW(accessor->addRecordDiff(zone_id, getVersion(diff_begin_data),
-                                         getOperation(diff_begin_data),
-                                         diff_params),
-                 SQLite3Error);
+    checkDiffs(expected_stored, accessor->getDiffs(zone_id, 1234, 1300));
 }
 } // end anonymous namespace
diff --git a/src/lib/datasrc/tests/sqlite3_unittest.cc b/src/lib/datasrc/tests/sqlite3_unittest.cc
index ce9413d..ac1211b 100644
--- a/src/lib/datasrc/tests/sqlite3_unittest.cc
+++ b/src/lib/datasrc/tests/sqlite3_unittest.cc
@@ -51,6 +51,10 @@ 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,
@@ -403,6 +407,17 @@ TEST_F(Sqlite3DataSourceTest, openBrokenDB) {
     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());
diff --git a/src/lib/datasrc/tests/static_unittest.cc b/src/lib/datasrc/tests/static_unittest.cc
index 08f2582..2a19ecb 100644
--- a/src/lib/datasrc/tests/static_unittest.cc
+++ b/src/lib/datasrc/tests/static_unittest.cc
@@ -56,6 +56,7 @@ protected:
         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");
@@ -65,6 +66,7 @@ protected:
         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");
diff --git a/src/lib/datasrc/tests/test_client.cc b/src/lib/datasrc/tests/test_client.cc
new file mode 100644
index 0000000..c7854ed
--- /dev/null
+++ b/src/lib/datasrc/tests/test_client.cc
@@ -0,0 +1,92 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/client.h>
+#include <datasrc/zone.h>
+#include <datasrc/sqlite3_accessor.h>
+
+#include "test_client.h"
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <cstdlib>
+#include <istream>
+#include <fstream>
+
+using namespace std;
+using boost::shared_ptr;
+
+using namespace isc::dns;
+
+namespace isc {
+namespace datasrc {
+namespace unittest {
+
+namespace {
+// A helper subroutine for the SQLite3Client creator.
+void
+addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
+    updater->addRRset(*rrset);
+}
+}
+
+shared_ptr<DataSourceClient>
+createSQLite3Client(RRClass zclass, const Name& zname,
+                    const char* const db_file, const char* const zone_file)
+{
+    ifstream ifs(zone_file, ios_base::in);
+    if (ifs.fail()) {
+        isc_throw(isc::Unexpected, "Failed to open test zone file: "
+                  << zone_file);
+    }
+    return (createSQLite3Client(zclass, zname, db_file, ifs));
+}
+
+shared_ptr<DataSourceClient>
+createSQLite3Client(RRClass zclass, const Name& zname,
+                    const char* const db_file, istream& rr_stream)
+{
+    // 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.
+    const char* const install_cmd_prefix = INSTALL_PROG " -c " TEST_DATA_COMMONDIR
+        "/rwtest.sqlite3 ";
+    const string install_cmd = string(install_cmd_prefix) + db_file;
+    if (system(install_cmd.c_str()) != 0) {
+        isc_throw(isc::Unexpected,
+                  "Error setting up; command failed: " << install_cmd);
+    }
+
+    shared_ptr<SQLite3Accessor> accessor(
+        new SQLite3Accessor(db_file, zclass.toText()));
+    shared_ptr<DatabaseClient> client(new DatabaseClient(zclass, accessor));
+
+    ZoneUpdaterPtr updater = client->getUpdater(zname, true);
+    masterLoad(rr_stream, zname, zclass, boost::bind(addRRset, updater, _1));
+
+    updater->commit();
+
+    return (client);
+}
+
+}
+}
+}
diff --git a/src/lib/datasrc/tests/test_client.h b/src/lib/datasrc/tests/test_client.h
new file mode 100644
index 0000000..2c692d3
--- /dev/null
+++ b/src/lib/datasrc/tests/test_client.h
@@ -0,0 +1,71 @@
+// 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 __TEST_DATA_SOURCE_CLIENT_H
+#define __TEST_DATA_SOURCE_CLIENT_H 1
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <istream>
+
+namespace isc {
+namespace datasrc {
+namespace unittest {
+
+// Here we define utility modules for the convenience of tests that create
+// a data source client according to the specified conditions.
+
+/// \brief Create an SQLite3 data source client from a zone file.
+///
+/// This function creates an SQLite3 client for the specified zone containing
+/// RRs in the specified zone file.  The zone will be created in the given
+/// SQLite3 database file.  The database file does not have to exist; this
+/// function will automatically create a new file for the test; if the given
+/// file already exists this function overrides the content (so basically the
+/// file must be an ephemeral one only for that test case).
+///
+/// The zone file must be formatted so it's accepted by the dns::masterLoad()
+/// function.
+///
+/// \param zclass The RR class of the zone
+/// \param zname The origin name of the zone
+/// \param db_file The SQLite3 data base file in which the zone data should be
+/// installed.
+/// \param zone_file The filename of the zone data in the textual format.
+/// \return Newly created \c DataSourceClient using the SQLite3 data source
+boost::shared_ptr<DataSourceClient>
+createSQLite3Client(dns::RRClass zclass, const dns::Name& zname,
+                    const char* const db_file, const char* const zone_file);
+
+/// \brief Create an SQLite3 data source client from a stream.
+///
+/// This is similar to the other version of the function, but takes an input
+/// stream for the zone data.  The stream produces strings as the corresponding
+/// dns::masterLoad() function expects.
+boost::shared_ptr<DataSourceClient>
+createSQLite3Client(dns::RRClass zclass, const dns::Name& zname,
+                    const char* const db_file, std::istream& rr_stream);
+
+} // end of unittest
+} // end of datasrc
+} // end of isc
+
+#endif  // __TEST_DATA_SOURCE_CLIENT_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/datasrc/tests/testdata/.gitignore b/src/lib/datasrc/tests/testdata/.gitignore
new file mode 100644
index 0000000..58ea8cd
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/.gitignore
@@ -0,0 +1 @@
+/*.sqlite3.copied
diff --git a/src/lib/datasrc/tests/testdata/contexttest.zone b/src/lib/datasrc/tests/testdata/contexttest.zone
new file mode 100644
index 0000000..e499e0d
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/contexttest.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/testdata/diffs.sqlite3 b/src/lib/datasrc/tests/testdata/diffs.sqlite3
index 3820563..4cf8fb7 100644
Binary files a/src/lib/datasrc/tests/testdata/diffs.sqlite3 and b/src/lib/datasrc/tests/testdata/diffs.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/example.org.sqlite3 b/src/lib/datasrc/tests/testdata/example.org.sqlite3
index 60e6e05..c7388ff 100644
Binary files a/src/lib/datasrc/tests/testdata/example.org.sqlite3 and b/src/lib/datasrc/tests/testdata/example.org.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/example2.com.sqlite3 b/src/lib/datasrc/tests/testdata/example2.com.sqlite3
index 9da7d0e..a0a576c 100644
Binary files a/src/lib/datasrc/tests/testdata/example2.com.sqlite3 and b/src/lib/datasrc/tests/testdata/example2.com.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/new_minor_schema.sqlite3 b/src/lib/datasrc/tests/testdata/new_minor_schema.sqlite3
new file mode 100644
index 0000000..1542c20
Binary files /dev/null and b/src/lib/datasrc/tests/testdata/new_minor_schema.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/newschema.sqlite3 b/src/lib/datasrc/tests/testdata/newschema.sqlite3
new file mode 100644
index 0000000..460cfa8
Binary files /dev/null and b/src/lib/datasrc/tests/testdata/newschema.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/oldschema.sqlite3 b/src/lib/datasrc/tests/testdata/oldschema.sqlite3
new file mode 100644
index 0000000..b44c5eb
Binary files /dev/null and b/src/lib/datasrc/tests/testdata/oldschema.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/rrset_toWire1 b/src/lib/datasrc/tests/testdata/rrset_toWire1
new file mode 100644
index 0000000..8f81a0e
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/rrset_toWire1
@@ -0,0 +1,23 @@
+#
+# Rendering an IN/A RRset containing 2 RRs:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+#
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6  7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: mostly the same except the RDATA
+04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02
diff --git a/src/lib/datasrc/tests/testdata/rrset_toWire2 b/src/lib/datasrc/tests/testdata/rrset_toWire2
new file mode 100644
index 0000000..b9a6a15
--- /dev/null
+++ b/src/lib/datasrc/tests/testdata/rrset_toWire2
@@ -0,0 +1,26 @@
+#
+# Rendering an IN/A RRset and NS RRset as follows:
+# test.example.com. 3600 IN A 192.0.2.1
+# test.example.com. 3600 IN A 192.0.2.2
+# example.com. 1D IN NS ns.example.com.
+# Names will be compressed when possible.
+#
+# 0  1  2  3  4  5
+#(4) t  e  s  t (7) e  x  a  m  p  l  e (3) c  o  m  .
+ 04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
+# type/class: A = 1, IN = 1
+00 01 00 01
+# TTL: 3600
+00 00 0e 10
+#6  7
+# RDLENGTH: 4
+00 04
+# RDATA: 192.0.2.1
+c0 00 02 01
+#
+# 2nd RR: the owner name is compresed
+c0 00
+00 01 00 01
+00 00 0e 10
+00 04
+c0 00 02 02
diff --git a/src/lib/datasrc/tests/testdata/rwtest.sqlite3 b/src/lib/datasrc/tests/testdata/rwtest.sqlite3
deleted file mode 100644
index ccbb884..0000000
Binary files a/src/lib/datasrc/tests/testdata/rwtest.sqlite3 and /dev/null differ
diff --git a/src/lib/datasrc/tests/testdata/test-root.sqlite3 b/src/lib/datasrc/tests/testdata/test-root.sqlite3
index c1dae47..1bef761 100644
Binary files a/src/lib/datasrc/tests/testdata/test-root.sqlite3 and b/src/lib/datasrc/tests/testdata/test-root.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/test.sqlite3 b/src/lib/datasrc/tests/testdata/test.sqlite3
index 521cf31..9c71cb5 100644
Binary files a/src/lib/datasrc/tests/testdata/test.sqlite3 and b/src/lib/datasrc/tests/testdata/test.sqlite3 differ
diff --git a/src/lib/datasrc/tests/testdata/test.sqlite3.nodiffs b/src/lib/datasrc/tests/testdata/test.sqlite3.nodiffs
deleted file mode 100644
index cc8cfc3..0000000
Binary files a/src/lib/datasrc/tests/testdata/test.sqlite3.nodiffs and /dev/null differ
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
new file mode 100644
index 0000000..50d409e
--- /dev/null
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -0,0 +1,413 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/masterload.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <datasrc/zone.h>
+#include <datasrc/memory_datasrc.h>
+#include <datasrc/database.h>
+#include <datasrc/sqlite3_accessor.h>
+
+#include "test_client.h"
+#include <testutils/dnsmessage_test.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <fstream>
+#include <sstream>
+#include <vector>
+
+using namespace std;
+using boost::shared_ptr;
+
+using namespace isc::dns;
+using namespace isc::datasrc;
+using namespace isc::testutils;
+
+namespace {
+
+// Commonly used test zone file.
+const char* const TEST_ZONE_FILE = TEST_DATA_DIR "/contexttest.zone";
+
+// Convenient shortcut
+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&);
+
+// 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);
+
+    return (client);
+}
+
+void
+addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
+    updater->addRRset(*rrset);
+}
+
+DataSourceClientPtr
+createSQLite3Client(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.
+    DataSourceClientPtr client = unittest::createSQLite3Client(
+        zclass, zname, TEST_DATA_BUILDDIR "/contexttest.sqlite3.copied",
+        TEST_ZONE_FILE);
+
+    // Insert an out-of-zone name to test if it's incorrectly returned.
+    // Note that neither updater nor SQLite3 accessor checks this condition,
+    // so this should succeed.
+    ZoneUpdaterPtr updater = client->getUpdater(zname, false);
+    stringstream ss("ns.example.com. 3600 IN A 192.0.2.7");
+    masterLoad(ss, Name::ROOT_NAME(), zclass,
+               boost::bind(addRRset, updater, _1));
+    updater->commit();
+
+    return (client);
+}
+
+// The test class.  Its parameterized so we can share the test scnearios
+// for any concrete data source implementaitons.
+class ZoneFinderContextTest :
+        public ::testing::TestWithParam<ClientCreator>
+{
+protected:
+    ZoneFinderContextTest() : qclass_(RRClass::IN()), qzone_("example.org") {
+        client_ = (*GetParam())(qclass_, qzone_);
+        REQUESTED_A.push_back(RRType::A());
+        REQUESTED_AAAA.push_back(RRType::AAAA());
+        REQUESTED_BOTH.push_back(RRType::A());
+        REQUESTED_BOTH.push_back(RRType::AAAA());
+    }
+    void SetUp() {
+        finder_ = client_->findZone(qzone_).zone_finder;
+        ASSERT_TRUE(finder_);
+    }
+
+    const RRClass qclass_;
+    const Name qzone_;
+    DataSourceClientPtr client_;
+    ZoneFinderPtr finder_;
+
+    vector<RRType> requested_types_;
+    vector<RRType> REQUESTED_A;
+    vector<RRType> REQUESTED_AAAA;
+    vector<RRType> REQUESTED_BOTH;
+    vector<ConstRRsetPtr> result_sets_;
+};
+
+// We test the in-memory and SQLite3 data source implementations.
+INSTANTIATE_TEST_CASE_P(, ZoneFinderContextTest,
+                        ::testing::Values(createInMemoryClient,
+                                          createSQLite3Client));
+
+TEST_P(ZoneFinderContextTest, getAdditionalAuthNS) {
+    ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    // Getting both A and AAAA NS addresses
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only A
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only AAAA
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting A again, without clearing the result sets.  This confirms
+    // getAdditional() doesn't change the existing vector content.
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    // The first element should be the existing AAAA RR, followed by the A's.
+    EXPECT_EQ(RRType::AAAA(), result_sets_[0]->getType());
+    rrsetsCheck("ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Normally expected type set contain only A and/or AAAA, but others aren't
+    // excluded.
+    result_sets_.clear();
+    requested_types_.push_back(RRType::TXT());
+    ctx->getAdditional(requested_types_, result_sets_);
+    rrsetsCheck("ns2.example.org. 3600 IN TXT \"text data\"",
+                result_sets_.begin(), result_sets_.end());
+
+    // Even empty set is okay.  The result should also be empty.
+    result_sets_.clear();
+    ctx->getAdditional(vector<RRType>(), result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegation) {
+    // Basically similar to the AuthNS case, but NS names are glues.
+    // It contains an out-of-zone NS name.  Its address (even if it's somehow
+    // inserted to the zone data) shouldn't be returned.
+    const Name qname("www.a.example.org");
+    ZoneFinderContextPtr ctx = finder_->find(qname, RRType::AAAA());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+                "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+                "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+                result_sets_.begin(), result_sets_.end());
+
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+                "ns2.a.example.org. 3600 IN A 192.0.2.6\n",
+                result_sets_.begin(), result_sets_.end());
+
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+    rrsetsCheck("ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationAtZoneCut) {
+    // Similar to the previous case, but one of the NS addresses is at the
+    // zone cut.
+
+    // XXX: the current database-based data source incorrectly rejects this
+    // setup (see #1771)
+    if (GetParam() == createSQLite3Client) {
+        return;
+    }
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.b.example.org"),
+                                             RRType::SOA());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("b.example.org. 3600 IN AAAA 2001:db8::8\n"
+                "ns.b.example.org. 3600 IN A 192.0.2.9\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithDname) {
+    // Delegation: One of the NS names under a DNAME delegation; another
+    // is at the delegation point; yet another is under DNAME below a zone cut.
+    // The first should be hidden.
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.c.example.org"),
+                                             RRType::TXT());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("dname.example.org. 3600 IN A 192.0.2.12\n"
+                "ns.deepdname.example.org. 3600 IN AAAA 2001:db8::9\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.
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.d.example.org"),
+                                             RRType::A());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationWithWild) {
+    // An NS name needs to be expanded by a wildcard.  Another NS name
+    // also matches a wildcard, but it's an empty node, so there's no
+    // corresponding additional RR.  The other NS name isn't subject to
+    // wildcard expansion, which shouldn't cause any disruption.
+    ZoneFinderContextPtr ctx = finder_->find(Name("www.e.example.org"),
+                                             RRType::AAAA());
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns.wild.example.org. 3600 IN A 192.0.2.15\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // ns.wild.example.org/A (expanded from a wildcard) should be considered
+    // the same kind, whether it's a direct result of find() or a result of
+    // getAdditional().
+    ctx = finder_->find(Name("ns.wild.example.org"), RRType::A());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+    for (vector<ConstRRsetPtr>::const_iterator it = result_sets_.begin();
+         it != result_sets_.end(); ++it) {
+        const bool same_kind = (*it)->isSameKind(*ctx->rrset);
+        EXPECT_EQ((*it)->getName() == ctx->rrset->getName(), same_kind);
+    }
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalDelegationForWild) {
+    // additional for an answer RRset (MX) as a result of wildcard expansion.
+    // note the difference from the previous test.  in this case wildcard
+    // applies to the owner name of the answer, not the owner name of the
+    // additional.
+    ZoneFinderContextPtr ctx = finder_->find(Name("mx.wildmx.example.org"),
+                                             RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalMX) {
+    // Similar to the previous cases, but for MX addresses.  The test zone
+    // contains MX name under a zone cut.  Its address shouldn't be returned.
+    ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    // Getting both A and AAAA NS addresses
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n"
+                "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only A
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_A, result_sets_);
+    rrsetsCheck("mx1.example.org. 3600 IN A 192.0.2.10\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // Getting only AAAA
+    result_sets_.clear();
+    ctx->getAdditional(REQUESTED_AAAA, result_sets_);
+    rrsetsCheck("mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalMXAtZoneCut) {
+    // XXX: the current database-based data source incorrectly rejects this
+    // setup (see #1771)
+    if (GetParam() == createSQLite3Client) {
+        return;
+    }
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("mxatcut.example.org."),
+                                             RRType::MX());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalWithSIG) {
+    // Similar to the AuthNS test, but the original find() requested DNSSEC
+    // RRSIGs.  Then additional records will also have RRSIGs.
+    ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS(),
+                                             ZoneFinder::FIND_DNSSEC);
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n",
+                result_sets_.begin(), result_sets_.end());
+
+    vector<ConstRRsetPtr> sigresult_sets;
+    BOOST_FOREACH(ConstRRsetPtr rrset, result_sets_) {
+        ConstRRsetPtr sig_rrset = rrset->getRRsig();
+        if (sig_rrset) {
+            sigresult_sets.push_back(sig_rrset);
+        }
+    }
+    rrsetsCheck("ns1.example.org. 3600 IN RRSIG	A 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKE\n"
+                "ns1.example.org. 3600 IN RRSIG	AAAA 7 3 3600 20150420235959 "
+                "20051021000000 40430 example.org. FAKEFAKEFAKE\n",
+                sigresult_sets.begin(), sigresult_sets.end());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalNoOP) {
+    // getAdditional() is only meaningful after SUCCESS or DELEGATION.
+
+    ZoneFinderContextPtr ctx = finder_->find(Name("nxdomain.example.org"),
+                                             RRType::NS());
+    EXPECT_EQ(ZoneFinder::NXDOMAIN, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+
+    ctx = finder_->find(qzone_, RRType::TXT());
+    EXPECT_EQ(ZoneFinder::NXRRSET, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+
+    ctx = finder_->find(Name("alias.example.org."), RRType::A());
+    EXPECT_EQ(ZoneFinder::CNAME, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+
+    ctx = finder_->find(Name("www.dname.example.org."), RRType::A());
+    EXPECT_EQ(ZoneFinder::DNAME, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    EXPECT_TRUE(result_sets_.empty());
+}
+
+TEST_P(ZoneFinderContextTest, getAdditionalForAny) {
+    // getAdditional() after successful type ANY query should return
+    // the additional records of all returned RRsets.
+    vector<ConstRRsetPtr> all_rrsets;
+    ZoneFinderContextPtr ctx = finder_->findAll(qzone_, all_rrsets);
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.example.org. 3600 IN A 192.0.2.1\n"
+                "ns1.example.org. 3600 IN AAAA 2001:db8::1\n"
+                "ns2.example.org. 3600 IN A 192.0.2.2\n"
+                "mx1.example.org. 3600 IN A 192.0.2.10\n"
+                "mx2.example.org. 3600 IN AAAA 2001:db8::10\n",
+                result_sets_.begin(), result_sets_.end());
+
+    // If the type ANY query results in DELEGATION, the result should be the
+    // same as normal query.
+    all_rrsets.clear();
+    result_sets_.clear();
+    ctx = finder_->findAll(Name("www.a.example.org"), all_rrsets);
+    EXPECT_EQ(ZoneFinder::DELEGATION, ctx->code);
+    ctx->getAdditional(REQUESTED_BOTH, result_sets_);
+    rrsetsCheck("ns1.a.example.org. 3600 IN A 192.0.2.5\n"
+                "ns2.a.example.org. 3600 IN A 192.0.2.6\n"
+                "ns2.a.example.org. 3600 IN AAAA 2001:db8::6\n",
+                result_sets_.begin(), result_sets_.end());
+}
+
+}
diff --git a/src/lib/datasrc/zone.h b/src/lib/datasrc/zone.h
index ff88746..c68a01c 100644
--- a/src/lib/datasrc/zone.h
+++ b/src/lib/datasrc/zone.h
@@ -15,17 +15,28 @@
 #ifndef __ZONE_H
 #define __ZONE_H 1
 
-#include <utility>
-#include <vector>
-
+#include <dns/name.h>
 #include <dns/rrset.h>
-#include <dns/rrsetlist.h>
+#include <dns/rrtype.h>
 
 #include <datasrc/result.h>
 
+#include <utility>
+#include <vector>
+
 namespace isc {
 namespace datasrc {
 
+/// \brief Out of zone exception
+///
+/// This is thrown when a method is called for a name or RRset which
+/// is not in or below the zone.
+class OutOfZone : public Exception {
+public:
+    OutOfZone(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
 /// \brief The base class to search a zone for RRsets
 ///
 /// The \c ZoneFinder class is an abstract base class for representing
@@ -79,7 +90,7 @@ public:
     /// proof of the result.
     ///
     /// The caller is generally expected to get access to the information
-    /// via read-only getter methods of \c FindResult so that it won't rely
+    /// via read-only getter methods of \c FindContext so that it won't rely
     /// on specific details of the representation of the flags.  So these
     /// definitions are basically only meaningful for data source
     /// implementations.
@@ -90,39 +101,120 @@ public:
         RESULT_NSEC3_SIGNED = 4   ///< The zone is signed with NSEC3 RRs
     };
 
-    /// A helper structure to represent the search result of \c find().
-    ///
-    /// This is a straightforward tuple of the result code and a pointer
-    /// (and optionally special flags) to the found RRset to represent the
-    /// result of \c find() (there will be more members in the future -
-    /// see the class description).
-    /// We use this in order to avoid overloading the return value for both
-    /// the result code ("success" or "not found") and the found object,
-    /// i.e., avoid using \c NULL to mean "not found", etc.
-    ///
-    /// This is a simple value class whose internal state never changes,
-    /// so for convenience we allow the applications to refer to some of the
-    /// members directly.  For others we provide read-only accessor methods
-    /// to hide specific representation.
-    ///
-    /// Note: we should eventually include a notion of "zone node", which
-    /// corresponds to a particular domain name of the zone, so that we can
-    /// find RRsets of a different RR type for that name (e.g. for type ANY
-    /// query or to include DS RRs with delegation).
-    ///
-    /// Note: we may also want to include the closest enclosure "node" to
-    /// optimize including the NSEC for no-wildcard proof (FWIW NSD does that).
-    struct FindResult {
-        FindResult(Result param_code,
-                   const isc::dns::ConstRRsetPtr param_rrset,
-                   FindResultFlags param_flags = RESULT_DEFAULT) :
-            code(param_code), rrset(param_rrset), flags(param_flags)
+    /// Find options.
+    ///
+    /// The option values are used as a parameter for \c find().
+    /// These are values of a bitmask type.  Bitwise operations can be
+    /// performed on these values to express compound options.
+    enum FindOptions {
+        FIND_DEFAULT = 0,       ///< The default options
+        FIND_GLUE_OK = 1,       ///< Allow search under a zone cut
+        FIND_DNSSEC = 2,        ///< Require DNSSEC data in the answer
+                                ///< (RRSIG, NSEC, etc.). The implementation
+                                ///< is allowed to include it even if it is
+                                ///< not set.
+        NO_WILDCARD = 4         ///< Do not try wildcard matching.
+    };
+
+protected:
+    /// \brief A convenient tuple representing a set of find() results.
+    ///
+    /// This helper structure is specifically expected to be used as an input
+    /// for the construct of the \c Context class object used by derived
+    /// ZoneFinder implementations.  This is therefore defined as protected.
+    struct ResultContext {
+        ResultContext(Result code_param,
+                      isc::dns::ConstRRsetPtr rrset_param,
+                      FindResultFlags flags_param = RESULT_DEFAULT) :
+            code(code_param), rrset(rrset_param), flags(flags_param)
+        {}
+        const Result code;
+        const isc::dns::ConstRRsetPtr rrset;
+        const FindResultFlags flags;
+    };
+
+public:
+    /// \brief Context of the result of a find() call.
+    ///
+    /// This class encapsulates results and (possibly) associated context
+    /// of a call to the \c find() method.   The public member variables of
+    /// this class reprsent the result of the call.  They are a
+    /// straightforward tuple of the result code and a pointer (and
+    /// optionally special flags) to the found RRset.
+    ///
+    /// These member variables will be initialized on construction and never
+    /// change, so for convenience we allow the applications to refer to some
+    /// of the members directly.  For some others we provide read-only accessor
+    /// methods to hide specific representation.
+    ///
+    /// Another role of this class is to provide the interface to some common
+    /// processing logic that may be necessary using the result of \c find().
+    /// Specifically, it's expected to be used in the context of DNS query
+    /// handling, where the caller would need to look into the data source
+    /// again based on the \c find() result.  For example, it would need to
+    /// get A and/or AAAA records for some of the answer or authority RRs.
+    ///
+    /// This class defines (a set of) method(s) that can be commonly used
+    /// for such purposes for any type of data source (as long as it conforms
+    /// to the public \c find() interface).  In some cases, a specific data
+    /// source implementation may want to (and can) optimize the processing
+    /// exploiting its internal data structure and the knowledge of the context
+    /// of the precedent \c find() call.  Such a data source implementation
+    /// can define a derived class of the base Context and override the
+    /// specific virtual method.
+    ///
+    /// 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.
+        ///
+        /// \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) :
+            code(result.code), rrset(result.rrset),
+            finder_(finder), flags_(result.flags), options_(options),
+            all_set_(all_set)
         {}
+
+        /// \brief The destructor.
+        virtual ~Context() {}
+
         const Result code;
         const isc::dns::ConstRRsetPtr rrset;
 
         /// Return true iff find() results in a wildcard match.
-        bool isWildcard() const { return ((flags & RESULT_WILDCARD) != 0); }
+        bool isWildcard() const { return ((flags_ & RESULT_WILDCARD) != 0); }
 
         /// Return true when the underlying zone is signed with NSEC.
         ///
@@ -134,7 +226,7 @@ public:
         /// that \c rrset be a valid NSEC RRset as described in \c find()
         /// documentation.
         bool isNSECSigned() const {
-            return ((flags & RESULT_NSEC_SIGNED) != 0);
+            return ((flags_ & RESULT_NSEC_SIGNED) != 0);
         }
 
         /// Return true when the underlying zone is signed with NSEC3.
@@ -143,25 +235,72 @@ public:
         /// \c FIND_DNSSEC isn't specified regardless of whether the zone
         /// is signed or which of NSEC/NSEC3 is used.
         bool isNSEC3Signed() const {
-            return ((flags & RESULT_NSEC3_SIGNED) != 0);
+            return ((flags_ & RESULT_NSEC3_SIGNED) != 0);
         }
-    private:
-        FindResultFlags flags;
-    };
 
-    /// Find options.
-    ///
-    /// The option values are used as a parameter for \c find().
-    /// These are values of a bitmask type.  Bitwise operations can be
-    /// performed on these values to express compound options.
-    enum FindOptions {
-        FIND_DEFAULT = 0,       ///< The default options
-        FIND_GLUE_OK = 1,       ///< Allow search under a zone cut
-        FIND_DNSSEC = 2,        ///< Require DNSSEC data in the answer
-                                ///< (RRSIG, NSEC, etc.). The implementation
-                                ///< is allowed to include it even if it is
-                                ///< not set.
-        NO_WILDCARD = 4         ///< Do not try wildcard matching.
+        /// \brief Find and return additional RRsets corresponding to the
+        ///        result of \c find().
+        ///
+        /// If this context is based on a normal find() call that resulted
+        /// in SUCCESS or DELEGATION, it examines the returned RRset (in many
+        /// cases NS, sometimes MX or others), searches the data source for
+        /// specified type of additional RRs for each RDATA of the RRset
+        /// (e.g., A or AAAA for the name server addresses), and stores the
+        /// result in the given vector.  The vector may not be empty; this
+        /// method appends any found RRsets to it, without touching existing
+        /// elements.
+        ///
+        /// If this context is based on a findAll() call that resulted in
+        /// SUCCESS, it performs the same process for each RRset returned in
+        /// the \c findAll() call.
+        ///
+        /// The caller specifies desired RR types of the additional RRsets
+        /// in \c requested_types.  Normally it consists of A and/or AAAA
+        /// types, but other types can be specified.
+        ///
+        /// This method is meaningful only when the precedent find()/findAll()
+        /// call resulted in SUCCESS or DELEGATION.  Otherwise this method
+        /// does nothing.
+        ///
+        /// \note The additional RRsets returned via method are limited to
+        /// ones contained in the zone which the corresponding find/findAll
+        /// call searched (possibly including glues under a zone cut where
+        /// they are applicable).  If the caller needs to get out-of-zone
+        /// additional RRsets, it needs to explicitly finds them by
+        /// identifying the corresponding zone and calls \c find() for it.
+        ///
+        /// \param requested_types A vector of RR types for desired additional
+        ///  RRsets.
+        /// \param result A vector to which any found additional RRsets are
+        /// to be inserted.
+        void getAdditional(
+            const std::vector<isc::dns::RRType>& requested_types,
+            std::vector<isc::dns::ConstRRsetPtr>& result)
+        {
+            // Perform common checks, and delegate the process to the default
+            // or specialized implementation.
+            if (code != SUCCESS && code != DELEGATION) {
+                return;
+            }
+
+            getAdditionalImpl(requested_types, result);
+        }
+
+    protected:
+        /// \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.
+        virtual void getAdditionalImpl(
+            const std::vector<isc::dns::RRType>& requested_types,
+            std::vector<isc::dns::ConstRRsetPtr>& result);
+
+    private:
+        ZoneFinder& finder_;
+        const FindResultFlags flags_;
+        const FindOptions options_;
+        std::vector<isc::dns::ConstRRsetPtr> all_set_;
     };
 
     ///
@@ -217,10 +356,10 @@ public:
     ///   the code of \c DNAME and that DNAME RR.
     ///
     /// No RRset will be returned in the \c NXDOMAIN and \c NXRRSET cases
-    /// (\c rrset member of \c FindResult will be NULL), unless DNSSEC data
+    /// (\c rrset member of \c FindContext will be NULL), unless DNSSEC data
     /// are required.  See below for the cases with DNSSEC.
     ///
-    /// The returned \c FindResult object can also provide supplemental
+    /// The returned \c FindContext object can also provide supplemental
     /// information about the search result via its methods returning a
     /// boolean value.  Such information may be useful for the caller if
     /// the caller wants to collect additional DNSSEC proofs based on the
@@ -276,7 +415,7 @@ public:
     /// returned from this method.
     ///
     /// In case it's signed with NSEC, this method will possibly return
-    /// a related NSEC RRset in the \c rrset member of \c FindResult.
+    /// a related NSEC RRset in the \c rrset member of \c FindContext.
     /// What kind of NSEC is returned depends on the result code
     /// (\c NXDOMAIN or \c NXRRSET) and on whether it's a wildcard match:
     ///
@@ -333,10 +472,12 @@ public:
     /// \endcode
     /// a call to \c find() for "y.b.example.org" with FIND_DNSSEC will
     /// result in NXRRSET and this NSEC; \c isWildcard() on the returned
-    /// \c FindResult object will return true.
+    /// \c FindContext object will return true.
     ///
     /// \exception std::bad_alloc Memory allocation such as for constructing
     ///  the resulting RRset fails
+    /// \throw OutOfZone The Name \c name is outside of the origin of the
+    /// zone of this ZoneFinder.
     /// \exception DataSourceError Derived class specific exception, e.g.
     /// when encountering a bad zone configuration or database connection
     /// failure.  Although these are considered rare, exceptional events,
@@ -348,11 +489,12 @@ public:
     /// \param name The domain name to be searched for.
     /// \param type The RR type to be searched for.
     /// \param options The search options.
-    /// \return A \c FindResult object enclosing the search result (see above).
-    virtual FindResult find(const isc::dns::Name& name,
-                            const isc::dns::RRType& type,
-                            const FindOptions options
-                            = FIND_DEFAULT) = 0;
+    /// \return A \c FindContext object enclosing the search result
+    ///         (see above).
+    virtual boost::shared_ptr<Context> find(const isc::dns::Name& name,
+                                            const isc::dns::RRType& type,
+                                            const FindOptions options
+                                            = FIND_DEFAULT) = 0;
 
     ///
     /// \brief Finds all RRsets in the given name.
@@ -370,13 +512,14 @@ public:
     /// \param target the successfull result is returned through this
     /// \param options \see find, parameter options
     /// \return \see find and it's result
-    virtual FindResult findAll(const isc::dns::Name& name,
-                               std::vector<isc::dns::ConstRRsetPtr> &target,
-                               const FindOptions options = FIND_DEFAULT) = 0;
+    virtual boost::shared_ptr<Context> findAll(
+        const isc::dns::Name& name,
+        std::vector<isc::dns::ConstRRsetPtr> &target,
+        const FindOptions options = FIND_DEFAULT) = 0;
 
     /// A helper structure to represent the search result of \c findNSEC3().
     ///
-    /// The idea is similar to that of \c FindResult, but \c findNSEC3() has
+    /// The idea is similar to that of \c FindContext, but \c findNSEC3() has
     /// special interface and semantics, we use a different structure to
     /// represent the result.
     struct FindNSEC3Result {
@@ -458,7 +601,7 @@ public:
     /// algorithm, and salt) from the zone as noted above.  If these
     /// assumptions aren't met, \c DataSourceError exception will be thrown.
     ///
-    /// \exception InvalidParameter name is not a subdomain of the zone origin
+    /// \exception OutOfZone name is not a subdomain of the zone origin
     /// \exception DataSourceError Low-level or internal datasource errors
     /// happened, or the zone isn't properly signed with NSEC3
     /// (NSEC3 parameters cannot be found, no NSEC3s are available, etc).
@@ -530,9 +673,16 @@ inline ZoneFinder::FindResultFlags operator |(
 /// \brief A pointer-like type pointing to a \c ZoneFinder object.
 typedef boost::shared_ptr<ZoneFinder> ZoneFinderPtr;
 
-/// \brief A pointer-like type pointing to a \c ZoneFinder object.
+/// \brief A pointer-like type pointing to an immutable \c ZoneFinder object.
 typedef boost::shared_ptr<const ZoneFinder> ConstZoneFinderPtr;
 
+/// \brief A pointer-like type pointing to a \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ZoneFinderContextPtr;
+
+/// \brief A pointer-like type pointing to an immutable
+/// \c ZoneFinder::Context object.
+typedef boost::shared_ptr<ZoneFinder::Context> ConstZoneFinderContextPtr;
+
 /// The base class to make updates to a single zone.
 ///
 /// On construction, each derived class object will start a "transaction"
diff --git a/src/lib/datasrc/zone_finder_context.cc b/src/lib/datasrc/zone_finder_context.cc
new file mode 100644
index 0000000..7913d71
--- /dev/null
+++ b/src/lib/datasrc/zone_finder_context.cc
@@ -0,0 +1,102 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrtype.h>
+#include <dns/rdataclass.h>
+
+#include <datasrc/zone.h>
+
+#include <boost/foreach.hpp>
+
+#include <vector>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace datasrc {
+
+namespace {
+void
+getAdditionalAddrs(ZoneFinder& finder, const Name& name,
+                   const vector<RRType>& requested_types,
+                   vector<ConstRRsetPtr>& result_rrsets,
+                   ZoneFinder::FindOptions options)
+{
+    // Ignore out-of-zone names
+    const NameComparisonResult cmp = finder.getOrigin().compare(name);
+    if ((cmp.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
+        (cmp.getRelation() != NameComparisonResult::EQUAL)) {
+        return;
+    }
+
+    BOOST_FOREACH(RRType rrtype, requested_types) {
+        ConstZoneFinderContextPtr ctx = finder.find(name, rrtype, options);
+        if (ctx->code == ZoneFinder::SUCCESS) {
+            result_rrsets.push_back(ctx->rrset);
+        }
+    }
+}
+
+void
+getAdditionalForRRset(ZoneFinder& finder, const AbstractRRset& rrset,
+                      const vector<RRType>& requested_types,
+                      vector<ConstRRsetPtr>& result,
+                      ZoneFinder::FindOptions orig_options)
+{
+    RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
+    ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
+    if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
+        options = options | ZoneFinder::FIND_DNSSEC;
+    }
+
+    for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
+        const Rdata& rdata(rdata_iterator->getCurrent());
+
+        if (rrset.getType() == RRType::NS()) {
+            // Need to perform the search in the "GLUE OK" mode.
+            const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
+            getAdditionalAddrs(finder, ns.getNSName(), requested_types,
+                               result, options | ZoneFinder::FIND_GLUE_OK);
+        } else if (rrset.getType() == RRType::MX()) {
+            const generic::MX& mx = dynamic_cast<const generic::MX&>(rdata);
+            getAdditionalAddrs(finder, mx.getMXName(), requested_types,
+                               result, options);
+        }
+    }
+}
+}
+
+void
+ZoneFinder::Context::getAdditionalImpl(const vector<RRType>& requested_types,
+                                       vector<ConstRRsetPtr>& result)
+{
+    // If rrset is non NULL, it should have been SUCCESS/DELEGATION; otherwise
+    // we should have responded to type ANY query.
+    if (rrset) {
+        getAdditionalForRRset(finder_, *rrset, requested_types, result,
+                              options_);
+        return;
+    }
+    BOOST_FOREACH(ConstRRsetPtr rrset_in_set, all_set_) {
+        getAdditionalForRRset(finder_, *rrset_in_set, requested_types, result,
+                              options_);
+    }
+}
+
+} // namespace datasrc
+} // datasrc isc
diff --git a/src/lib/dhcp/dhcp6.h b/src/lib/dhcp/dhcp6.h
index 6012003..15c306d 100644
--- a/src/lib/dhcp/dhcp6.h
+++ b/src/lib/dhcp/dhcp6.h
@@ -108,6 +108,15 @@ extern const int dhcpv6_type_name_max;
 #define DUID_LLT        1
 #define DUID_EN         2
 #define DUID_LL         3
+#define DUID_UUID       4
+
+// Define hardware types
+// Taken from http://www.iana.org/assignments/arp-parameters/
+#define HWTYPE_ETHERNET    0x0001
+#define HWTYPE_INIFINIBAND 0x0020
+
+// Taken from http://www.iana.org/assignments/enterprise-numbers
+#define ENTERPRISE_ID_ISC 2495
 
 /* Offsets into IA_*'s where Option spaces commence.  */
 #define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 3aa54e8..990e987 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -23,13 +23,14 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include <exceptions/exceptions.h>
+#include <util/io/pktinfo_utilities.h>
 
 using namespace std;
-using namespace isc;
 using namespace isc::asiolink;
-using namespace isc::dhcp;
+using namespace isc::util::io::internal;
 
 namespace isc {
+namespace dhcp {
 
 /// IfaceMgr is a singleton implementation
 IfaceMgr* IfaceMgr::instance_ = 0;
@@ -141,7 +142,7 @@ IfaceMgr::IfaceMgr()
         // interface detection is implemented. Otherwise
         // it is not possible to run tests in a portable
         // way (see detectIfaces() method).
-        throw ex;
+        throw;
     }
 }
 
@@ -166,12 +167,12 @@ IfaceMgr::~IfaceMgr() {
     closeSockets();
 }
 
-void
-IfaceMgr::stubDetectIfaces() {
+void IfaceMgr::stubDetectIfaces() {
     string ifaceName, linkLocal;
 
-    // TODO do the actual detection. Currently interface detection is faked
-    //      by reading a text file.
+    // This is a stub implementation for interface detection. Actual detection
+    // is faked by reading a text file. It will eventually be removed once
+    // we have actual implementations for all supported systems.
 
     cout << "Interface detection is not implemented yet. "
          << "Reading interfaces.txt file instead." << endl;
@@ -189,7 +190,13 @@ IfaceMgr::stubDetectIfaces() {
 
         cout << "Detected interface " << ifaceName << "/" << linkLocal << endl;
 
-        Iface iface(ifaceName, if_nametoindex( ifaceName.c_str() ) );
+        Iface iface(ifaceName, if_nametoindex(ifaceName.c_str()));
+        iface.flag_up_ = true;
+        iface.flag_running_ = true;
+        iface.flag_loopback_ = false;
+        iface.flag_multicast_ = true;
+        iface.flag_broadcast_ = true;
+        iface.setHWType(HWTYPE_ETHERNET);
         IOAddress addr(linkLocal);
         iface.addAddress(addr);
         addInterface(iface);
@@ -202,17 +209,20 @@ IfaceMgr::stubDetectIfaces() {
 
         // TODO Do LOG_FATAL here
         std::cerr << "Interface detection failed." << std::endl;
-        throw ex;
+        throw;
     }
 }
 
+/// @todo: Remove this once we have OS-specific interface detection
+/// routines (or at least OS-specific files, like iface_mgr_solaris.cc)
+/// for all OSes.
 #if !defined(OS_LINUX) && !defined(OS_BSD)
 void IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 #endif
 
-bool IfaceMgr::openSockets4(uint16_t port) {
+bool IfaceMgr::openSockets4(const uint16_t port) {
     int sock;
     int count = 0;
 
@@ -252,7 +262,7 @@ bool IfaceMgr::openSockets4(uint16_t port) {
 
 }
 
-bool IfaceMgr::openSockets6(uint16_t port) {
+bool IfaceMgr::openSockets6(const uint16_t port) {
     int sock;
     int count = 0;
 
@@ -283,6 +293,9 @@ bool IfaceMgr::openSockets6(uint16_t port) {
                 return (false);
             }
 
+            // Binding socket to unicast address and then joining multicast group
+            // works well on Mac OS (and possibly other BSDs), but does not work
+            // on Linux.
             if ( !joinMulticast(sock, iface->getName(),
                                 string(ALL_DHCP_RELAY_AGENTS_AND_SERVERS) ) ) {
                 close(sock);
@@ -291,8 +304,12 @@ bool IfaceMgr::openSockets6(uint16_t port) {
             }
 
             count++;
+
+            /// @todo: Remove this ifdef once we start supporting BSD systems.
 #if defined(OS_LINUX)
-            // this doesn't work too well on NetBSD
+            // To receive multicast traffic, Linux requires binding socket to
+            // a multicast group. That in turn doesn't work on NetBSD.
+
             int sock2 = openSocket(iface->getName(),
                                    IOAddress(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
                                    port);
@@ -359,9 +376,8 @@ IfaceMgr::getIface(const std::string& ifname) {
     return (NULL); // not found
 }
 
-int IfaceMgr::openSocket(const std::string& ifname,
-                     const IOAddress& addr,
-                     int port) {
+int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
+                         const uint16_t port) {
     Iface* iface = getIface(ifname);
     if (!iface) {
         isc_throw(BadValue, "There is no " << ifname << " interface present.");
@@ -377,7 +393,7 @@ int IfaceMgr::openSocket(const std::string& ifname,
     }
 }
 
-int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
+int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, uint16_t port) {
 
     cout << "Creating UDP4 socket on " << iface.getFullName()
          << " " << addr.toText() << "/port=" << port << endl;
@@ -421,7 +437,7 @@ int IfaceMgr::openSocket4(Iface& iface, const IOAddress& addr, int port) {
     return (sock);
 }
 
-int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
+int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
 
     cout << "Creating UDP6 socket on " << iface.getFullName()
          << " " << addr.toText() << "/port=" << port << endl;
@@ -448,8 +464,8 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
         isc_throw(Unexpected, "Failed to create UDP6 socket.");
     }
 
-    /* Set the REUSEADDR option so that we don't fail to start if
-       we're being restarted. */
+    // Set the REUSEADDR option so that we don't fail to start if
+    // we're being restarted.
     int flag = 1;
     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
                    (char *)&flag, sizeof(flag)) < 0) {
@@ -463,14 +479,14 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, int port) {
                   << "/port=" << port);
     }
 #ifdef IPV6_RECVPKTINFO
-    /* RFC3542 - a new way */
+    // RFC3542 - a new way
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
         isc_throw(Unexpected, "setsockopt: IPV6_RECVPKTINFO failed.");
     }
 #else
-    /* RFC2292 - an old way */
+    // RFC2292 - an old way
     if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO,
                    &flag, sizeof(flag)) != 0) {
         close(sock);
@@ -527,42 +543,49 @@ const std::string & mcast) {
 }
 
 bool
-IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
-    struct msghdr m;
-    struct iovec v;
+IfaceMgr::send(const Pkt6Ptr& pkt) {
     int result;
-    struct in6_pktinfo *pktinfo;
-    struct cmsghdr *cmsg;
 
-    Iface* iface = getIface(pkt->iface_);
+    Iface* iface = getIface(pkt->getIface());
     if (!iface) {
         isc_throw(BadValue, "Unable to send Pkt6. Invalid interface ("
-                  << pkt->iface_ << ") specified.");
+                  << pkt->getIface() << ") specified.");
     }
 
     memset(&control_buf_[0], 0, control_buf_len_);
 
-    // Initialize our message header structure.
-    memset(&m, 0, sizeof(m));
 
     // Set the target address we're sending to.
     sockaddr_in6 to;
     memset(&to, 0, sizeof(to));
     to.sin6_family = AF_INET6;
-    to.sin6_port = htons(pkt->remote_port_);
+    to.sin6_port = htons(pkt->getRemotePort());
     memcpy(&to.sin6_addr,
-           pkt->remote_addr_.getAddress().to_v6().to_bytes().data(),
+           pkt->getRemoteAddr().getAddress().to_v6().to_bytes().data(),
            16);
-    to.sin6_scope_id = pkt->ifindex_;
+    to.sin6_scope_id = pkt->getIndex();
 
+    // Initialize our message header structure.
+    struct msghdr m;
+    memset(&m, 0, sizeof(m));
     m.msg_name = &to;
     m.msg_namelen = sizeof(to);
 
     // Set the data buffer we're sending. (Using this wacky
     // "scatter-gather" stuff... we only have a single chunk
     // of data to send, so we declare a single vector entry.)
-    v.iov_base = (char *) &pkt->data_[0];
-    v.iov_len = pkt->data_len_;
+
+    // As v structure is a C-style is used for both sending and
+    // receiving data, it is shared between sending and receiving
+    // (sendmsg and recvmsg). It is also defined in system headers,
+    // so we have no control over its definition. To set iov_base
+    // (defined as void*) we must use const cast from void *.
+    // Otherwise C++ compiler would complain that we are trying
+    // to assign const void* to void*.
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
+    v.iov_len = pkt->getBuffer().getLength();
     m.msg_iov = &v;
     m.msg_iovlen = 1;
 
@@ -574,34 +597,31 @@ IfaceMgr::send(boost::shared_ptr<Pkt6>& pkt) {
     // kernel decide what that should be.
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
-    cmsg = CMSG_FIRSTHDR(&m);
+    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&m);
     cmsg->cmsg_level = IPPROTO_IPV6;
     cmsg->cmsg_type = IPV6_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
-    pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
-    memset(pktinfo, 0, sizeof(*pktinfo));
-    pktinfo->ipi6_ifindex = pkt->ifindex_;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+    struct in6_pktinfo *pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
+    memset(pktinfo, 0, sizeof(struct in6_pktinfo));
+    pktinfo->ipi6_ifindex = pkt->getIndex();
     m.msg_controllen = cmsg->cmsg_len;
 
     result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
-        cout << "Send packet failed." << endl;
+        isc_throw(Unexpected, "Pkt6 send failed: sendmsg() returned " << result);
     }
-    cout << "Sent " << pkt->data_len_ << " bytes over socket " << getSocket(*pkt)
+    cout << "Sent " << pkt->getBuffer().getLength() << " bytes over socket " << getSocket(*pkt)
          << " on " << iface->getFullName() << " interface: "
-         << " dst=" << pkt->remote_addr_.toText()
-         << ", src=" << pkt->local_addr_.toText()
+         << " dst=[" << pkt->getRemoteAddr().toText() << "]:" << pkt->getRemotePort()
+         << ", src=" << pkt->getLocalAddr().toText() << "]:" << pkt->getLocalPort()
          << endl;
 
     return (result);
 }
 
 bool
-IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
+IfaceMgr::send(const Pkt4Ptr& pkt)
 {
-    struct msghdr m;
-    struct iovec v;
-    int result;
 
     Iface* iface = getIface(pkt->getIface());
     if (!iface) {
@@ -611,8 +631,6 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
 
     memset(&control_buf_[0], 0, control_buf_len_);
 
-    // Initialize our message header structure.
-    memset(&m, 0, sizeof(m));
 
     // Set the target address we're sending to.
     sockaddr_in to;
@@ -621,45 +639,33 @@ IfaceMgr::send(boost::shared_ptr<Pkt4>& pkt)
     to.sin_port = htons(pkt->getRemotePort());
     to.sin_addr.s_addr = htonl(pkt->getRemoteAddr());
 
+    struct msghdr m;
+    // Initialize our message header structure.
+    memset(&m, 0, sizeof(m));
     m.msg_name = &to;
     m.msg_namelen = sizeof(to);
 
     // Set the data buffer we're sending. (Using this wacky
     // "scatter-gather" stuff... we only have a single chunk
     // of data to send, so we declare a single vector entry.)
-    v.iov_base = (char *) pkt->getBuffer().getData();
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    // iov_base field is of void * type. We use it for packet
+    // transmission, so this buffer will not be modified.
+    v.iov_base = const_cast<void *>(pkt->getBuffer().getData());
     v.iov_len = pkt->getBuffer().getLength();
     m.msg_iov = &v;
     m.msg_iovlen = 1;
 
-// OS_LINUX defines are part of ticket #1237
-#if defined(OS_LINUX)
-    // Setting the interface is a bit more involved.
-    //
-    // We have to create a "control message", and set that to
-    // define the IPv4 packet information. We could set the
-    // source address if we wanted, but we can safely let the
-    // kernel decide what that should be.
-    struct in_pktinfo *pktinfo;
-    struct cmsghdr *cmsg;
-    m.msg_control = &control_buf_[0];
-    m.msg_controllen = control_buf_len_;
-    cmsg = CMSG_FIRSTHDR(&m);
-    cmsg->cmsg_level = IPPROTO_IP;
-    cmsg->cmsg_type = IP_PKTINFO;
-    cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
-    pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
-    memset(pktinfo, 0, sizeof(*pktinfo));
-    pktinfo->ipi_ifindex = pkt->getIndex();
-    m.msg_controllen = cmsg->cmsg_len;
-#endif
+    // 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;
 
-    result = sendmsg(getSocket(*pkt), &m, 0);
+    int result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
         isc_throw(Unexpected, "Pkt4 send failed.");
     }
@@ -681,22 +687,12 @@ IfaceMgr::receive4() {
     IfaceCollection::const_iterator iface;
     for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
 
-#if 0
-        // uncomment this once #1237 is merged
-        // Let's skip loopback and downed interfaces.
-        if (iface->flag_loopback_ ||
-            !iface->flag_up_ ||
-            !iface->flag_running_) {
-            continue;
-        }
-#endif
-
-        SocketCollection::const_iterator s = iface->sockets_.begin();
-        while (s != iface->sockets_.end()) {
+        /// @todo: rewrite this as part of #1555
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
 
             // We don't want IPv6 addresses here.
             if (s->addr_.getFamily() != AF_INET) {
-                ++s;
                 continue;
             }
 
@@ -705,8 +701,6 @@ IfaceMgr::receive4() {
                 candidate = &(*s);
                 break;
             }
-
-            ++s;
         }
 
         if (candidate) {
@@ -723,28 +717,22 @@ IfaceMgr::receive4() {
          << iface->getFullName() << endl;
 
     // Now we have a socket, let's get some data from it!
-
-    struct msghdr m;
-    struct iovec v;
-    int result;
     struct sockaddr_in from_addr;
-    struct in_addr to_addr;
-    boost::shared_ptr<Pkt4> pkt;
-    const uint32_t RCVBUFSIZE = 1500;
-    static uint8_t buf[RCVBUFSIZE];
+    uint8_t buf[RCVBUFSIZE];
 
     memset(&control_buf_[0], 0, control_buf_len_);
     memset(&from_addr, 0, sizeof(from_addr));
-    memset(&to_addr, 0, sizeof(to_addr));
 
     // Initialize our message header structure.
+    struct msghdr m;
     memset(&m, 0, sizeof(m));
 
     // Point so we can get the from address.
     m.msg_name = &from_addr;
     m.msg_namelen = sizeof(from_addr);
 
-    v.iov_base = (void*)buf;
+    struct iovec v;
+    v.iov_base = static_cast<void*>(buf);
     v.iov_len = RCVBUFSIZE;
     m.msg_iov = &v;
     m.msg_iovlen = 1;
@@ -758,132 +746,73 @@ IfaceMgr::receive4() {
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
 
-    result = recvmsg(candidate->sockfd_, &m, 0);
-
+    int result = recvmsg(candidate->sockfd_, &m, 0);
     if (result < 0) {
         cout << "Failed to receive UDP4 data." << endl;
-        return (boost::shared_ptr<Pkt4>()); // NULL
+        return (Pkt4Ptr()); // NULL
     }
 
-    unsigned int ifindex = iface->getIndex();
+    // We have all data let's create Pkt4 object.
+    Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
 
-// OS_LINUX defines are part of ticket #1237
-#if defined(OS_LINUX)
-    struct cmsghdr* cmsg;
-    struct in_pktinfo* pktinfo;
-
-    int found_pktinfo = 0;
-    cmsg = CMSG_FIRSTHDR(&m);
-    while (cmsg != NULL) {
-        if ((cmsg->cmsg_level == IPPROTO_IP) &&
-            (cmsg->cmsg_type == IP_PKTINFO)) {
-            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
-
-            ifindex = pktinfo->ipi_ifindex;
-            to_addr = pktinfo->ipi_addr;
-
-            // This field is useful, when we are bound to unicast
-            // address e.g. 192.0.2.1 and the packet was sent to
-            // 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:
-            // to_addr = pktinfo->ipi_spec_dst;
-            found_pktinfo = 1;
-        }
-        cmsg = CMSG_NXTHDR(&m, cmsg);
-    }
-    if (!found_pktinfo) {
-        cout << "Unable to find pktinfo" << endl;
-        return (boost::shared_ptr<Pkt4>()); // NULL
-    }
-#endif
+    unsigned int ifindex = iface->getIndex();
 
-    IOAddress to(htonl(to_addr.s_addr));
     IOAddress from(htonl(from_addr.sin_addr.s_addr));
     uint16_t from_port = htons(from_addr.sin_port);
 
-    cout << "Received " << result << " bytes from " << from.toText()
-         << "/port=" << from_port
-         << " sent to " << to.toText() << " over interface "
-         << iface->getFullName() << endl;
-
-    // we have all data let's create Pkt4 object
-    pkt = boost::shared_ptr<Pkt4>(new Pkt4(buf, result));
-
-    pkt->setIface(iface->getName());
+    // Set receiving interface based on information, which socket was used to
+    // receive data. OS-specific info (see os_receive4()) may be more reliable,
+    // so this value may be overwritten.
     pkt->setIndex(ifindex);
-    pkt->setLocalAddr(to);
+    pkt->setIface(iface->getName());
     pkt->setRemoteAddr(from);
     pkt->setRemotePort(from_port);
     pkt->setLocalPort(candidate->port_);
 
+    if (!os_receive4(m, pkt)) {
+        cout << "Unable to find pktinfo" << endl;
+        return (boost::shared_ptr<Pkt4>()); // NULL
+    }
+
+    cout << "Received " << result << " bytes from " << from.toText()
+         << "/port=" << from_port
+         << " sent to " << pkt->getLocalAddr().toText() << " over interface "
+         << iface->getFullName() << endl;
+
     return (pkt);
 }
 
-boost::shared_ptr<Pkt6>
-IfaceMgr::receive6() {
-    struct msghdr m;
-    struct iovec v;
-    int result;
-    struct cmsghdr* cmsg;
-    struct in6_pktinfo* pktinfo;
-    struct sockaddr_in6 from;
-    struct in6_addr to_addr;
-    boost::shared_ptr<Pkt6> pkt;
-    char addr_str[INET6_ADDRSTRLEN];
-
-    try {
-        // RFC3315 states that server responses may be
-        // fragmented if they are over MTU. There is no
-        // text whether client's packets may be larger
-        // than 1500. Nevertheless to be on the safe side
-        // we use larger buffer. This buffer limit is checked
-        // during reception (see iov_len below), so we are
-        // safe
-        pkt = boost::shared_ptr<Pkt6>(new Pkt6(65536));
-    } catch (const std::exception& ex) {
-        cout << "Failed to create new packet." << endl;
-        return (boost::shared_ptr<Pkt6>()); // NULL
-    }
+Pkt6Ptr IfaceMgr::receive6() {
+    uint8_t buf[RCVBUFSIZE];
 
     memset(&control_buf_[0], 0, control_buf_len_);
-
+    struct sockaddr_in6 from;
     memset(&from, 0, sizeof(from));
-    memset(&to_addr, 0, sizeof(to_addr));
 
-    /*
-     * Initialize our message header structure.
-     */
+    // Initialize our message header structure.
+    struct msghdr m;
     memset(&m, 0, sizeof(m));
 
-    /*
-     * Point so we can get the from address.
-     */
+    // Point so we can get the from address.
     m.msg_name = &from;
     m.msg_namelen = sizeof(from);
 
-    /*
-     * Set the data buffer we're receiving. (Using this wacky
-     * "scatter-gather" stuff... but we that doesn't really make
-     * sense for us, so we use a single vector entry.)
-     */
-    v.iov_base = (void*)&pkt->data_[0];
-    v.iov_len = pkt->data_len_;
+    // Set the data buffer we're receiving. (Using this wacky
+    // "scatter-gather" stuff... but we that doesn't really make
+    // sense for us, so we use a single vector entry.)
+    struct iovec v;
+    memset(&v, 0, sizeof(v));
+    v.iov_base = static_cast<void*>(buf);
+    v.iov_len = RCVBUFSIZE;
     m.msg_iov = &v;
     m.msg_iovlen = 1;
 
-    /*
-     * Getting the interface is a bit more involved.
-     *
-     * We set up some space for a "control message". We have
-     * previously asked the kernel to give us packet
-     * information (when we initialized the interface), so we
-     * should get the destination address from that.
-     */
+    // Getting the interface is a bit more involved.
+    //
+    // We set up some space for a "control message". We have
+    // previously asked the kernel to give us packet
+    // information (when we initialized the interface), so we
+    // should get the destination address from that.
     m.msg_control = &control_buf_[0];
     m.msg_controllen = control_buf_len_;
 
@@ -893,10 +822,9 @@ IfaceMgr::receive6() {
     IfaceCollection::const_iterator iface = ifaces_.begin();
     const SocketInfo* candidate = 0;
     while (iface != ifaces_.end()) {
-        SocketCollection::const_iterator s = iface->sockets_.begin();
-        while (s != iface->sockets_.end()) {
+        for (SocketCollection::const_iterator s = iface->sockets_.begin();
+             s != iface->sockets_.end(); ++s) {
             if (s->addr_.getFamily() != AF_INET6) {
-                ++s;
                 continue;
             }
             if (s->addr_.getAddress().to_v6().is_multicast()) {
@@ -906,7 +834,6 @@ IfaceMgr::receive6() {
             if (!candidate) {
                 candidate = &(*s); // it's not multicast, but it's better than nothing
             }
-            ++s;
         }
         if (candidate) {
             break;
@@ -914,7 +841,7 @@ IfaceMgr::receive6() {
         ++iface;
     }
     if (iface == ifaces_.end()) {
-        isc_throw(Unexpected, "No interfaces detected. Can't receive anything.");
+        isc_throw(Unexpected, "No suitable IPv6 interfaces detected. Can't receive anything.");
     }
 
     if (!candidate) {
@@ -925,125 +852,121 @@ IfaceMgr::receive6() {
     cout << "Trying to receive over UDP6 socket " << candidate->sockfd_ << " bound to "
          << candidate->addr_.toText() << "/port=" << candidate->port_ << " on "
          << iface->getFullName() << endl;
-    result = recvmsg(candidate->sockfd_, &m, 0);
+    int result = recvmsg(candidate->sockfd_, &m, 0);
 
+    struct in6_addr to_addr;
+    memset(&to_addr, 0, sizeof(to_addr));
+
+    int ifindex = -1;
     if (result >= 0) {
-        /*
-         * If we did read successfully, then we need to loop
-         * through the control messages we received and
-         * find the one with our destination address.
-         *
-         * We also keep a flag to see if we found it. If we
-         * didn't, then we consider this to be an error.
-         */
-        int found_pktinfo = 0;
-        cmsg = CMSG_FIRSTHDR(&m);
+        struct in6_pktinfo* pktinfo = NULL;
+
+
+        // If we did read successfully, then we need to loop
+        // through the control messages we received and
+        // find the one with our destination address.
+        //
+        // We also keep a flag to see if we found it. If we
+        // didn't, then we consider this to be an error.
+        bool found_pktinfo = false;
+        struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
         while (cmsg != NULL) {
             if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
                 (cmsg->cmsg_type == IPV6_PKTINFO)) {
-                pktinfo = (struct in6_pktinfo*)CMSG_DATA(cmsg);
+                pktinfo = convertPktInfo6(CMSG_DATA(cmsg));
                 to_addr = pktinfo->ipi6_addr;
-                pkt->ifindex_ = pktinfo->ipi6_ifindex;
-                found_pktinfo = 1;
+                ifindex = pktinfo->ipi6_ifindex;
+                found_pktinfo = true;
+                break;
             }
             cmsg = CMSG_NXTHDR(&m, cmsg);
         }
         if (!found_pktinfo) {
             cout << "Unable to find pktinfo" << endl;
-            return (boost::shared_ptr<Pkt6>()); // NULL
+            return (Pkt6Ptr()); // NULL
         }
     } else {
         cout << "Failed to receive data." << endl;
-        return (boost::shared_ptr<Pkt6>()); // NULL
+        return (Pkt6Ptr()); // NULL
     }
 
-    // That's ugly.
-    // TODO add IOAddress constructor that will take struct in6_addr*
-    // TODO: there's from_bytes() method added in IOAddress. Use it!
-    inet_ntop(AF_INET6, &to_addr, addr_str,INET6_ADDRSTRLEN);
-    pkt->local_addr_ = IOAddress(string(addr_str));
-
-    // TODO: there's from_bytes() method added in IOAddress. Use it!
-    inet_ntop(AF_INET6, &from.sin6_addr, addr_str, INET6_ADDRSTRLEN);
-    pkt->remote_addr_ = IOAddress(string(addr_str));
+    // Let's create a packet.
+    Pkt6Ptr pkt;
+    try {
+        pkt = Pkt6Ptr(new Pkt6(buf, result));
+    } catch (const std::exception& ex) {
+        cout << "Failed to create new packet." << endl;
+        return (Pkt6Ptr()); // NULL
+    }
 
-    pkt->remote_port_ = ntohs(from.sin6_port);
+    pkt->setLocalAddr(IOAddress::from_bytes(AF_INET6,
+                      reinterpret_cast<const uint8_t*>(&to_addr)));
+    pkt->setRemoteAddr(IOAddress::from_bytes(AF_INET6,
+                       reinterpret_cast<const uint8_t*>(&from.sin6_addr)));
+    pkt->setRemotePort(ntohs(from.sin6_port));
+    pkt->setIndex(ifindex);
 
-    Iface* received = getIface(pkt->ifindex_);
+    Iface* received = getIface(pkt->getIndex());
     if (received) {
-        pkt->iface_ = received->getName();
+        pkt->setIface(received->getName());
     } else {
         cout << "Received packet over unknown interface (ifindex="
-             << pkt->ifindex_ << ")." << endl;
+             << pkt->getIndex() << ")." << endl;
         return (boost::shared_ptr<Pkt6>()); // NULL
     }
 
-    pkt->data_len_ = result;
-
-    // TODO Move this to LOG_DEBUG
-    cout << "Received " << pkt->data_len_ << " bytes over "
-         << pkt->iface_ << "/" << pkt->ifindex_ << " interface: "
-         << " src=" << pkt->remote_addr_.toText()
-         << ", dst=" << pkt->local_addr_.toText()
+    /// @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);
 }
 
-uint16_t
-IfaceMgr::getSocket(isc::dhcp::Pkt6 const& pkt) {
-    Iface* iface = getIface(pkt.iface_);
-    if (!iface) {
+uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
+    Iface* iface = getIface(pkt.getIface());
+    if (iface == NULL) {
         isc_throw(BadValue, "Tried to find socket for non-existent interface "
-                  << pkt.iface_);
+                  << pkt.getIface());
     }
 
     SocketCollection::const_iterator s;
     for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
-        if (s->family_ != AF_INET6) {
-            // don't use IPv4 sockets
-            continue;
-        }
-        if (s->addr_.getAddress().to_v6().is_multicast()) {
-            // don't use IPv6 sockets bound to multicast address
-            continue;
+        if ((s->family_ == AF_INET6) &&
+            (!s->addr_.getAddress().to_v6().is_multicast())) {
+            return (s->sockfd_);
         }
-        /// TODO: Add more checks here later. If remote address is
+        /// @todo: Add more checks here later. If remote address is
         /// not link-local, we can't use link local bound socket
         /// to send data.
-
-        return (s->sockfd_);
     }
 
     isc_throw(Unexpected, "Interface " << iface->getFullName()
               << " does not have any suitable IPv6 sockets open.");
 }
 
-uint16_t
-IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
+uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
     Iface* iface = getIface(pkt.getIface());
-    if (!iface) {
+    if (iface == NULL) {
         isc_throw(BadValue, "Tried to find socket for non-existent interface "
                   << pkt.getIface());
     }
 
     SocketCollection::const_iterator s;
     for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
-        if (s->family_ != AF_INET) {
-            // don't use IPv4 sockets
-            continue;
+        if (s->family_ == AF_INET) {
+            return (s->sockfd_);
         }
         /// TODO: Add more checks here later. If remote address is
         /// not link-local, we can't use link local bound socket
         /// to send data.
-
-        return (s->sockfd_);
     }
 
     isc_throw(Unexpected, "Interface " << iface->getFullName()
               << " does not have any suitable IPv4 sockets open.");
 }
 
-
-
-}
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/iface_mgr.h b/src/lib/dhcp/iface_mgr.h
index 842915a..b09a1ac 100644
--- a/src/lib/dhcp/iface_mgr.h
+++ b/src/lib/dhcp/iface_mgr.h
@@ -42,6 +42,15 @@ public:
     /// maximum MAC address length (Infiniband uses 20 bytes)
     static const unsigned int MAX_MAC_LEN = 20;
 
+    /// @brief Packet reception buffer size
+    ///
+    /// RFC3315 states that server responses may be
+    /// fragmented if they are over MTU. There is no
+    /// text whether client's packets may be larger
+    /// than 1500. For now, we can assume that
+    /// we don't support packets larger than 1500.
+    static const uint32_t RCVBUFSIZE = 1500;
+
     /// Holds information about socket.
     struct SocketInfo {
         uint16_t sockfd_; /// socket descriptor
@@ -167,7 +176,7 @@ public:
 
         /// @brief Adds socket descriptor to an interface.
         ///
-        /// @param socket SocketInfo structure that describes socket.
+        /// @param sock SocketInfo structure that describes socket.
         void addSocket(const SocketInfo& sock)
             { sockets_.push_back(sock); }
 
@@ -176,7 +185,7 @@ public:
         /// Closes socket and removes corresponding SocketInfo structure
         /// from an interface.
         ///
-        /// @param socket descriptor to be closed/removed.
+        /// @param sockfd socket descriptor to be closed/removed.
         /// @return true if there was such socket, false otherwise
         bool delSocket(uint16_t sockfd);
 
@@ -260,6 +269,15 @@ public:
     Iface*
     getIface(const std::string& ifname);
 
+    /// @brief Returns container with all interfaces.
+    ///
+    /// This reference is only valid as long as IfaceMgr is valid. However,
+    /// since IfaceMgr is a singleton and is expected to be destroyed after
+    /// main() function completes, you should not worry much about this.
+    ///
+    /// @return container with all interfaces.
+    const IfaceCollection& getIfaces() { return ifaces_; }
+
     /// @brief Return most suitable socket for transmitting specified IPv6 packet.
     ///
     /// This method takes Pkt6 (see overloaded implementation that takes
@@ -301,7 +319,7 @@ public:
     /// @param pkt packet to be sent
     ///
     /// @return true if sending was successful
-    bool send(boost::shared_ptr<Pkt6>& pkt);
+    bool send(const Pkt6Ptr& pkt);
 
     /// @brief Sends an IPv4 packet.
     ///
@@ -312,7 +330,7 @@ public:
     /// @param pkt a packet to be sent
     ///
     /// @return true if sending was successful
-    bool send(boost::shared_ptr<Pkt4>& pkt);
+    bool send(const Pkt4Ptr& pkt);
 
     /// @brief Tries to receive IPv6 packet over open IPv6 sockets.
     ///
@@ -325,7 +343,7 @@ public:
     /// (e.g. remove expired leases)
     ///
     /// @return Pkt6 object representing received packet (or NULL)
-    boost::shared_ptr<Pkt6> receive6();
+    Pkt6Ptr receive6();
 
     /// @brief Tries to receive IPv4 packet over open IPv4 sockets.
     ///
@@ -338,7 +356,7 @@ public:
     /// (e.g. remove expired leases)
     ///
     /// @return Pkt4 object representing received packet (or NULL)
-    boost::shared_ptr<Pkt4> receive4();
+    Pkt4Ptr receive4();
 
     /// Opens UDP/IP socket and binds it to address, interface and port.
     ///
@@ -355,7 +373,7 @@ public:
     /// @return socket descriptor, if socket creation, binding and multicast
     /// group join were all successful.
     int openSocket(const std::string& ifname,
-                   const isc::asiolink::IOAddress& addr, int port);
+                   const isc::asiolink::IOAddress& addr, const uint16_t port);
 
     /// Opens IPv6 sockets on detected interfaces.
     ///
@@ -364,7 +382,7 @@ public:
     /// @param port specifies port number (usually DHCP6_SERVER_PORT)
     ///
     /// @return true if any sockets were open
-    bool openSockets6(uint16_t port = DHCP6_SERVER_PORT);
+    bool openSockets6(const uint16_t port = DHCP6_SERVER_PORT);
 
     /// @brief Closes all open sockets.
     /// Is used in destructor, but also from Dhcpv4_srv and Dhcpv6_srv classes.
@@ -376,7 +394,7 @@ public:
     /// @param port specifies port number (usually DHCP4_SERVER_PORT)
     ///
     /// @return true if any sockets were open
-    bool openSockets4(uint16_t port = DHCP4_SERVER_PORT);
+    bool openSockets4(const uint16_t port = DHCP4_SERVER_PORT);
 
     /// @brief returns number of detected interfaces
     ///
@@ -405,7 +423,7 @@ protected:
     /// @param port a port that created socket should be bound to
     ///
     /// @return socket descriptor
-    int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+    int openSocket4(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
 
     /// @brief Opens IPv6 socket.
     ///
@@ -418,7 +436,7 @@ protected:
     /// @param port a port that created socket should be bound to
     ///
     /// @return socket descriptor
-    int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, int port);
+    int openSocket6(Iface& iface, const isc::asiolink::IOAddress& addr, uint16_t port);
 
     /// @brief Adds an interface to list of known interfaces.
     ///
@@ -464,14 +482,32 @@ protected:
     // to people who try to use multicast as source address.
 
     /// length of the control_buf_ array
-    int control_buf_len_;
+    size_t control_buf_len_;
 
     /// control-buffer, used in transmission and reception
     boost::scoped_array<char> control_buf_;
 
+
+    /// @brief A wrapper for OS-specific operations before sending IPv4 packet
+    ///
+    /// @param m message header (will be later used for sendmsg() call)
+    /// @param control_buf buffer to be used during transmission
+    /// @param control_buf_len buffer length
+    /// @param pkt packet to be sent
+    void os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
+                  size_t control_buf_len, const Pkt4Ptr& pkt);
+
+    /// @brief OS-specific operations during IPv4 packet reception
+    ///
+    /// @param m message header (was used during recvmsg() call)
+    /// @param pkt packet received (some fields will be set here)
+    ///
+    /// @return true if successful, false otherwise
+    bool os_receive4(struct msghdr& m, Pkt4Ptr& pkt);
+
 private:
 
-    /// creates a single instance of this class (a singleton implementation)
+    /// @brief Creates a single instance of this class (a singleton implementation)
     static void
     instanceCreate();
 
diff --git a/src/lib/dhcp/iface_mgr_bsd.cc b/src/lib/dhcp/iface_mgr_bsd.cc
index 7786b92..8e56df0 100644
--- a/src/lib/dhcp/iface_mgr_bsd.cc
+++ b/src/lib/dhcp/iface_mgr_bsd.cc
@@ -33,6 +33,15 @@ IfaceMgr::detectIfaces() {
     stubDetectIfaces();
 }
 
+void IfaceMgr::os_send4_setup(struct msghdr& ,
+                              boost::scoped_array<char>& ,
+                              size_t , const Pkt4Ptr& ) {
+    // do nothing here. There's nothing BSD specific to do and os_send4_setup()
+    // interface is there only to not mix Linux-specific code in common
+    // IfaceMgr file.
+}
+
+
 }
 
 #endif
diff --git a/src/lib/dhcp/iface_mgr_linux.cc b/src/lib/dhcp/iface_mgr_linux.cc
index d2f7ff4..3faac02 100644
--- a/src/lib/dhcp/iface_mgr_linux.cc
+++ b/src/lib/dhcp/iface_mgr_linux.cc
@@ -30,6 +30,11 @@
 #include <config.h>
 
 #if defined(OS_LINUX)
+
+#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
+
+#include <stdint.h>
 #include <net/if.h>
 #include <linux/rtnetlink.h>
 #include <boost/array.hpp>
@@ -94,6 +99,7 @@ public:
         rtnl_close_socket();
     }
 
+
     void rtnl_open_socket();
     void rtnl_send_request(int family, int type);
     void rtnl_store_reply(NetlinkMessages& storage, const nlmsghdr* msg);
@@ -510,8 +516,62 @@ void IfaceMgr::Iface::setFlags(uint32_t flags) {
     flag_broadcast_ = flags & IFF_BROADCAST;
 }
 
-} // end of isc::dhcp namespace
+void IfaceMgr::os_send4(struct msghdr& m, boost::scoped_array<char>& control_buf,
+                        size_t control_buf_len, const Pkt4Ptr& pkt) {
+
+    // Setting the interface is a bit more involved.
+    //
+    // We have to create a "control message", and set that to
+    // define the IPv4 packet information. We could set the
+    // source address if we wanted, but we can safely let the
+    // kernel decide what that should be.
+    m.msg_control = &control_buf[0];
+    m.msg_controllen = control_buf_len;
+    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&m);
+    cmsg->cmsg_level = IPPROTO_IP;
+    cmsg->cmsg_type = IP_PKTINFO;
+    cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+    struct in_pktinfo* pktinfo =(struct in_pktinfo *)CMSG_DATA(cmsg);
+    memset(pktinfo, 0, sizeof(struct in_pktinfo));
+    pktinfo->ipi_ifindex = pkt->getIndex();
+    m.msg_controllen = cmsg->cmsg_len;
+}
+
+bool IfaceMgr::os_receive4(struct msghdr& m, Pkt4Ptr& pkt) {
+    struct cmsghdr* cmsg;
+    struct in_pktinfo* pktinfo;
+    struct in_addr to_addr;
+
+    memset(&to_addr, 0, sizeof(to_addr));
+
+    cmsg = CMSG_FIRSTHDR(&m);
+    while (cmsg != NULL) {
+        if ((cmsg->cmsg_level == IPPROTO_IP) &&
+            (cmsg->cmsg_type == IP_PKTINFO)) {
+            pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
 
+            pkt->setIndex(pktinfo->ipi_ifindex);
+            pkt->setLocalAddr(IOAddress(htonl(pktinfo->ipi_addr.s_addr)));
+            return (true);
+
+            // This field is useful, when we are bound to unicast
+            // address e.g. 192.0.2.1 and the packet was sent to
+            // 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:
+            // to_addr = pktinfo->ipi_spec_dst;
+        }
+        cmsg = CMSG_NXTHDR(&m, cmsg);
+    }
+
+    return (false);
+}
+
+} // end of isc::dhcp namespace
 } // end of isc namespace
 
 #endif // if defined(LINUX)
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 801c136..c054a4b 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -28,153 +28,159 @@ using namespace isc::dhcp;
 using namespace isc::util;
 
 // static array with factories for options
+std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
+
+// static array with factories for options
 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
 
-unsigned int
-LibDHCP::unpackOptions6(const boost::shared_array<uint8_t> buf,
-                        unsigned int buf_len,
-                        unsigned int offset, unsigned int parse_len,
-                        isc::dhcp::Option::OptionCollection& options) {
-    if (offset + parse_len > buf_len) {
-        isc_throw(OutOfRange, "Option parse failed. Tried to parse "
-                  << parse_len << " bytes at offset " << offset
-                  << ":  out of buffer");
-    }
-    unsigned int end = offset + parse_len;
+
+size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
+                               isc::dhcp::Option::OptionCollection& options) {
+    size_t offset = 0;
+    size_t end = buf.size();
 
     while (offset +4 <= end) {
-        uint16_t opt_type = buf[offset]*256 + buf[offset+1];
+        uint16_t opt_type = buf[offset] * 256 + buf[offset + 1];
         offset += 2;
-        uint16_t opt_len = buf[offset]*256 + buf[offset+1];
+        uint16_t opt_len = buf[offset] * 256 + buf[offset + 1];
         offset += 2;
 
-        if (offset + opt_len > end ) {
+        if (offset + opt_len > end) {
             cout << "Option " << opt_type << " truncated." << endl;
             return (offset);
         }
-        boost::shared_ptr<Option> opt;
+        OptionPtr opt;
         switch (opt_type) {
         case D6O_IA_NA:
         case D6O_IA_PD:
             // cout << "Creating Option6IA" << endl;
-            opt = boost::shared_ptr<Option>(new Option6IA(opt_type,
-                                                          buf, buf_len,
-                                                          offset,
-                                                          opt_len));
+            opt = OptionPtr(new Option6IA(opt_type,
+                                          buf.begin() + offset,
+                                          buf.begin() + offset + opt_len));
             break;
         case D6O_IAADDR:
             // cout << "Creating Option6IAAddr" << endl;
-            opt = boost::shared_ptr<Option>(new Option6IAAddr(opt_type,
-                                                              buf, buf_len,
-                                                              offset, opt_len));
+            opt = OptionPtr(new Option6IAAddr(opt_type,
+                                              buf.begin() + offset,
+                                              buf.begin() + offset + opt_len));
             break;
         default:
             // cout << "Creating Option" << endl;
-            opt = boost::shared_ptr<Option>(new Option(Option::V6,
-                                                       opt_type,
-                                                       buf,
-                                                       offset,
-                                                       opt_len));
+            opt = OptionPtr(new Option(Option::V6,
+                                       opt_type,
+                                       buf.begin() + offset,
+                                       buf.begin() + offset + opt_len));
             break;
         }
         // add option to options
-        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+        options.insert(std::make_pair(opt_type, opt));
         offset += opt_len;
     }
 
     return (offset);
 }
 
-void
-LibDHCP::unpackOptions4(const std::vector<uint8_t>& buf,
-                        isc::dhcp::Option::OptionCollection& options) {
+size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
+                                 isc::dhcp::Option::OptionCollection& options) {
     size_t offset = 0;
 
-    // 2 - header of DHCPv4 option
+    // 2 byte - header of DHCPv4 option
     while (offset + 1 <= buf.size()) {
         uint8_t opt_type = buf[offset++];
 
+        // DHO_END is a special, one octet long option
         if (opt_type == DHO_END)
-          return; // just return. Don't need to add DHO_END option
+            return (offset); // just return. Don't need to add DHO_END option
+
+        // DHO_PAD is just a padding after DHO_END. Let's continue parsing
+        // in case we receive a message without DHO_END.
+        if (opt_type == DHO_PAD)
+            continue;
 
         if (offset + 1 >= buf.size()) {
-          isc_throw(OutOfRange, "Attempt to parse truncated option "
-                    << opt_type);
+            isc_throw(OutOfRange, "Attempt to parse truncated option "
+                      << opt_type);
         }
 
         uint8_t opt_len =  buf[offset++];
-        if (offset + opt_len > buf.size() ) {
+        if (offset + opt_len > buf.size()) {
             isc_throw(OutOfRange, "Option parse failed. Tried to parse "
                       << offset + opt_len << " bytes from " << buf.size()
                       << "-byte long buffer.");
         }
 
-        boost::shared_ptr<Option> opt;
+        OptionPtr opt;
         switch(opt_type) {
         default:
-            opt = boost::shared_ptr<Option>(new Option(Option::V4, opt_type,
-                                                       buf.begin()+offset,
-                                                       buf.begin()+offset+opt_len));
+            opt = OptionPtr(new Option(Option::V4, opt_type,
+                                       buf.begin()+offset,
+                                       buf.begin()+offset+opt_len));
         }
 
-        options.insert(pair<int, boost::shared_ptr<Option> >(opt_type, opt));
+        options.insert(std::make_pair(opt_type, opt));
         offset += opt_len;
     }
+    return (offset);
 }
 
-unsigned int
-LibDHCP::packOptions6(boost::shared_array<uint8_t> data,
-                      unsigned int data_len,
-                      unsigned int offset,
-                      const isc::dhcp::Option::OptionCollection& options) {
+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) {
-            unsigned short opt_len = (*it).second->len();
-            if (offset + opt_len > data_len) {
-                isc_throw(OutOfRange, "Failed to build option " <<
-                          (*it).first << ": out of buffer");
-            }
-            offset = it->second->pack(data, data_len, offset);
+             it != options.end(); ++it) {
+            it->second->pack(buf);
         }
     }
     catch (const Exception&) {
         cout << "Packet build failed (Option build failed)." << endl;
         throw;
     }
-    return (offset);
 }
 
 void
 LibDHCP::packOptions(isc::util::OutputBuffer& buf,
                      const Option::OptionCollection& options) {
     for (Option::OptionCollection::const_iterator it = options.begin();
-         it != options.end();
-         ++it) {
+         it != options.end(); ++it) {
         it->second->pack4(buf);
     }
 }
 
-
-bool
-LibDHCP::OptionFactoryRegister(Option::Universe u,
-                               unsigned short opt_type,
-                               Option::Factory * factory) {
+void LibDHCP::OptionFactoryRegister(Option::Universe u,
+                                    uint16_t opt_type,
+                                    Option::Factory* factory) {
     switch (u) {
     case Option::V6: {
-        if (v6factories_.find(opt_type)!=v6factories_.end()) {
+        if (v6factories_.find(opt_type) != v6factories_.end()) {
             isc_throw(BadValue, "There is already DHCPv6 factory registered "
                      << "for option type "  << opt_type);
         }
         v6factories_[opt_type]=factory;
-        return true;
+        return;
     }
     case Option::V4:
-    default:{
-        isc_throw(BadValue, "This universe type is not supported yet.");
-        return false; // never happens
+    {
+        // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
+        // instantiated as an Option object, but rather consumed during packet parsing.
+        if (opt_type == 0) {
+            isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
+        }
+        // Option 255 is never instantiated as an option object. It is special
+        // (a one-octet equal 255) option that is added at the end of all options
+        // during packet assembly. It is also silently consumed during packet parsing.
+        if (opt_type > 254) {
+            isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
+        }
+        if (v4factories_.find(opt_type)!=v4factories_.end()) {
+            isc_throw(BadValue, "There is already DHCPv4 factory registered "
+                     << "for option type "  << opt_type);
+        }
+        v4factories_[opt_type]=factory;
+        return;
     }
+    default:
+        isc_throw(BadValue, "Invalid universe type specified.");
     }
 
+    return;
 }
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index 468e6bb..c7935c8 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -29,18 +29,10 @@ public:
     ///
     /// Builds raw (on-wire) data for provided collection of options.
     ///
-    /// @param buf shared pointer to buffer. Data will be stored there.
-    /// @param buf_len buffer length. Used for buffer overflow protection.
-    /// @param offset Offset from beginning of the buffer, where store options
+    /// @param buf output buffer (assembled options will be stored here)
     /// @param options collection of options to store to
-    ///
-    /// @return offset to the first unused byte in buffer (next one after last
-    ///         used byte)
-    ///
-    static unsigned int
-    packOptions6(boost::shared_array<uint8_t> buf, unsigned int buf_len,
-                 unsigned int offset,
-                 const isc::dhcp::Option::OptionCollection& options);
+    static void packOptions6(isc::util::OutputBuffer& buf,
+                             const isc::dhcp::Option::OptionCollection& options);
 
 
     /// @brief Stores options in a buffer.
@@ -52,48 +44,49 @@ public:
     /// may be different reasons (option too large, option malformed,
     /// too many options etc.)
     ///
-    /// @param buf
-    /// @param options
-    static void
-    packOptions(isc::util::OutputBuffer& buf,
-                const isc::dhcp::Option::OptionCollection& options);
+    /// @param buf output buffer (assembled options will be stored here)
+    /// @param options collection of options to store to
+    static void packOptions(isc::util::OutputBuffer& buf,
+                            const isc::dhcp::Option::OptionCollection& options);
 
-    static void
-    unpackOptions4(const std::vector<uint8_t>& buf,
-                   isc::dhcp::Option::OptionCollection& options);
-    ///
-    /// Parses provided buffer and creates Option objects.
+    /// @brief Parses provided buffer as DHCPv4 options and creates Option objects.
     ///
-    /// Parses provided buf array and stores created Option objects
+    /// Parses provided buffer and stores created Option objects
     /// in options container.
     ///
     /// @param buf Buffer to be parsed.
-    /// @param offset Specifies offset for the first option.
     /// @param options Reference to option container. Options will be
     ///        put here.
+    static size_t unpackOptions4(const OptionBuffer& buf,
+                                 isc::dhcp::Option::OptionCollection& options);
+
+    /// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
     ///
-    /// @return offset to first byte after last parsed option
+    /// Parses provided buffer and stores created Option objects
+    /// in options container.
     ///
-    static unsigned int
-    unpackOptions6(const boost::shared_array<uint8_t> buf, unsigned int buf_len,
-                   unsigned int offset, unsigned int parse_len,
-                   isc::dhcp::Option::OptionCollection& options_);
+    /// @param buf Buffer to be parsed.
+    /// @param options Reference to option container. Options will be
+    ///        put here.
+    static size_t unpackOptions6(const OptionBuffer& buf,
+                                 isc::dhcp::Option::OptionCollection& options);
 
-    ///
     /// Registers factory method that produces options of specific option types.
     ///
+    /// @exception BadValue if provided type is already registered, has too large
+    ///            value or invalid universe is specified
+    ///
     /// @param u universe of the option (V4 or V6)
-    /// @param opt_type option-type
+    /// @param type option-type
     /// @param factory function pointer
-    ///
-    /// @return true, if registration was successful, false otherwise
-    ///
-    static bool
-    OptionFactoryRegister(Option::Universe u,
-                          unsigned short type,
-                          Option::Factory * factory);
+    static void OptionFactoryRegister(Option::Universe u,
+                                      uint16_t type,
+                                      Option::Factory * factory);
 protected:
-    // pointers to factories that produce DHCPv6 options
+    /// pointers to factories that produce DHCPv6 options
+    static std::map<unsigned short, Option::Factory*> v4factories_;
+
+    /// pointers to factories that produce DHCPv6 options
     static std::map<unsigned short, Option::Factory*> v6factories_;
 };
 
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 23de315..03b4a3d 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -17,7 +17,6 @@
 #include <arpa/inet.h>
 #include <sstream>
 #include <iomanip>
-#include <boost/shared_array.hpp>
 #include "exceptions/exceptions.h"
 #include "util/io_utilities.h"
 
@@ -25,38 +24,29 @@
 #include "dhcp/libdhcp++.h"
 
 using namespace std;
-using namespace isc::dhcp;
 using namespace isc::util;
 
-Option::Option(Universe u, unsigned short type)
+namespace isc {
+namespace dhcp {
+
+Option::Option(Universe u, uint16_t type)
     :universe_(u), type_(type) {
 
-    if ((u == V4) && (type > 255)) {
+    // END option (type 255 is forbidden as well)
+    if ((u == V4) && ((type == 0) || (type > 254))) {
         isc_throw(BadValue, "Can't create V4 option of type "
-                  << type << ", V4 options are in range 0..255");
+                  << type << ", V4 options are in range 1..254");
     }
 }
 
-Option::Option(Universe u, unsigned short type,
-               const boost::shared_array<uint8_t>& buf,
-               unsigned int offset, unsigned int len)
-    :universe_(u), type_(type),
-     offset_(offset)
-{
-    uint8_t* ptr = &buf[offset];
-    data_ = std::vector<uint8_t>(ptr, ptr + len);
-
-    check();
-}
-
-Option::Option(Universe u, unsigned short type, std::vector<uint8_t>& data)
+Option::Option(Universe u, uint16_t type, const OptionBuffer& data)
     :universe_(u), type_(type), data_(data) {
     check();
 }
 
-Option::Option(Universe u, uint16_t type, vector<uint8_t>::const_iterator first,
-               vector<uint8_t>::const_iterator last)
-    :universe_(u), type_(type), data_(std::vector<uint8_t>(first,last)) {
+Option::Option(Universe u, uint16_t type, OptionBufferConstIter first,
+               OptionBufferConstIter last)
+    :universe_(u), type_(type), data_(OptionBuffer(first,last)) {
     check();
 }
 
@@ -84,22 +74,23 @@ Option::check() {
     // both types and data size.
 }
 
-unsigned int
-Option::pack(boost::shared_array<uint8_t>& buf,
-             unsigned int buf_len,
-             unsigned int offset) {
-    if (universe_ != V6) {
+void Option::pack(isc::util::OutputBuffer& buf) {
+    switch (universe_) {
+    case V6:
+        return (pack6(buf));
+    case V4:
+        return (pack4(buf));
+    default:
         isc_throw(BadValue, "Failed to pack " << type_ << " option. Do not "
                   << "use this method for options other than DHCPv6.");
     }
-    return pack6(buf, buf_len, offset);
 }
 
 void
 Option::pack4(isc::util::OutputBuffer& buf) {
     switch (universe_) {
     case V4: {
-        if (data_.size() > 255) {
+        if (len() > 255) {
             isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big."
                       << "At most 255 bytes are supported.");
             /// TODO Larger options can be stored as separate instances
@@ -127,84 +118,25 @@ Option::pack4(isc::util::OutputBuffer& buf) {
     }
 }
 
-unsigned int
-Option::pack6(boost::shared_array<uint8_t>& buf,
-             unsigned int buf_len,
-             unsigned int offset) {
-    if (offset+len() > buf_len) {
-        isc_throw(OutOfRange, "Failed to pack v6 option=" <<
-                  type_ << ",len=" << len() << ": too small buffer.");
-    }
-
-    uint8_t* ptr = &buf[offset];
-
-    ptr = writeUint16(type_, ptr);
-
-    ptr = writeUint16(len() - getHeaderLen(), ptr);
-
-    if (! data_.empty())
-        memcpy(ptr, &data_[0], data_.size());
-
-    // end of fixed part of this option
-    offset += OPTION6_HDR_LEN + data_.size();
-
-    return LibDHCP::packOptions6(buf, buf_len, offset, options_);
-}
+void Option::pack6(isc::util::OutputBuffer& buf) {
+    buf.writeUint16(type_);
+    buf.writeUint16(len() - getHeaderLen());
 
-unsigned int
-Option::unpack(const boost::shared_array<uint8_t>& buf,
-               unsigned int buf_len,
-               unsigned int offset,
-               unsigned int parse_len) {
-    switch (universe_) {
-    case V4:
-        return unpack4(buf, buf_len, offset, parse_len);
-    case V6:
-        return unpack6(buf, buf_len, offset, parse_len);
-    default:
-        isc_throw(BadValue, "Unknown universe defined for Option " << type_);
+    if (! data_.empty()) {
+        buf.writeData(&data_[0], data_.size());
     }
 
-    return 0; // should not happen
+    return LibDHCP::packOptions6(buf, options_);
 }
 
-unsigned int
-Option::unpack4(const boost::shared_array<uint8_t>&,
-                unsigned int ,
-                unsigned int ,
-                unsigned int ) {
-    isc_throw(Unexpected, "IPv4 support not implemented yet.");
-    return 0;
+void Option::unpack(OptionBufferConstIter begin,
+                    OptionBufferConstIter end) {
+    data_ = OptionBuffer(begin, end);
 }
 
-unsigned int
-Option::unpack6(const boost::shared_array<uint8_t>& buf,
-                unsigned int buf_len,
-                unsigned int offset,
-                unsigned int parse_len) {
-
-    if (buf_len < offset+parse_len) {
-        isc_throw(OutOfRange, "Failed to unpack DHCPv6 option len="
-                  << parse_len << " offset=" << offset
-                  << " from buffer (length=" << buf_len
-                  << "): too small buffer.");
-    }
-
-    uint8_t* ptr = &buf[offset];
-    data_ = std::vector<uint8_t>(ptr, ptr + parse_len);
-
-    offset_ = offset;
-
-    return (offset+parse_len);
-
-    //return LibDHCP::unpackOptions6(buf, buf_len, offset, parse_len,
-    //                               options_);
-}
-
-/// Returns length of the complete option (data length + DHCPv4/DHCPv6
-/// option header)
-uint16_t
-Option::len() {
+uint16_t Option::len() {
+    // Returns length of the complete option (data length + DHCPv4/DHCPv6
+    // option header)
 
     // length of the whole option is header and data stored in this option...
     int length = getHeaderLen() + data_.size();
@@ -232,18 +164,16 @@ Option::valid() {
     return (true);
 }
 
-boost::shared_ptr<isc::dhcp::Option>
-Option::getOption(unsigned short opt_type) {
+OptionPtr Option::getOption(uint16_t opt_type) {
     isc::dhcp::Option::OptionCollection::const_iterator x =
         options_.find(opt_type);
     if ( x != options_.end() ) {
         return (*x).second;
     }
-    return boost::shared_ptr<isc::dhcp::Option>(); // NULL
+    return OptionPtr(); // NULL
 }
 
-bool
-Option::delOption(unsigned short opt_type) {
+bool Option::delOption(uint16_t opt_type) {
     isc::dhcp::Option::OptionCollection::iterator x = options_.find(opt_type);
     if ( x != options_.end() ) {
         options_.erase(x);
@@ -289,8 +219,7 @@ Option::getHeaderLen() {
     return 0; // should not happen
 }
 
-void
-Option::addOption(boost::shared_ptr<Option> opt) {
+void Option::addOption(OptionPtr opt) {
     if (universe_ == V4) {
         // check for uniqueness (DHCPv4 options must be unique)
         if (getOption(opt->getType())) {
@@ -298,7 +227,7 @@ Option::addOption(boost::shared_ptr<Option> opt) {
                       << " already present in this message.");
         }
     }
-    options_.insert(pair<int, boost::shared_ptr<Option> >(opt->getType(), opt));
+    options_.insert(make_pair(opt->getType(), opt));
 }
 
 uint8_t Option::getUint8() {
@@ -344,3 +273,6 @@ void Option::setUint32(uint32_t value) {
 Option::~Option() {
 
 }
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index d80fc05..c7f5d10 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -19,12 +19,30 @@
 #include <map>
 #include <vector>
 #include <boost/shared_ptr.hpp>
-#include <boost/shared_array.hpp>
 #include <util/buffer.h>
 
 namespace isc {
 namespace dhcp {
 
+/// @brief buffer types used in DHCP code.
+///
+/// Dereferencing OptionBuffer iterator will point out to contiguous memory.
+typedef std::vector<uint8_t> OptionBuffer;
+
+/// iterator for walking over OptionBuffer
+typedef OptionBuffer::iterator OptionBufferIter;
+
+/// const_iterator for walking over OptionBuffer
+typedef OptionBuffer::const_iterator OptionBufferConstIter;
+
+/// pointer to a DHCP buffer
+typedef boost::shared_ptr<OptionBuffer> OptionBufferPtr;
+
+/// shared pointer to Option object
+class Option;
+typedef boost::shared_ptr<Option> OptionPtr;
+
+
 class Option {
 public:
     /// length of the usual DHCPv4 option header (there are exceptions)
@@ -37,46 +55,22 @@ public:
     enum Universe { V4, V6 };
 
     /// a collection of DHCPv6 options
-    typedef std::multimap<unsigned int, boost::shared_ptr<Option> >
-    OptionCollection;
+    typedef std::multimap<unsigned int, OptionPtr> OptionCollection;
 
     /// @brief a factory function prototype
     ///
     /// @param u option universe (DHCPv4 or DHCPv6)
     /// @param type option type
     /// @param buf pointer to a buffer
-    /// @param offset offset to first data byte in that buffer
-    /// @param len data length of this option
     ///
     /// @return a pointer to a created option object
-    typedef boost::shared_ptr<Option> Factory(Option::Universe u,
-                                              unsigned short type,
-                                              boost::shared_array<uint8_t>& buf,
-                                              unsigned int offset,
-                                              unsigned int len);
+    typedef OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer& buf);
 
     /// @brief ctor, used for options constructed, usually during transmission
     ///
     /// @param u option universe (DHCPv4 or DHCPv6)
     /// @param type option type
-    Option(Universe u, unsigned short type);
-
-    /// @brief ctor, used for received options
-    ///
-    /// boost::shared_array allows sharing a buffer, but it requires that
-    /// different instances share pointer to the whole array, not point
-    /// to different elements in shared array. Therefore we need to share
-    /// pointer to the whole array and remember offset where data for
-    /// this option begins
-    ///
-    /// @param u specifies universe (V4 or V6)
-    /// @param type option type
-    /// @param buf pointer to a buffer
-    /// @param offset offset in a buffer pointing to first byte of data
-    /// @param len length of the option data
-    Option(Universe u, unsigned short type,
-           const boost::shared_array<uint8_t>& buf, unsigned int offset,
-           unsigned int len);
+    Option(Universe u, uint16_t type);
 
     /// @brief Constructor, used for received options.
     ///
@@ -88,7 +82,7 @@ public:
     /// @param u specifies universe (V4 or V6)
     /// @param type option type (0-255 for V4 and 0-65535 for V6)
     /// @param data content of the option
-    Option(Universe u, unsigned short type, std::vector<uint8_t>& data);
+    Option(Universe u, uint16_t type, const OptionBuffer& data);
 
     /// @brief Constructor, used for received options.
     ///
@@ -110,15 +104,13 @@ public:
     /// @param first iterator to the first element that should be copied
     /// @param last iterator to the next element after the last one
     ///        to be copied.
-    Option(Universe u, uint16_t type,
-           std::vector<uint8_t>::const_iterator first,
-           std::vector<uint8_t>::const_iterator last);
+    Option(Universe u, uint16_t type, OptionBufferConstIter first,
+           OptionBufferConstIter last);
 
     /// @brief returns option universe (V4 or V6)
     ///
     /// @return universe type
-    Universe
-    getUniverse() { return universe_; };
+    Universe  getUniverse() { return universe_; };
 
     /// @brief Writes option in wire-format to a buffer.
     ///
@@ -129,14 +121,7 @@ public:
     /// TODO: Migrate DHCPv6 code to pack(OutputBuffer& buf) version
     ///
     /// @param buf pointer to a buffer
-    /// @param buf_len length of the buffer
-    /// @param offset offset to place, where option shout be stored
-    ///
-    /// @return offset to first unused byte after stored option
-    ///
-    virtual unsigned int
-    pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
-         unsigned int offset);
+    virtual void pack(isc::util::OutputBuffer& buf);
 
     /// @brief Writes option in a wire-format to a buffer.
     ///
@@ -146,64 +131,48 @@ public:
     /// unify pack4() and pack6() and rename them to just pack().
     ///
     /// @param buf output buffer (option will be stored there)
-    virtual void
-    pack4(isc::util::OutputBuffer& buf);
-
+    virtual void pack4(isc::util::OutputBuffer& buf);
 
-    /// @brief Parses buffer.
+    /// @brief Parses received buffer.
     ///
-    /// Parses received buffer, returns offset to the first unused byte after
-    /// parsed option.
-    ///
-    /// @param buf pointer to buffer
-    /// @param buf_len length of buf
-    /// @param offset offset, where start parsing option
-    /// @param parse_len how many bytes should be parsed
-    ///
-    /// @return offset after last parsed octet
-    virtual unsigned int
-    unpack(const boost::shared_array<uint8_t>& buf,
-           unsigned int buf_len,
-           unsigned int offset,
-           unsigned int parse_len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    virtual void unpack(OptionBufferConstIter begin,
+                        OptionBufferConstIter end);
 
     /// Returns string representation of the option.
     ///
     /// @param indent number of spaces before printing text
     ///
     /// @return string with text representation.
-    virtual std::string
-    toText(int indent = 0);
+    virtual std::string toText(int indent = 0);
 
     /// Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
     ///
     /// @return option type
-    unsigned short getType() { return (type_); }
+    uint16_t getType() { return (type_); }
 
     /// Returns length of the complete option (data length + DHCPv4/DHCPv6
     /// option header)
     ///
     /// @return length of the option
-    virtual uint16_t
-    len();
+    virtual uint16_t len();
 
     /// @brief Returns length of header (2 for v4, 4 for v6)
     ///
     /// @return length of option header
-    virtual uint16_t
-    getHeaderLen();
+    virtual uint16_t getHeaderLen();
 
     /// returns if option is valid (e.g. option may be truncated)
     ///
     /// @return true, if option is valid
-    virtual bool
-    valid();
+    virtual bool valid();
 
     /// Returns pointer to actual data.
     ///
     /// @return pointer to actual data (or reference to an empty vector
     ///         if there is no data)
-    virtual const std::vector<uint8_t>& getData() { return (data_); }
+    virtual const OptionBuffer& getData() { return (data_); }
 
     /// Adds a sub-option.
     ///
@@ -217,24 +186,21 @@ public:
     /// many places. Requiring casting is not feasible.
     ///
     /// @param opt shared pointer to a suboption that is going to be added.
-    void
-    addOption(boost::shared_ptr<Option> opt);
+    void addOption(OptionPtr opt);
 
     /// Returns shared_ptr to suboption of specific type
     ///
     /// @param type type of requested suboption
     ///
     /// @return shared_ptr to requested suoption
-    boost::shared_ptr<isc::dhcp::Option>
-    getOption(unsigned short type);
+    OptionPtr getOption(uint16_t type);
 
     /// Attempts to delete first suboption of requested type
     ///
     /// @param type Type of option to be deleted.
     ///
     /// @return true if option was deleted, false if no such option existed
-    bool
-    delOption(unsigned short type);
+    bool delOption(uint16_t type);
 
     /// @brief Returns content of first byte.
     ///
@@ -286,40 +252,7 @@ protected:
     /// defined suboptions. Version for building DHCPv4 options.
     ///
     /// @param buf output buffer (built options will be stored here)
-    /// @param buf_len buffer length (used for buffer overflow checks)
-    /// @param offset offset from start of the buf buffer
-    ///
-    /// @return offset to the next byte after last used byte
-    virtual unsigned int
-    pack6(boost::shared_array<uint8_t>& buf,
-          unsigned int buf_len,
-          unsigned int offset);
-
-    /// Parses provided buffer and creates DHCPv4 options.
-    ///
-    /// @param buf buffer that contains raw buffer to parse (on-wire format)
-    /// @param buf_len buffer length (used for buffer overflow checks)
-    /// @param offset offset from start of the buf buffer
-    ///
-    /// @return offset to the next byte after last parsed byte
-    virtual unsigned int
-    unpack4(const boost::shared_array<uint8_t>& buf,
-            unsigned int buf_len,
-            unsigned int offset,
-            unsigned int parse_len);
-
-    /// Parses provided buffer and creates DHCPv6 options.
-    ///
-    /// @param buf buffer that contains raw buffer to parse (on-wire format)
-    /// @param buf_len buffer length (used for buffer overflow checks)
-    /// @param offset offset from start of the buf buffer
-    ///
-    /// @return offset to the next byte after last parsed byte
-    virtual unsigned int
-    unpack6(const boost::shared_array<uint8_t>& buf,
-            unsigned int buf_len,
-            unsigned int offset,
-            unsigned int parse_len);
+    virtual void pack6(isc::util::OutputBuffer& buf);
 
     /// @brief A private method used for option correctness.
     ///
@@ -332,17 +265,10 @@ protected:
     Universe universe_;
 
     /// option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
-    unsigned short type_;
+    uint16_t type_;
 
     /// contains content of this data
-    std::vector<uint8_t> data_;
-
-    /// TODO: Remove this field. vector<uint8_t> should be used
-    /// instead.
-    /// data is a shared_pointer that points out to the
-    /// whole packet. offset_ specifies where data for
-    /// this option begins.
-    unsigned int offset_;
+    OptionBuffer data_;
 
     /// collection for storing suboptions
     OptionCollection options_;
diff --git a/src/lib/dhcp/option4_addrlst.cc b/src/lib/dhcp/option4_addrlst.cc
index 88eb915..4b0224f 100644
--- a/src/lib/dhcp/option4_addrlst.cc
+++ b/src/lib/dhcp/option4_addrlst.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -23,10 +23,12 @@
 #include <dhcp/option4_addrlst.h>
 
 using namespace std;
-using namespace isc::dhcp;
 using namespace isc::util;
 using namespace isc::asiolink;
 
+namespace isc {
+namespace dhcp {
+
 Option4AddrLst::Option4AddrLst(uint8_t type)
     :Option(V4, type) {
 }
@@ -38,9 +40,8 @@ Option4AddrLst::Option4AddrLst(uint8_t type, const AddressContainer& addrs)
 }
 
 
-Option4AddrLst::Option4AddrLst(uint8_t type,
-                               vector<uint8_t>::const_iterator first,
-                               vector<uint8_t>::const_iterator last)
+Option4AddrLst::Option4AddrLst(uint8_t type, OptionBufferConstIter first,
+                               OptionBufferConstIter last)
     :Option(V4, type) {
     if ( (distance(first, last) % V4ADDRESS_LEN) ) {
         isc_throw(OutOfRange, "DHCPv4 Option4AddrLst " << type_
@@ -133,3 +134,6 @@ std::string Option4AddrLst::toText(int indent /* =0 */ ) {
 
     return tmp.str();
 }
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option4_addrlst.h b/src/lib/dhcp/option4_addrlst.h
index c795805..3bedc6d 100644
--- a/src/lib/dhcp/option4_addrlst.h
+++ b/src/lib/dhcp/option4_addrlst.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -77,8 +77,8 @@ public:
     /// @param first iterator to the first element that should be copied
     /// @param last iterator to the next element after the last one
     ///        to be copied.
-    Option4AddrLst(uint8_t type, std::vector<uint8_t>::const_iterator first,
-           std::vector<uint8_t>::const_iterator last);
+    Option4AddrLst(uint8_t type, OptionBufferConstIter first,
+                   OptionBufferConstIter last);
 
     /// @brief Writes option in a wire-format to a buffer.
     ///
@@ -88,16 +88,14 @@ public:
     /// unify pack4() and pack6() and rename them to just pack().
     ///
     /// @param buf output buffer (option will be stored there)
-    virtual void
-    pack4(isc::util::OutputBuffer& buf);
+    virtual void pack4(isc::util::OutputBuffer& buf);
 
     /// Returns string representation of the option.
     ///
     /// @param indent number of spaces before printing text
     ///
     /// @return string with text representation.
-    virtual std::string
-    toText(int indent = 0);
+    virtual std::string toText(int indent = 0);
 
     /// Returns length of the complete option (data length + DHCPv4/DHCPv6
     /// option header)
@@ -113,8 +111,7 @@ public:
     /// a couple (1-3) addresses, the overhead is not that big.
     ///
     /// @return address container with addresses
-    AddressContainer
-    getAddresses() { return addrs_; };
+    AddressContainer getAddresses() { return addrs_; };
 
     /// @brief Sets addresses list.
     ///
diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc
index fb082fa..d23b700 100644
--- a/src/lib/dhcp/option6_addrlst.cc
+++ b/src/lib/dhcp/option6_addrlst.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -29,23 +29,21 @@ using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::util;
 
-Option6AddrLst::Option6AddrLst(unsigned short type,
-                               const AddressContainer& addrs)
+namespace isc {
+namespace dhcp {
+
+Option6AddrLst::Option6AddrLst(uint16_t type, const AddressContainer& addrs)
     :Option(V6, type), addrs_(addrs) {
 }
 
-Option6AddrLst::Option6AddrLst(unsigned short type,
-                               const isc::asiolink::IOAddress& addr)
+Option6AddrLst::Option6AddrLst(uint16_t type, const isc::asiolink::IOAddress& addr)
     :Option(V6, type), addrs_(1,addr) {
 }
 
-Option6AddrLst::Option6AddrLst(unsigned short type,
-                               boost::shared_array<uint8_t> buf,
-                               unsigned int buf_len,
-                               unsigned int offset,
-                               unsigned int option_len)
+Option6AddrLst::Option6AddrLst(uint16_t type, OptionBufferConstIter begin,
+                               OptionBufferConstIter end)
     :Option(V6, type) {
-    unpack(buf, buf_len, offset, option_len);
+    unpack(begin, end);
 }
 
 void
@@ -63,70 +61,42 @@ Option6AddrLst::setAddresses(const AddressContainer& addrs) {
     addrs_ = addrs;
 }
 
-unsigned int
-Option6AddrLst::pack(boost::shared_array<uint8_t>& buf,
-                    unsigned int buf_len,
-                    unsigned int offset) {
-    if (len() > buf_len) {
-        isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
-                  << ", buffer=" << buf_len << ": too small buffer.");
-    }
+void Option6AddrLst::pack(isc::util::OutputBuffer& buf) {
 
-    writeUint16(type_, &buf[offset]);
-    offset += sizeof(uint16_t);
+    buf.writeUint16(type_);
 
     // len() returns complete option length.
     // len field contains length without 4-byte option header
-    writeUint16(len() - OPTION6_HDR_LEN, &buf[offset]);
-    offset += sizeof(uint16_t);
+    buf.writeUint16(len() - getHeaderLen());
 
-    // this wrapping is *ugly*. I wish there was a a
     for (AddressContainer::const_iterator addr=addrs_.begin();
-         addr!=addrs_.end();
-         ++addr) {
-        memcpy(&buf[offset],
-               addr->getAddress().to_v6().to_bytes().data(),
-               V6ADDRESS_LEN);
-        offset += V6ADDRESS_LEN;
+         addr!=addrs_.end(); ++addr) {
+        buf.writeData(addr->getAddress().to_v6().to_bytes().data(), V6ADDRESS_LEN);
     }
-
-    return offset;
 }
 
-unsigned int
-Option6AddrLst::unpack(const boost::shared_array<uint8_t>& buf,
-                       unsigned int buf_len,
-                       unsigned int offset,
-                       unsigned int option_len) {
-    if (offset+option_len > buf_len) {
-        isc_throw(OutOfRange, "Option " << type_
-                  << " truncated.");
-    }
-
-    if (option_len%16) {
+void Option6AddrLst::unpack(OptionBufferConstIter begin,
+                        OptionBufferConstIter end) {
+    if ((distance(begin, end) % V6ADDRESS_LEN) != 0) {
         isc_throw(OutOfRange, "Option " << type_
-                  << " malformed: len=" << option_len
+                  << " malformed: len=" << distance(begin, end)
                   << " is not divisible by 16.");
     }
-    while (option_len > 0) {
-        addrs_.push_back(IOAddress::from_bytes(AF_INET6, &buf[offset]));
-        offset += 16;
-        option_len -= 16;
+    while (begin != end) {
+        addrs_.push_back(IOAddress::from_bytes(AF_INET6, &(*begin)));
+        begin += V6ADDRESS_LEN;
     }
-
-    return offset;
 }
 
 std::string Option6AddrLst::toText(int indent /* =0 */) {
     stringstream tmp;
-    for (int i=0; i<indent; i++)
+    for (int i = 0; i < indent; i++)
         tmp << " ";
 
     tmp << "type=" << type_ << " " << addrs_.size() << "addr(s): ";
 
     for (AddressContainer::const_iterator addr=addrs_.begin();
-         addr!=addrs_.end();
-         ++addr) {
+         addr!=addrs_.end(); ++addr) {
         tmp << addr->toText() << " ";
     }
     return tmp.str();
@@ -134,5 +104,8 @@ std::string Option6AddrLst::toText(int indent /* =0 */) {
 
 uint16_t Option6AddrLst::len() {
 
-    return (OPTION6_HDR_LEN + addrs_.size()*16);
+    return (OPTION6_HDR_LEN + addrs_.size()*V6ADDRESS_LEN);
 }
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/option6_addrlst.h b/src/lib/dhcp/option6_addrlst.h
index a73dc55..209d2dd 100644
--- a/src/lib/dhcp/option6_addrlst.h
+++ b/src/lib/dhcp/option6_addrlst.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -36,70 +36,44 @@ public:
     ///
     /// @param type option type
     /// @param addrs vector of addresses to be stored
-    ///
-    Option6AddrLst(unsigned short type,
-                   const AddressContainer& addrs);
+    Option6AddrLst(uint16_t type, const AddressContainer& addrs);
 
     /// @brief Simplified constructor for a single address
     ///
     /// @param type option type
     /// @param addr a single address to be stored
-    ///
-    Option6AddrLst(unsigned short type,
-                   const isc::asiolink::IOAddress& addr);
+    Option6AddrLst(uint16_t type, const isc::asiolink::IOAddress& addr);
 
     /// @brief Constructor used for parsing received option
     ///
     /// @param type option type
-    /// @param buf pointer to packet buffer
-    /// @param buf_len length of packet buffer
-    /// @param offset offset to beginning of option data
-    /// @param len length of option data
-    ///
-    Option6AddrLst(unsigned short type, boost::shared_array<uint8_t> buf,
-                   unsigned int buf_len,
-                   unsigned int offset,
-                   unsigned int len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    Option6AddrLst(uint16_t type, OptionBufferConstIter begin,
+                   OptionBufferConstIter end);
 
     /// @brief Assembles on-wire form of this option
     ///
     /// @param buf pointer to packet buffer
-    /// @param buf_len length of packet buffer
-    /// @param offset offset to place, where option is to be stored
-    ///
-    /// @return offset to the next unused char (just after stored option)
-    ///
-    unsigned int
-    pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
-         unsigned int offset);
+    void pack(isc::util::OutputBuffer& buf);
 
     /// @brief Parses received data
     ///
-    /// @param buf pointer to packet buffer
-    /// @param buf_len length of packet buffer
-    /// @param offset offset to option data
-    /// @param parse_len specified option data length
-    ///
-    /// @return offset to the next unparsed char (just after parsed option)
-    ///
-    virtual unsigned int
-    unpack(const boost::shared_array<uint8_t>& buf,
-           unsigned int buf_len,
-           unsigned int offset,
-           unsigned int parse_len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    virtual void unpack(OptionBufferConstIter begin,
+                        OptionBufferConstIter end);
 
     virtual std::string toText(int indent = 0);
 
     /// @brief Sets a single address.
     ///
     /// @param addr a single address to be added
-    ///
     void setAddress(const isc::asiolink::IOAddress& addr);
 
     /// @brief Sets list of addresses.
     ///
     /// @param addrs a vector of addresses to be added
-    ///
     void setAddresses(const AddressContainer& addrs);
 
     /// @brief Returns vector with addresses.
@@ -110,8 +84,7 @@ public:
     /// a couple (1-3) addresses, the overhead is not that big.
     ///
     /// @return address container with addresses
-    AddressContainer
-    getAddresses() { return addrs_; };
+    AddressContainer getAddresses() { return addrs_; };
 
     // returns data length (data length + DHCPv4/DHCPv6 option header)
     virtual uint16_t len();
diff --git a/src/lib/dhcp/option6_ia.cc b/src/lib/dhcp/option6_ia.cc
index cd55553..65be711 100644
--- a/src/lib/dhcp/option6_ia.cc
+++ b/src/lib/dhcp/option6_ia.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -15,82 +15,54 @@
 #include <stdint.h>
 #include <arpa/inet.h>
 #include <sstream>
-#include "exceptions/exceptions.h"
 
-#include "dhcp/libdhcp++.h"
-#include "dhcp/option6_ia.h"
-#include "dhcp/dhcp6.h"
-#include "util/io_utilities.h"
+#include <exceptions/exceptions.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/dhcp6.h>
+#include <util/io_utilities.h>
 
 using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
 using namespace isc::util;
 
-Option6IA::Option6IA(unsigned short type, unsigned int iaid)
+namespace isc {
+namespace dhcp {
+
+Option6IA::Option6IA(uint16_t type, uint32_t iaid)
     :Option(Option::V6, type), iaid_(iaid) {
 }
 
-Option6IA::Option6IA(unsigned short type,
-                     const boost::shared_array<uint8_t>& buf,
-                     unsigned int buf_len,
-                     unsigned int offset,
-                     unsigned int option_len)
+Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
     :Option(Option::V6, type) {
-    unpack(buf, buf_len, offset, option_len);
+    unpack(begin, end);
 }
 
-unsigned int
-Option6IA::pack(boost::shared_array<uint8_t>& buf,
-                unsigned int buf_len,
-                unsigned int offset) {
-    if (offset + len() > buf_len) {
-        isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
-                  << ", buffer=" << buf_len << ": too small buffer.");
-    }
-
-    if (len() < 16 ) {
-        isc_throw(OutOfRange, "Attempt to build malformed IA option: len="
-                  << len() << " is too small (at least 16 is required).");
-    }
-
-    uint8_t* ptr = &buf[offset];
-
-    ptr = writeUint16(type_, ptr);
-    ptr = writeUint16(len() - OPTION6_HDR_LEN, ptr);
-    offset += OPTION6_HDR_LEN;
+void Option6IA::pack(isc::util::OutputBuffer& buf) {
+    buf.writeUint16(type_);
+    buf.writeUint16(len() - OPTION6_HDR_LEN);
+    buf.writeUint32(iaid_);
+    buf.writeUint32(t1_);
+    buf.writeUint32(t2_);
 
-    ptr = writeUint32(iaid_, ptr);
-    ptr = writeUint32(t1_, ptr);
-    ptr = writeUint32(t2_, ptr);
-    offset += OPTION6_IA_LEN;
-
-    offset = LibDHCP::packOptions6(buf, buf_len, offset, options_);
-    return offset;
+    LibDHCP::packOptions6(buf, options_);
 }
 
-unsigned int
-Option6IA::unpack(const boost::shared_array<uint8_t>& buf,
-                  unsigned int buf_len,
-                  unsigned int offset,
-                  unsigned int parse_len) {
-    if ( parse_len < OPTION6_IA_LEN || offset + OPTION6_IA_LEN > buf_len) {
+void Option6IA::unpack(OptionBufferConstIter begin,
+                       OptionBufferConstIter end) {
+    // IA_NA and IA_PD have 12 bytes content (iaid, t1, t2 fields)
+    // followed by 0 or more sub-options.
+    if (distance(begin, end) < OPTION6_IA_LEN) {
         isc_throw(OutOfRange, "Option " << type_ << " truncated");
     }
+    iaid_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
+    t1_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
 
-    iaid_ = readUint32(&buf[offset]);
-    offset += sizeof(uint32_t);
-
-    t1_ = readUint32(&buf[offset]);
-    offset += sizeof(uint32_t);
-
-    t2_ = readUint32(&buf[offset]);
-    offset += sizeof(uint32_t);
+    t2_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
 
-    offset = LibDHCP::unpackOptions6(buf, buf_len, offset,
-                                     parse_len - OPTION6_IA_LEN, options_);
-
-    return (offset);
+    LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
 }
 
 std::string Option6IA::toText(int indent /* = 0*/) {
@@ -134,3 +106,6 @@ uint16_t Option6IA::len() {
     }
     return (length);
 }
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option6_ia.h b/src/lib/dhcp/option6_ia.h
index cab8068..c2089d4 100644
--- a/src/lib/dhcp/option6_ia.h
+++ b/src/lib/dhcp/option6_ia.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -27,54 +27,34 @@ public:
     /// Length of IA_NA and IA_PD content
     const static size_t OPTION6_IA_LEN = 12;
 
-    /// @brief ctor, used for options constructed, usually during transmission
+    /// @brief Ctor, used for constructed options, usually during transmission.
     ///
     /// @param type option type (usually 4 for IA_NA, 25 for IA_PD)
     /// @param iaid identity association identifier (id of IA)
-    Option6IA(uint16_t type, unsigned int iaid);
+    Option6IA(uint16_t type, uint32_t iaid);
 
-    /// @brief ctor, used for received options
-    ///
-    /// boost::shared_array allows sharing a buffer, but it requires that
-    /// different instances share pointer to the whole array, not point
-    /// to different elements in shared array. Therefore we need to share
-    /// pointer to the whole array and remember offset where data for
-    /// this option begins
+    /// @brief Ctor, used for received options.
     ///
     /// @param type option type (usually 4 for IA_NA, 25 for IA_PD)
-    /// @param buf buffer to be parsed
-    /// @param buf_len buffer length
-    /// @param offset offset in buffer
-    /// @param len number of bytes to parse
-    Option6IA(uint16_t type, const boost::shared_array<uint8_t>& buf,
-              unsigned int buf_len, unsigned int offset, unsigned int len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    Option6IA(uint16_t type, OptionBuffer::const_iterator begin,
+              OptionBuffer::const_iterator end);
 
     /// Writes option in wire-format to buf, returns pointer to first unused
     /// byte after stored option.
     ///
     /// @param buf buffer (option will be stored here)
-    /// @param buf_len (buffer length)
-    /// @param offset offset place where option should be stored
-    ///
-    /// @return offset to the first unused byte after stored option
-    unsigned int
-    pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
-         unsigned int offset);
+    void pack(isc::util::OutputBuffer& buf);
 
     /// @brief Parses received buffer
     ///
     /// Parses received buffer and returns offset to the first unused byte after
     /// parsed option.
     ///
-    /// @param buf pointer to buffer
-    /// @param buf_len length of buf
-    /// @param offset offset, where start parsing option
-    /// @param parse_len how many bytes should be parsed
-    ///
-    /// @return offset after last parsed octet
-    virtual unsigned int
-    unpack(const boost::shared_array<uint8_t>& buf, unsigned int buf_len,
-           unsigned int offset, unsigned int parse_len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
 
     /// Provides human readable text representation
     ///
@@ -87,48 +67,46 @@ public:
     /// Sets T1 timer.
     ///
     /// @param t1 t1 value to be set
-    void setT1(unsigned int t1) { t1_=t1; }
-
+    void setT1(uint32_t t1) { t1_=t1; }
 
     /// Sets T2 timer.
     ///
     /// @param t2 t2 value to be set
-    void setT2(unsigned int t2) { t2_=t2; }
+    void setT2(uint32_t t2) { t2_=t2; }
 
     /// Returns IA identifier.
     ///
     /// @return IAID value.
     ///
-    unsigned int getIAID() const { return iaid_; }
+    uint32_t getIAID() const { return iaid_; }
 
     /// Returns T1 timer.
     ///
     /// @return T1 value.
-    unsigned int getT1() const { return t1_; }
+    uint32_t getT1() const { return t1_; }
 
     /// Returns T2 timer.
     ///
     /// @return T2 value.
-    unsigned int getT2() const { return t2_; }
+    uint32_t getT2() const { return t2_; }
 
     /// @brief returns complete length of option
     ///
     /// Returns length of this option, including option header and suboptions
     ///
     /// @return length of this option
-    virtual uint16_t
-    len();
+    virtual uint16_t len();
 
 protected:
 
     /// keeps IA identifier
-    unsigned int iaid_;
+    uint32_t iaid_;
 
     /// keeps T1 timer value
-    unsigned int t1_;
+    uint32_t t1_;
 
     /// keeps T2 timer value
-    unsigned int t2_;
+    uint32_t t2_;
 };
 
 } // isc::dhcp namespace
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index 70c2948..084a5f3 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -24,79 +24,59 @@
 #include "util/io_utilities.h"
 
 using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::util;
 
-Option6IAAddr::Option6IAAddr(unsigned short type,
-                             const isc::asiolink::IOAddress& addr,
-                             unsigned int pref, unsigned int valid)
+namespace isc {
+namespace dhcp {
+
+Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
+                             uint32_t pref, uint32_t valid)
     :Option(V6, type), addr_(addr), preferred_(pref),
      valid_(valid) {
 }
 
-Option6IAAddr::Option6IAAddr(unsigned short type,
-                             boost::shared_array<uint8_t> buf,
-                             unsigned int buf_len, unsigned int offset,
-                             unsigned int option_len)
+Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
+                             OptionBuffer::const_iterator end)
     :Option(V6, type), addr_("::") {
-    unpack(buf, buf_len, offset, option_len);
+    unpack(begin, end);
 }
 
-unsigned int
-Option6IAAddr::pack(boost::shared_array<uint8_t>& buf,
-                    unsigned int buf_len,
-                    unsigned int offset) {
-    if (len() > buf_len) {
-        isc_throw(OutOfRange, "Failed to pack IA option: len=" << len()
-                  << ", buffer=" << buf_len << ": too small buffer.");
-    }
-
-    uint8_t* ptr = &buf[offset];
+void Option6IAAddr::pack(isc::util::OutputBuffer& buf) {
 
-    ptr = writeUint16(type_, ptr);
+    buf.writeUint16(type_);
 
     // len() returns complete option length. len field contains
     // length without 4-byte option header
-    ptr = writeUint16(len() - OPTION6_HDR_LEN, ptr);
-    offset += OPTION6_HDR_LEN;
+    buf.writeUint16(len() - getHeaderLen());
 
-    memcpy(ptr, addr_.getAddress().to_v6().to_bytes().data(), 16);
-    ptr += V6ADDRESS_LEN;
 
-    ptr = writeUint32(preferred_, ptr);
+    buf.writeData(addr_.getAddress().to_v6().to_bytes().data(),
+                  isc::asiolink::V6ADDRESS_LEN);
 
-    ptr = writeUint32(valid_, ptr);
-    offset += OPTION6_IAADDR_LEN;
+    buf.writeUint32(preferred_);
+    buf.writeUint32(valid_);
 
-    // parse suboption (there shouldn't be any)
-    offset = LibDHCP::packOptions6(buf, buf_len, offset, options_);
-    return offset;
+    // parse suboption (there shouldn't be any for IAADDR)
+    LibDHCP::packOptions6(buf, options_);
 }
 
-unsigned int
-Option6IAAddr::unpack(const boost::shared_array<uint8_t>& buf,
-                  unsigned int buf_len,
-                  unsigned int offset,
-                  unsigned int parse_len) {
-    if ( parse_len < OPTION6_IAADDR_LEN || offset + OPTION6_IAADDR_LEN > buf_len) {
+void Option6IAAddr::unpack(OptionBuffer::const_iterator begin,
+                      OptionBuffer::const_iterator end) {
+    if ( distance(begin, end) < OPTION6_IAADDR_LEN) {
         isc_throw(OutOfRange, "Option " << type_ << " truncated");
     }
 
     // 16 bytes: IPv6 address
-    addr_ = IOAddress::from_bytes(AF_INET6, &buf[offset]);
-    offset += V6ADDRESS_LEN;
+    addr_ = IOAddress::from_bytes(AF_INET6, &(*begin));
+    begin += V6ADDRESS_LEN;
 
-    preferred_ = readUint32(&buf[offset]);
-    offset += sizeof(uint32_t);
+    preferred_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
 
-    valid_ = readUint32(&buf[offset]);
-    offset += sizeof(uint32_t);
-    offset = LibDHCP::unpackOptions6(buf, buf_len, offset,
-                                     parse_len - 24, options_);
-
-    return offset;
+    valid_ = readUint32( &(*begin) );
+    begin += sizeof(uint32_t);
+    LibDHCP::unpackOptions6(OptionBuffer(begin, end), options_);
 }
 
 std::string Option6IAAddr::toText(int indent /* =0 */) {
@@ -130,3 +110,6 @@ uint16_t Option6IAAddr::len() {
     }
     return (length);
 }
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
diff --git a/src/lib/dhcp/option6_iaaddr.h b/src/lib/dhcp/option6_iaaddr.h
index 40e5967..e6e2c16 100644
--- a/src/lib/dhcp/option6_iaaddr.h
+++ b/src/lib/dhcp/option6_iaaddr.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -27,28 +27,22 @@ public:
     /// length of the fixed part of the IAADDR option
     static const size_t OPTION6_IAADDR_LEN = 24;
 
-    /// @brief ctor, used for options constructed (during transmission)
+    /// @brief Ctor, used for options constructed (during transmission).
     ///
     /// @param type option type
     /// @param addr reference to an address
     /// @param preferred address preferred lifetime (in seconds)
     /// @param valid address valid lifetime (in seconds)
-    Option6IAAddr(unsigned short type, const isc::asiolink::IOAddress& addr,
-                  unsigned int preferred, unsigned int valid);
-
-    /// ctor, used for received options
-    /// boost::shared_array allows sharing a buffer, but it requires that
-    /// different instances share pointer to the whole array, not point
-    /// to different elements in shared array. Therefore we need to share
-    /// pointer to the whole array and remember offset where data for
-    /// this option begins
+    Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr,
+                  uint32_t preferred, uint32_t valid);
+
+    /// @brief ctor, used for received options.
     ///
     /// @param type option type
-    /// @param buf pointer to a buffer
-    /// @param offset offset to first data byte in that buffer
-    /// @param len data length of this option
-    Option6IAAddr(unsigned short type, boost::shared_array<uint8_t> buf,
-                  unsigned int buf_len, unsigned int offset, unsigned int len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
+                  OptionBuffer::const_iterator end);
 
     /// @brief Writes option in wire-format.
     ///
@@ -56,30 +50,14 @@ public:
     /// byte after stored option.
     ///
     /// @param buf pointer to a buffer
-    /// @param buf_len length of the buffer
-    /// @param offset offset to place, where option shout be stored
-    ///
-    /// @return offset to first unused byte after stored option
-    unsigned int
-    pack(boost::shared_array<uint8_t>& buf, unsigned int buf_len,
-         unsigned int offset);
+    void pack(isc::util::OutputBuffer& buf);
 
-    /// @brief Parses buffer.
-    ///
-    /// Parses received buffer, returns offset to the first unused byte after
-    /// parsed option.
-    ///
-    /// @param buf pointer to buffer
-    /// @param buf_len length of buf
-    /// @param offset offset, where start parsing option
-    /// @param parse_len how many bytes should be parsed
+    /// @brief Parses received buffer.
     ///
-    /// @return offset after last parsed octet
-    virtual unsigned int
-    unpack(const boost::shared_array<uint8_t>& buf,
-           unsigned int buf_len,
-           unsigned int offset,
-           unsigned int parse_len);
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    virtual void unpack(OptionBufferConstIter begin,
+                        OptionBufferConstIter end);
 
     /// Returns string representation of the option.
     ///
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index c520747..a3f683f 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -18,7 +18,6 @@
 #include <iostream>
 #include <vector>
 #include <boost/shared_ptr.hpp>
-#include <boost/shared_array.hpp>
 #include "asiolink/io_address.h"
 #include "util/buffer.h"
 #include "dhcp/option.h"
@@ -103,8 +102,7 @@ public:
     /// This function is useful mainly for debugging.
     ///
     /// @return string with text representation
-    std::string
-    toText();
+    std::string toText();
 
     /// @brief Returns the size of the required buffer to build the packet.
     ///
@@ -112,118 +110,110 @@ public:
     /// the current set of packet options.
     ///
     /// @return number of bytes required to build this packet
-    size_t
-    len();
+    size_t len();
 
-    /// Sets hops field
+    /// @brief Sets hops field.
     ///
     /// @param hops value to be set
-    void
-    setHops(uint8_t hops) { hops_ = hops; };
+    void setHops(uint8_t hops) { hops_ = hops; };
 
-    /// Returns hops field
+    /// @brief Returns hops field.
     ///
     /// @return hops field
-    uint8_t
-    getHops() const { return (hops_); };
+    uint8_t getHops() const { return (hops_); };
 
     // Note: There's no need to manipulate OP field directly,
     // thus no setOp() method. See op_ comment.
 
-    /// Returns op field
+    /// @brief Returns op field.
     ///
     /// @return op field
-    uint8_t
-    getOp() const { return (op_); };
+    uint8_t getOp() const { return (op_); };
 
-    /// Sets secs field
+    /// @brief Sets secs field.
     ///
     /// @param secs value to be set
-    void
-    setSecs(uint16_t secs) { secs_ = secs; };
+    void setSecs(uint16_t secs) { secs_ = secs; };
 
-    /// Returns secs field
+    /// @brief Returns secs field.
     ///
     /// @return secs field
-    uint16_t
-    getSecs() const { return (secs_); };
+    uint16_t getSecs() const { return (secs_); };
 
-    /// Sets flags field
+    /// @brief Sets flags field.
     ///
     /// @param flags value to be set
-    void
-    setFlags(uint16_t flags) { flags_ = flags; };
+    void setFlags(uint16_t flags) { flags_ = flags; };
 
-    /// Returns flags field
+    /// @brief Returns flags field.
     ///
     /// @return flags field
-    uint16_t
-    getFlags() const { return (flags_); };
+    uint16_t getFlags() const { return (flags_); };
 
 
-    /// Returns ciaddr field
+    /// @brief Returns ciaddr field.
     ///
     /// @return ciaddr field
     const isc::asiolink::IOAddress&
     getCiaddr() const { return (ciaddr_); };
 
-    /// Sets ciaddr field
+    /// @brief Sets ciaddr field.
     ///
     /// @param ciaddr value to be set
     void
     setCiaddr(const isc::asiolink::IOAddress& ciaddr) { ciaddr_ = ciaddr; };
 
 
-    /// Returns siaddr field
+    /// @brief Returns siaddr field.
     ///
     /// @return siaddr field
     const isc::asiolink::IOAddress&
     getSiaddr() const { return (siaddr_); };
 
-    /// Sets siaddr field
+    /// @brief Sets siaddr field.
     ///
     /// @param siaddr value to be set
     void
     setSiaddr(const isc::asiolink::IOAddress& siaddr) { siaddr_ = siaddr; };
 
 
-    /// Returns yiaddr field
+    /// @brief Returns yiaddr field.
     ///
     /// @return yiaddr field
     const isc::asiolink::IOAddress&
     getYiaddr() const { return (yiaddr_); };
 
-    /// Sets yiaddr field
+    /// @brief Sets yiaddr field.
     ///
     /// @param yiaddr value to be set
     void
     setYiaddr(const isc::asiolink::IOAddress& yiaddr) { yiaddr_ = yiaddr; };
 
 
-    /// Returns giaddr field
+    /// @brief Returns giaddr field.
     ///
     /// @return giaddr field
     const isc::asiolink::IOAddress&
     getGiaddr() const { return (giaddr_); };
 
-    /// Sets giaddr field
+    /// @brief Sets giaddr field.
     ///
     /// @param giaddr value to be set
     void
     setGiaddr(const isc::asiolink::IOAddress& giaddr) { giaddr_ = giaddr; };
 
-    /// Returns value of transaction-id field
+    /// @brief Returns value of transaction-id field.
     ///
     /// @return transaction-id
     uint32_t getTransid() const { return (transid_); };
 
-    /// Returns message type (e.g. 1 = DHCPDISCOVER)
+    /// @brief Returns message type (e.g. 1 = DHCPDISCOVER).
     ///
     /// @return message type
     uint8_t
     getType() const { return (msg_type_); }
 
-    /// Sets message type (e.g. 1 = DHCPDISCOVER)
+    /// @brief Sets message type (e.g. 1 = DHCPDISCOVER).
     ///
     /// @param type message type to be set
     void setType(uint8_t type) { msg_type_=type; };
@@ -234,14 +224,14 @@ public:
     /// null-terminated. Do not use strlen() or similar on it.
     ///
     /// @return sname field
-    const std::vector<uint8_t>
+    const OptionBuffer
     getSname() const { return (std::vector<uint8_t>(sname_, &sname_[MAX_SNAME_LEN])); };
 
-    /// Sets sname field
+    /// @brief Sets sname field.
     ///
     /// @param sname value to be set
-    void
-    setSname(const uint8_t* sname, size_t snameLen = MAX_SNAME_LEN);
+    /// @param sname_len length of the sname buffer (up to MAX_SNAME_LEN)
+    void setSname(const uint8_t* sname, size_t sname_len = MAX_SNAME_LEN);
 
     /// @brief Returns file field
     ///
@@ -249,14 +239,15 @@ public:
     /// null-terminated. Do not use strlen() or similar on it.
     ///
     /// @return pointer to file field
-    const std::vector<uint8_t>
+    const OptionBuffer
     getFile() const { return (std::vector<uint8_t>(file_, &file_[MAX_FILE_LEN])); };
 
     /// Sets file field
     ///
     /// @param file value to be set
+    /// @param file_len length of the file buffer (up to MAX_FILE_LEN)
     void
-    setFile(const uint8_t* file, size_t fileLen = MAX_FILE_LEN);
+    setFile(const uint8_t* file, size_t file_len = MAX_FILE_LEN);
 
     /// @brief Sets hardware address.
     ///
@@ -266,7 +257,7 @@ public:
     ///
     /// Note: macAddr must be a buffer of at least hlen bytes.
     ///
-    /// @param hwType hardware type (will be sent in htype field)
+    /// @param hType hardware type (will be sent in htype field)
     /// @param hlen hardware length (will be sent in hlen field)
     /// @param macAddr pointer to hardware address
     void setHWAddr(uint8_t hType, uint8_t hlen,
@@ -350,7 +341,7 @@ public:
 
     /// @brief Sets remote address.
     ///
-    /// @params remote specifies remote address
+    /// @param remote specifies remote address
     void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
         remote_addr_ = remote;
     }
@@ -364,7 +355,7 @@ public:
 
     /// @brief Sets local address.
     ///
-    /// @params local specifies local address
+    /// @param local specifies local address
     void setLocalAddr(const isc::asiolink::IOAddress& local) {
         local_addr_ = local;
     }
@@ -378,7 +369,7 @@ public:
 
     /// @brief Sets local port.
     ///
-    /// @params local specifies local port
+    /// @param local specifies local port
     void setLocalPort(uint16_t local) { local_port_ = local; }
 
     /// @brief Returns local port.
@@ -388,7 +379,7 @@ public:
 
     /// @brief Sets remote port.
     ///
-    /// @params remote specifies remote port
+    /// @param remote specifies remote port
     void setRemotePort(uint16_t remote) { remote_port_ = remote; }
 
     /// @brief Returns remote port.
@@ -478,13 +469,13 @@ protected:
 
     // end of real DHCPv4 fields
 
-    /// output buffer (used during message
+    /// output buffer (used during message transmission)
     isc::util::OutputBuffer bufferOut_;
 
-    // that's the data of input buffer used in RX packet. Note that
-    // InputBuffer does not store the data itself, but just expects that
-    // data will be valid for the whole life of InputBuffer. Therefore we
-    // need to keep the data around.
+    /// that's the data of input buffer used in RX packet. Note that
+    /// InputBuffer does not store the data itself, but just expects that
+    /// data will be valid for the whole life of InputBuffer. Therefore we
+    /// need to keep the data around.
     std::vector<uint8_t> data_;
 
     /// message type (e.g. 1=DHCPDISCOVER)
@@ -496,6 +487,8 @@ protected:
     isc::dhcp::Option::OptionCollection options_;
 }; // Pkt4 class
 
+typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
+
 } // isc::dhcp namespace
 
 } // isc namespace
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index ff27d5e..7e1c691 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -13,55 +13,48 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 
-#include "dhcp/dhcp6.h"
-#include "dhcp/pkt6.h"
-#include "dhcp/libdhcp++.h"
-#include "exceptions/exceptions.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/pkt6.h>
+#include <dhcp/libdhcp++.h>
+#include <exceptions/exceptions.h>
 #include <iostream>
 #include <sstream>
 
 using namespace std;
-using namespace isc::dhcp;
 
 namespace isc {
-
-Pkt6::Pkt6(unsigned int dataLen, DHCPv6Proto proto /* = UDP */)
-    :data_len_(dataLen),
-     local_addr_("::"),
-     remote_addr_("::"),
-     iface_(""),
-     ifindex_(-1),
-     local_port_(-1),
-     remote_port_(-1),
-     proto_(proto),
-     msg_type_(-1),
-     transid_(rand()%0xffffff)
-{
-
-    data_ = boost::shared_array<uint8_t>(new uint8_t[dataLen]);
-    data_len_ = dataLen;
+namespace dhcp {
+
+Pkt6::Pkt6(const uint8_t* buf, uint32_t buf_len, DHCPv6Proto proto /* = UDP */) :
+    proto_(proto),
+    msg_type_(-1),
+    transid_(rand()%0xffffff),
+    iface_(""),
+    ifindex_(-1),
+    local_addr_("::"),
+    remote_addr_("::"),
+    local_port_(0),
+    remote_port_(0),
+    bufferOut_(0) {
+    data_.resize(buf_len);
+    memcpy(&data_[0], buf, buf_len);
 }
 
-Pkt6::Pkt6(uint8_t msg_type,
-           unsigned int transid,
-           DHCPv6Proto proto /*= UDP*/)
-    :local_addr_("::"),
-     remote_addr_("::"),
-     iface_(""),
-     ifindex_(-1),
-     local_port_(-1),
-     remote_port_(-1),
-     proto_(proto),
-     msg_type_(msg_type),
-     transid_(transid) {
-
-    data_ = boost::shared_array<uint8_t>(new uint8_t[4]);
-    data_len_ = 4;
+Pkt6::Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto /*= UDP*/) :
+    proto_(proto),
+    msg_type_(msg_type),
+    transid_(transid),
+    iface_(""),
+    ifindex_(-1),
+    local_addr_("::"),
+    remote_addr_("::"),
+    local_port_(0),
+    remote_port_(0),
+    bufferOut_(0) {
 }
 
-unsigned short
-Pkt6::len() {
-    unsigned int length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
+uint16_t Pkt6::len() {
+    uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
 
     for (Option::OptionCollection::iterator it = options_.begin();
          it != options_.end();
@@ -95,44 +88,21 @@ Pkt6::packUDP() {
     // It is better to implement a method in IOAddress that extracts
     // vector<uint8_t>
 
-    unsigned short length = len();
-    if (data_len_ < length) {
-        cout << "Previous len=" << data_len_ << ", allocating new buffer: len="
-             << length << endl;
-
-        // May throw exception if out of memory. That is rather fatal,
-        // so we don't catch this
-        data_ = boost::shared_array<uint8_t>(new uint8_t[length]);
-        data_len_ = length;
-    }
-
-    data_len_ = length;
     try {
         // DHCPv6 header: message-type (1 octect) + transaction id (3 octets)
-        data_[0] = msg_type_;
-
+        bufferOut_.writeUint8(msg_type_);
         // store 3-octet transaction-id
-        data_[1] = (transid_ >> 16) & 0xff;
-        data_[2] = (transid_ >> 8) & 0xff;
-        data_[3] = (transid_) & 0xff;
+        bufferOut_.writeUint8( (transid_ >> 16) & 0xff );
+        bufferOut_.writeUint8( (transid_ >> 8) & 0xff );
+        bufferOut_.writeUint8( (transid_) & 0xff );
 
         // the rest are options
-        unsigned short offset = LibDHCP::packOptions6(data_, length,
-                                                      4/*offset*/,
-                                                      options_);
-
-        // sanity check
-        if (offset != length) {
-            isc_throw(OutOfRange, "Packet build failed: expected size="
-                      << length << ", actual len=" << offset);
-        }
+        LibDHCP::packOptions6(bufferOut_, options_);
     }
     catch (const Exception& e) {
         cout << "Packet build failed:" << e.what() << endl;
         return (false);
     }
-    // Limited verbosity of this method
-    // cout << "Packet built, len=" << len() << endl;
     return (true);
 }
 
@@ -158,8 +128,8 @@ Pkt6::unpack() {
 
 bool
 Pkt6::unpackUDP() {
-    if (data_len_ < 4) {
-        std::cout << "DHCPv6 packet truncated. Only " << data_len_
+    if (data_.size() < 4) {
+        std::cout << "DHCPv6 packet truncated. Only " << data_.size()
                   << " bytes. Need at least 4." << std::endl;
         return (false);
     }
@@ -168,16 +138,13 @@ Pkt6::unpackUDP() {
         ((data_[2]) << 8) + (data_[3]);
     transid_ = transid_ & 0xffffff;
 
-    unsigned int offset = LibDHCP::unpackOptions6(data_,
-                                                  data_len_,
-                                                  4, //offset
-                                                  data_len_ - 4,
-                                                  options_);
-    if (offset != data_len_) {
-        cout << "DHCPv6 packet contains trailing garbage. Parsed "
-             << offset << " bytes, packet is " << data_len_ << " bytes."
-             << endl;
-        // just a warning. Ignore trailing garbage and continue
+    try {
+        OptionBuffer opt_buffer(data_.begin() + 4, data_.end());
+
+        LibDHCP::unpackOptions6(opt_buffer, options_);
+    } catch (const Exception& e) {
+        cout << "Packet parsing failed:" << e.what() << endl;
+        return (false);
     }
     return (true);
 }
@@ -206,7 +173,7 @@ Pkt6::toText() {
 }
 
 boost::shared_ptr<isc::dhcp::Option>
-Pkt6::getOption(unsigned short opt_type) {
+Pkt6::getOption(uint16_t opt_type) {
     isc::dhcp::Option::OptionCollection::const_iterator x = options_.find(opt_type);
     if (x!=options_.end()) {
         return (*x).second;
@@ -220,7 +187,7 @@ Pkt6::addOption(boost::shared_ptr<Option> opt) {
 }
 
 bool
-Pkt6::delOption(unsigned short type) {
+Pkt6::delOption(uint16_t type) {
     isc::dhcp::Option::OptionCollection::iterator x = options_.find(type);
     if (x!=options_.end()) {
         options_.erase(x);
@@ -229,4 +196,11 @@ Pkt6::delOption(unsigned short type) {
     return (false); // can't find option to be deleted
 }
 
-};
+void Pkt6::repack() {
+    cout << "Convering RX packet to TX packet: " << data_.size() << " bytes." << endl;
+
+    bufferOut_.writeData(&data_[0], data_.size());
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index 019eeb2..97ac996 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -41,17 +41,18 @@ public:
     /// @param msg_type type of message (SOLICIT=1, ADVERTISE=2, ...)
     /// @param transid transaction-id
     /// @param proto protocol (TCP or UDP)
-    Pkt6(unsigned char msg_type,
-         unsigned int transid,
+    Pkt6(uint8_t msg_type,
+         uint32_t transid,
          DHCPv6Proto proto = UDP);
 
     /// Constructor, used in message transmission
     ///
     /// Creates new message. Transaction-id will randomized.
     ///
-    /// @param len size of buffer to be allocated for this packet.
+    /// @param buf pointer to a buffer of received packet content
+    /// @param len size of buffer of received packet content
     /// @param proto protocol (usually UDP, but TCP will be supported eventually)
-    Pkt6(unsigned int len, DHCPv6Proto proto = UDP);
+    Pkt6(const uint8_t* buf, uint32_t len, DHCPv6Proto proto = UDP);
 
     /// @brief Prepares on-wire format.
     ///
@@ -61,8 +62,7 @@ public:
     /// will be set in data_len_.
     ///
     /// @return true if packing procedure was successful
-    bool
-    pack();
+    bool pack();
 
     /// @brief Dispatch method that handles binary packet parsing.
     ///
@@ -70,59 +70,74 @@ public:
     /// unpackTCP).
     ///
     /// @return true if parsing was successful
-    bool
-    unpack();
+    bool unpack();
 
-    /// Returns protocol of this packet (UDP or TCP)
+    /// @brief Returns reference to output buffer.
+    ///
+    /// Returned buffer will contain reasonable data only for
+    /// output (TX) packet and after pack() was called. This buffer
+    /// is only valid till Pkt6 object is valid.
+    ///
+    /// RX packet or TX packet before pack() will return buffer with
+    /// zero length
+    ///
+    /// @return reference to output buffer
+    const isc::util::OutputBuffer& getBuffer() const { return (bufferOut_); };
+
+
+    /// @brief Returns reference to input buffer.
+    ///
+    /// @return reference to input buffer
+    const OptionBuffer& getData() const { return(data_); }
+
+    /// @brief Returns protocol of this packet (UDP or TCP).
     ///
     /// @return protocol type
-    DHCPv6Proto
-    getProto();
+    DHCPv6Proto getProto();
 
     /// Sets protocol of this packet.
     ///
     /// @param proto protocol (UDP or TCP)
-    ///
-    void
-    setProto(DHCPv6Proto proto = UDP) { proto_ = proto; }
+    void setProto(DHCPv6Proto proto = UDP) { proto_ = proto; }
 
     /// @brief Returns text representation of the packet.
     ///
     /// This function is useful mainly for debugging.
     ///
     /// @return string with text representation
-    std::string
-    toText();
+    std::string toText();
 
-    /// @brief Returns calculated length of the packet.
+    /// @brief Returns length of the packet.
+    ///
+    /// This function returns size required to hold this packet.
+    /// It includes DHCPv6 header and all options stored in
+    /// options_ field.
     ///
-    /// This function returns size of required buffer to buld this packet.
-    /// To use that function, options_ field must be set.
+    /// Note: It does not return proper length of incoming packets
+    /// before they are unpacked.
     ///
-    /// @return number of bytes required to build this packet
-    unsigned short
-    len();
+    /// @return number of bytes required to assemble this packet
+    uint16_t len();
 
     /// Returns message type (e.g. 1 = SOLICIT)
     ///
     /// @return message type
-    unsigned char
-    getType() { return (msg_type_); }
+    uint8_t getType() { return (msg_type_); }
 
     /// Sets message type (e.g. 1 = SOLICIT)
     ///
     /// @param type message type to be set
-    void setType(unsigned char type) { msg_type_=type; };
+    void setType(uint8_t type) { msg_type_=type; };
 
     /// Returns value of transaction-id field
     ///
     /// @return transaction-id
-    unsigned int getTransid() { return (transid_); };
+    uint32_t getTransid() { return (transid_); };
 
     /// Adds an option to this packet.
     ///
     /// @param opt option to be added.
-    void addOption(boost::shared_ptr<isc::dhcp::Option> opt);
+    void addOption(OptionPtr opt);
 
     /// @brief Returns the first option of specified type.
     ///
@@ -130,52 +145,88 @@ public:
     /// instances of the same option are allowed (and frequently used).
     /// See getOptions().
     ///
-    /// @param opt_type option type we are looking for
+    /// @param type option type we are looking for
     ///
     /// @return pointer to found option (or NULL)
-    boost::shared_ptr<isc::dhcp::Option>
-    getOption(unsigned short type);
+    OptionPtr getOption(uint16_t type);
 
     /// Attempts to delete first suboption of requested type
     ///
     /// @param type Type of option to be deleted.
     ///
     /// @return true if option was deleted, false if no such option existed
-    bool
-    delOption(unsigned short type);
+    bool delOption(uint16_t type);
 
-    /// TODO need getter/setter wrappers
-    ///      and hide following fields as protected
+    /// @brief This method copies data from output buffer to input buffer
+    ///
+    /// This is useful only in testing
+    void repack();
 
-    /// buffer that holds memory. It is shared_array as options may
-    /// share pointer to this buffer
-    boost::shared_array<uint8_t> data_;
+    /// @brief Sets remote address.
+    ///
+    /// @param remote specifies remote address
+    void setRemoteAddr(const isc::asiolink::IOAddress& remote) { remote_addr_ = remote; }
 
-    /// length of the data
-    unsigned int data_len_;
+    /// @brief Returns remote address
+    ///
+    /// @return remote address
+    const isc::asiolink::IOAddress& getRemoteAddr() { return (remote_addr_); }
 
-    /// local address (dst if receiving packet, src if sending packet)
-    isc::asiolink::IOAddress local_addr_;
+    /// @brief Sets local address.
+    ///
+    /// @param local specifies local address
+    void setLocalAddr(const isc::asiolink::IOAddress& local) { local_addr_ = local; }
 
-    /// remote address (src if receiving packet, dst if sending packet)
-    isc::asiolink::IOAddress remote_addr_;
+    /// @brief Returns local address.
+    ///
+    /// @return local address
+    const isc::asiolink::IOAddress& getLocalAddr() { return (local_addr_); }
 
-    /// name of the network interface the packet was received/to be sent over
-    std::string iface_;
+    /// @brief Sets local port.
+    ///
+    /// @param local specifies local port
+    void setLocalPort(uint16_t local) { local_port_ = local; }
 
-    /// @brief interface index
+    /// @brief Returns local port.
     ///
-    /// interface index (each network interface has assigned unique ifindex
-    /// it is functional equvalent of name, but sometimes more useful, e.g.
-    /// when using crazy systems that allow spaces in interface names
-    /// e.g. windows
-    int ifindex_;
+    /// @return local port
+    uint16_t getLocalPort() { return (local_port_); }
 
-    /// local TDP or UDP port
-    int local_port_;
+    /// @brief Sets remote port.
+    ///
+    /// @param remote specifies remote port
+    void setRemotePort(uint16_t remote) { remote_port_ = remote; }
 
-    /// remote TCP or UDP port
-    int remote_port_;
+    /// @brief Returns remote port.
+    ///
+    /// @return remote port
+    uint16_t getRemotePort() { return (remote_port_); }
+
+    /// @brief Sets interface index.
+    ///
+    /// @param ifindex specifies interface index.
+    void setIndex(uint32_t ifindex) { ifindex_ = ifindex; };
+
+    /// @brief Returns interface index.
+    ///
+    /// @return interface index
+    uint32_t getIndex() const { return (ifindex_); };
+
+    /// @brief Returns interface name.
+    ///
+    /// Returns interface name over which packet was received or is
+    /// going to be transmitted.
+    ///
+    /// @return interface name
+    std::string getIface() const { return iface_; };
+
+    /// @brief Sets interface name.
+    ///
+    /// Sets interface name over which packet was received or is
+    /// going to be transmitted.
+    ///
+    /// @return interface name
+    void setIface(const std::string& iface ) { iface_ = iface; };
 
     /// TODO Need to implement getOptions() as well
 
@@ -221,12 +272,43 @@ protected:
     DHCPv6Proto proto_;
 
     /// DHCPv6 message type
-    int msg_type_;
+    uint8_t msg_type_;
 
     /// DHCPv6 transaction-id
-    unsigned int transid_;
+    uint32_t transid_;
+
+    /// unparsed data (in received packets)
+    OptionBuffer data_;
+
+    /// name of the network interface the packet was received/to be sent over
+    std::string iface_;
+
+    /// @brief interface index
+    ///
+    /// interface index (each network interface has assigned unique ifindex
+    /// it is functional equvalent of name, but sometimes more useful, e.g.
+    /// when using crazy systems that allow spaces in interface names
+    /// e.g. windows
+    int ifindex_;
+
+    /// local address (dst if receiving packet, src if sending packet)
+    isc::asiolink::IOAddress local_addr_;
+
+    /// remote address (src if receiving packet, dst if sending packet)
+    isc::asiolink::IOAddress remote_addr_;
+
+    /// local TDP or UDP port
+    uint16_t local_port_;
+
+    /// remote TCP or UDP port
+    uint16_t remote_port_;
+
+    /// output buffer (used during message transmission)
+    isc::util::OutputBuffer bufferOut_;
 }; // Pkt6 class
 
+typedef boost::shared_ptr<Pkt6> Pkt6Ptr;
+
 } // isc::dhcp namespace
 
 } // isc namespace
diff --git a/src/lib/dhcp/tests/.gitignore b/src/lib/dhcp/tests/.gitignore
new file mode 100644
index 0000000..313429d
--- /dev/null
+++ b/src/lib/dhcp/tests/.gitignore
@@ -0,0 +1 @@
+/libdhcp++_unittests
diff --git a/src/lib/dhcp/tests/iface_mgr_unittest.cc b/src/lib/dhcp/tests/iface_mgr_unittest.cc
index fc41b89..83ba231 100644
--- a/src/lib/dhcp/tests/iface_mgr_unittest.cc
+++ b/src/lib/dhcp/tests/iface_mgr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -7,7 +7,7 @@
 // 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
+ // 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.
@@ -127,8 +127,8 @@ TEST_F(IfaceMgrTest, dhcp6Sniffer) {
         cout << "    pkt->ifindex_ = " << pkt->ifindex_ << ";" << endl;
         cout << "    pkt->iface_ = \"" << pkt->iface_ << "\";" << endl;
 
-        // TODO it is better to declare an array and then memcpy it to
-        // packet.
+        // TODO it is better to declare statically initialize the array
+        // and then memcpy it to packet.
         for (int i=0; i< pkt->data_len_; i++) {
             cout << "    pkt->data_[" << i << "]="
                  << (int)(unsigned char)pkt->data_[i] << "; ";
@@ -202,16 +202,16 @@ TEST_F(IfaceMgrTest, getIface) {
     // check that interface can be retrieved by ifindex
     IfaceMgr::Iface* tmp = ifacemgr->getIface(5);
     // ASSERT_NE(NULL, tmp); is not supported. hmmmm.
-    ASSERT_TRUE( tmp != NULL );
+    ASSERT_TRUE(tmp != NULL);
 
-    EXPECT_EQ( "en3", tmp->getName() );
+    EXPECT_EQ("en3", tmp->getName());
     EXPECT_EQ(5, tmp->getIndex());
 
     // check that interface can be retrieved by name
     tmp = ifacemgr->getIface("lo1");
-    ASSERT_TRUE( tmp != NULL );
+    ASSERT_TRUE(tmp != NULL);
 
-    EXPECT_EQ( "lo1", tmp->getName() );
+    EXPECT_EQ("lo1", tmp->getName());
     EXPECT_EQ(1, tmp->getIndex());
 
     // check that non-existing interfaces are not returned
@@ -237,7 +237,7 @@ TEST_F(IfaceMgrTest, detectIfaces_stub) {
 
     NakedIfaceMgr* ifacemgr = new NakedIfaceMgr();
 
-    ASSERT_TRUE( ifacemgr->getIface("eth0") != NULL );
+    ASSERT_TRUE(ifacemgr->getIface("eth0") != NULL);
 
     IfaceMgr::Iface* eth0 = ifacemgr->getIface("eth0");
 
@@ -247,7 +247,7 @@ TEST_F(IfaceMgrTest, detectIfaces_stub) {
 
     IOAddress addr = *addrs.begin();
 
-    EXPECT_STREQ( "fe80::1234", addr.toText().c_str() );
+    EXPECT_STREQ("fe80::1234", addr.toText().c_str());
 
     delete ifacemgr;
 }
@@ -263,8 +263,8 @@ TEST_F(IfaceMgrTest, sockets6) {
 
     IOAddress loAddr("::1");
 
-    Pkt6 pkt6(128);
-    pkt6.iface_ = LOOPBACK;
+    Pkt6 pkt6(DHCPV6_SOLICIT, 123);
+    pkt6.setIface(LOOPBACK);
 
     // bind multicast socket to port 10547
     int socket1 = ifacemgr->openSocket(LOOPBACK, loAddr, 10547);
@@ -335,38 +335,41 @@ TEST_F(IfaceMgrTest, sendReceive6) {
     EXPECT_GT(socket1, 0);
     EXPECT_GT(socket2, 0);
 
-    boost::shared_ptr<Pkt6> sendPkt(new Pkt6(128) );
 
     // prepare dummy payload
-    for (int i=0;i<128; i++) {
-        sendPkt->data_[i] = i;
+    uint8_t data[128];
+    for (int i = 0; i < 128; i++) {
+        data[i] = i;
     }
+    Pkt6Ptr sendPkt = Pkt6Ptr(new Pkt6(data, 128));
 
-    sendPkt->remote_port_ = 10547;
-    sendPkt->remote_addr_ = IOAddress("::1");
-    sendPkt->ifindex_ = 1;
-    sendPkt->iface_ = LOOPBACK;
+    sendPkt->repack();
 
-    boost::shared_ptr<Pkt6> rcvPkt;
+    sendPkt->setRemotePort(10547);
+    sendPkt->setRemoteAddr(IOAddress("::1"));
+    sendPkt->setIndex(1);
+    sendPkt->setIface(LOOPBACK);
+
+    Pkt6Ptr rcvPkt;
 
     EXPECT_EQ(true, ifacemgr->send(sendPkt));
 
     rcvPkt = ifacemgr->receive6();
 
-    ASSERT_TRUE( rcvPkt ); // received our own packet
+    ASSERT_TRUE(rcvPkt); // received our own packet
 
     // let's check that we received what was sent
-    EXPECT_EQ(sendPkt->data_len_, rcvPkt->data_len_);
-    EXPECT_EQ(0, memcmp(&sendPkt->data_[0], &rcvPkt->data_[0],
-                        rcvPkt->data_len_) );
+    ASSERT_EQ(sendPkt->getData().size(), rcvPkt->getData().size());
+    EXPECT_EQ(0, memcmp(&sendPkt->getData()[0], &rcvPkt->getData()[0],
+                        rcvPkt->getData().size()));
 
-    EXPECT_EQ(sendPkt->remote_addr_.toText(), rcvPkt->remote_addr_.toText());
+    EXPECT_EQ(sendPkt->getRemoteAddr().toText(), rcvPkt->getRemoteAddr().toText());
 
     // since we opened 2 sockets on the same interface and none of them is multicast,
     // none is preferred over the other for sending data, so we really should not
     // assume the one or the other will always be choosen for sending data. Therefore
     // we should accept both values as source ports.
-    EXPECT_TRUE( (rcvPkt->remote_port_ == 10546) || (rcvPkt->remote_port_ == 10547) );
+    EXPECT_TRUE((rcvPkt->getRemotePort() == 10546) || (rcvPkt->getRemotePort() == 10547));
 
     delete ifacemgr;
 }
@@ -427,7 +430,7 @@ TEST_F(IfaceMgrTest, sendReceive4) {
 
     rcvPkt = ifacemgr->receive4();
 
-    ASSERT_TRUE( rcvPkt ); // received our own packet
+    ASSERT_TRUE(rcvPkt); // received our own packet
 
     ASSERT_NO_THROW(
         rcvPkt->unpack();
@@ -589,7 +592,7 @@ TEST_F(IfaceMgrTest, socketInfo) {
     loopback->addSocket(sock1);
     loopback->addSocket(sock2);
 
-    Pkt6 pkt6(100);
+    Pkt6 pkt6(DHCPV6_REPLY, 123456);
 
     // pkt6 dos not have interface set yet
     EXPECT_THROW(
@@ -598,14 +601,14 @@ TEST_F(IfaceMgrTest, socketInfo) {
     );
 
     // try to send over non-existing interface
-    pkt6.iface_ = "nosuchinterface45";
+    pkt6.setIface("nosuchinterface45");
     EXPECT_THROW(
         ifacemgr->getSocket(pkt6),
         BadValue
     );
 
     // this will work
-    pkt6.iface_ = LOOPBACK;
+    pkt6.setIface(LOOPBACK);
     EXPECT_EQ(9, ifacemgr->getSocket(pkt6));
 
     bool deleted = false;
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index ee3b873..7e18be6 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -42,7 +42,7 @@ static const uint8_t packed[] = {
 };
 
 TEST(LibDhcpTest, packOptions6) {
-    boost::shared_array<uint8_t> buf(new uint8_t[512]);
+    OptionBuffer buf(512);
     isc::dhcp::Option::OptionCollection opts; // list of options
 
     // generate content for options
@@ -50,24 +50,23 @@ TEST(LibDhcpTest, packOptions6) {
         buf[i]=i+100;
     }
 
-    boost::shared_ptr<Option> opt1(new Option(Option::V6, 12, buf, 0, 5));
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13, buf, 5, 3));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 14, buf, 8, 2));
-    boost::shared_ptr<Option> opt4(new Option(Option::V6,256, buf,10, 4));
-    boost::shared_ptr<Option> opt5(new Option(Option::V6,257, buf,14, 1));
+    OptionPtr opt1(new Option(Option::V6, 12, buf.begin() + 0, buf.begin() + 5));
+    OptionPtr opt2(new Option(Option::V6, 13, buf.begin() + 5, buf.begin() + 8));
+    OptionPtr opt3(new Option(Option::V6, 14, buf.begin() + 8, buf.begin() + 10));
+    OptionPtr opt4(new Option(Option::V6,256, buf.begin() + 10,buf.begin() + 14));
+    OptionPtr opt5(new Option(Option::V6,257, buf.begin() + 14,buf.begin() + 15));
 
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+    opts.insert(pair<int, OptionPtr >(opt1->getType(), opt1));
+    opts.insert(pair<int, OptionPtr >(opt1->getType(), opt2));
+    opts.insert(pair<int, OptionPtr >(opt1->getType(), opt3));
+    opts.insert(pair<int, OptionPtr >(opt1->getType(), opt4));
+    opts.insert(pair<int, OptionPtr >(opt1->getType(), opt5));
 
-    unsigned int offset;
-    EXPECT_NO_THROW ({
-         offset = LibDHCP::packOptions6(buf, 512, 100, opts);
-    });
-    EXPECT_EQ(135, offset); // options should take 35 bytes
-    EXPECT_EQ(0, memcmp(&buf[100], packed, 35) );
+    OutputBuffer assembled(512);
+
+    EXPECT_NO_THROW(LibDHCP::packOptions6(assembled, opts));
+    EXPECT_EQ(35, assembled.getLength()); // options should take 35 bytes
+    EXPECT_EQ(0, memcmp(assembled.getData(), packed, 35) );
 }
 
 TEST(LibDhcpTest, unpackOptions6) {
@@ -78,17 +77,13 @@ TEST(LibDhcpTest, unpackOptions6) {
     // specific derived classes.
     isc::dhcp::Option::OptionCollection options; // list of options
 
-    // we can't use packed directly, as shared_array would try to
-    // free it eventually
-    boost::shared_array<uint8_t> buf(new uint8_t[512]);
+    OptionBuffer buf(512);
     memcpy(&buf[0], packed, 35);
 
-    unsigned int offset;
     EXPECT_NO_THROW ({
-        offset = LibDHCP::unpackOptions6(buf, 512, 0, 35, options);
+        LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin()+35), options);
     });
 
-    EXPECT_EQ(35, offset); // parsed first 35 bytes (offset 0..34)
     EXPECT_EQ(options.size(), 5); // there should be 5 options
 
     isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
@@ -153,25 +148,23 @@ TEST(LibDhcpTest, packOptions4) {
         payload[i][2] = i*10+2;
     }
 
-    boost::shared_ptr<Option> opt1(new Option(Option::V4, 12, payload[0]));
-    boost::shared_ptr<Option> opt2(new Option(Option::V4, 13, payload[1]));
-    boost::shared_ptr<Option> opt3(new Option(Option::V4, 14, payload[2]));
-    boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[3]));
-    boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[4]));
+    OptionPtr opt1(new Option(Option::V4, 12, payload[0]));
+    OptionPtr opt2(new Option(Option::V4, 13, payload[1]));
+    OptionPtr opt3(new Option(Option::V4, 14, payload[2]));
+    OptionPtr opt4(new Option(Option::V4,254, payload[3]));
+    OptionPtr opt5(new Option(Option::V4,128, payload[4]));
 
     isc::dhcp::Option::OptionCollection opts; // list of options
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt1));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt2));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt3));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt4));
-    opts.insert(pair<int, boost::shared_ptr<Option> >(opt1->getType(), opt5));
+    opts.insert(make_pair(opt1->getType(), opt1));
+    opts.insert(make_pair(opt1->getType(), opt2));
+    opts.insert(make_pair(opt1->getType(), opt3));
+    opts.insert(make_pair(opt1->getType(), opt4));
+    opts.insert(make_pair(opt1->getType(), opt5));
 
     vector<uint8_t> expVect(v4Opts, v4Opts + sizeof(v4Opts));
 
     OutputBuffer buf(100);
-    EXPECT_NO_THROW (
-        LibDHCP::packOptions(buf, opts);
-    );
+    EXPECT_NO_THROW(LibDHCP::packOptions(buf, opts));
     ASSERT_EQ(buf.getLength(), sizeof(v4Opts));
     EXPECT_EQ(0, memcmp(v4Opts, buf.getData(), sizeof(v4Opts)));
 
diff --git a/src/lib/dhcp/tests/option6_addrlst_unittest.cc b/src/lib/dhcp/tests/option6_addrlst_unittest.cc
index 60b618b..89d4f7c 100644
--- a/src/lib/dhcp/tests/option6_addrlst_unittest.cc
+++ b/src/lib/dhcp/tests/option6_addrlst_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -21,17 +21,24 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/option.h>
 #include <dhcp/option6_addrlst.h>
+#include <util/buffer.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
+using namespace isc::util;
 
 namespace {
 class Option6AddrLstTest : public ::testing::Test {
 public:
-    Option6AddrLstTest() {
+    Option6AddrLstTest(): buf_(255), outBuf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
     }
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
 };
 
 TEST_F(Option6AddrLstTest, basic) {
@@ -97,16 +104,12 @@ TEST_F(Option6AddrLstTest, basic) {
         0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
     };
 
-    boost::shared_array<uint8_t> buf(new uint8_t[300]);
-    for (int i = 0; i < 300; i++)
-        buf[i] = 0;
-
-    memcpy(&buf[0], sampledata, 48);
+    memcpy(&buf_[0], sampledata, 48);
 
     // just a single address
     Option6AddrLst* opt1 = 0;
     EXPECT_NO_THROW(
-        opt1 = new Option6AddrLst(D6O_NAME_SERVERS, buf, 128, 0, 16);
+        opt1 = new Option6AddrLst(D6O_NAME_SERVERS, buf_.begin(), buf_.begin() + 16 );
     );
 
     EXPECT_EQ(Option::V6, opt1->getUniverse());
@@ -118,17 +121,16 @@ TEST_F(Option6AddrLstTest, basic) {
     IOAddress addr = addrs[0];
     EXPECT_EQ("2001:db8:1::dead:beef", addr.toText());
 
-    // pack this option again in the same buffer, but in
-    // different place
-    int offset = opt1->pack(buf,300, 100);
+    // pack this option
+    opt1->pack(outBuf_);
 
-    EXPECT_EQ(120, offset);
-    EXPECT_EQ( 0, memcmp(expected1, &buf[100], 20) );
+    EXPECT_EQ(20, outBuf_.getLength());
+    EXPECT_EQ(0, memcmp(expected1, outBuf_.getData(), 20));
 
     // two addresses
     Option6AddrLst* opt2 = 0;
     EXPECT_NO_THROW(
-        opt2 = new Option6AddrLst(D6O_SIP_SERVERS_ADDR, buf, 128, 0, 32);
+        opt2 = new Option6AddrLst(D6O_SIP_SERVERS_ADDR, buf_.begin(), buf_.begin() + 32);
     );
     EXPECT_EQ(D6O_SIP_SERVERS_ADDR, opt2->getType());
     EXPECT_EQ(36, opt2->len());
@@ -137,17 +139,17 @@ TEST_F(Option6AddrLstTest, basic) {
     EXPECT_EQ("2001:db8:1::dead:beef", addrs[0].toText());
     EXPECT_EQ("ff02::face:b00c", addrs[1].toText());
 
-    // pack this option again in the same buffer, but in
-    // different place
-    offset = opt2->pack(buf,300, 150);
+    // pack this option
+    outBuf_.clear();
+    opt2->pack(outBuf_);
 
-    EXPECT_EQ(150+36, offset);
-    EXPECT_EQ( 0, memcmp(expected2, &buf[150], 36));
+    EXPECT_EQ(36, outBuf_.getLength() );
+    EXPECT_EQ(0, memcmp(expected2, outBuf_.getData(), 36));
 
     // three addresses
     Option6AddrLst* opt3 = 0;
     EXPECT_NO_THROW(
-        opt3 = new Option6AddrLst(D6O_NIS_SERVERS, buf, 128, 0, 48);
+        opt3 = new Option6AddrLst(D6O_NIS_SERVERS, buf_.begin(), buf_.begin() + 48);
     );
 
     EXPECT_EQ(D6O_NIS_SERVERS, opt3->getType());
@@ -158,12 +160,12 @@ TEST_F(Option6AddrLstTest, basic) {
     EXPECT_EQ("ff02::face:b00c", addrs[1].toText());
     EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", addrs[2].toText());
 
-    // pack this option again in the same buffer, but in
-    // different place
-    offset = opt3->pack(buf,300, 200);
+    // pack this option
+    outBuf_.clear();
+    opt3->pack(outBuf_);
 
-    EXPECT_EQ(252, offset);
-    EXPECT_EQ( 0, memcmp(expected3, &buf[200], 52) );
+    EXPECT_EQ(52, outBuf_.getLength());
+    EXPECT_EQ(0, memcmp(expected3, outBuf_.getData(), 52));
 
     EXPECT_NO_THROW(
         delete opt1;
diff --git a/src/lib/dhcp/tests/option6_ia_unittest.cc b/src/lib/dhcp/tests/option6_ia_unittest.cc
index 3fd52f5..47af50e 100644
--- a/src/lib/dhcp/tests/option6_ia_unittest.cc
+++ b/src/lib/dhcp/tests/option6_ia_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -19,53 +19,51 @@
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 
-#include <boost/shared_array.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include "dhcp/dhcp6.h"
-#include "dhcp/option.h"
-#include "dhcp/option6_ia.h"
-#include "dhcp/option6_iaaddr.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_ia.h>
+#include <dhcp/option6_iaaddr.h>
+#include <util/buffer.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
 using namespace isc::asiolink;
+using namespace isc::util;
 
 namespace {
 class Option6IATest : public ::testing::Test {
 public:
-    Option6IATest() {
+    Option6IATest(): buf_(255), outBuf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
     }
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
 };
 
 TEST_F(Option6IATest, basic) {
+    buf_[0] = 0xa1; // iaid
+    buf_[1] = 0xa2;
+    buf_[2] = 0xa3;
+    buf_[3] = 0xa4;
 
-    boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
-    for (int i = 0; i < 128; i++)
-        simple_buf[i] = 0;
-    simple_buf[0] = 0xa1; // iaid
-    simple_buf[1] = 0xa2;
-    simple_buf[2] = 0xa3;
-    simple_buf[3] = 0xa4;
-
-    simple_buf[4] = 0x81; // T1
-    simple_buf[5] = 0x02;
-    simple_buf[6] = 0x03;
-    simple_buf[7] = 0x04;
+    buf_[4] = 0x81; // T1
+    buf_[5] = 0x02;
+    buf_[6] = 0x03;
+    buf_[7] = 0x04;
 
-    simple_buf[8] = 0x84; // T2
-    simple_buf[9] = 0x03;
-    simple_buf[10] = 0x02;
-    simple_buf[11] = 0x01;
+    buf_[8] = 0x84; // T2
+    buf_[9] = 0x03;
+    buf_[10] = 0x02;
+    buf_[11] = 0x01;
 
     // create an option
     // unpack() is called from constructor
     Option6IA* opt = new Option6IA(D6O_IA_NA,
-                                   simple_buf,
-                                   128,
-                                   0,
-                                   12);
+                                   buf_.begin(),
+                                   buf_.begin() + 12);
 
     EXPECT_EQ(Option::V6, opt->getUniverse());
     EXPECT_EQ(D6O_IA_NA, opt->getType());
@@ -77,36 +75,31 @@ TEST_F(Option6IATest, basic) {
     // different place
 
     // test for pack()
-    int offset = opt->pack(simple_buf, 128, 60);
+    opt->pack(outBuf_);
 
-    // 4 bytes header + 4 bytes content
-    EXPECT_EQ(12, opt->len() - 4);
+    // 12 bytes header + 4 bytes content
+    EXPECT_EQ(12, opt->len() - opt->getHeaderLen());
     EXPECT_EQ(D6O_IA_NA, opt->getType());
 
-    EXPECT_EQ(offset, 76); // 60 + lenght(IA_NA) = 76
+    EXPECT_EQ(16, outBuf_.getLength()); // lenght(IA_NA) = 16
 
     // check if pack worked properly:
+    InputBuffer out(outBuf_.getData(), outBuf_.getLength());
+
     // if option type is correct
-    EXPECT_EQ(D6O_IA_NA, simple_buf[60]*256 + simple_buf[61]);
+    EXPECT_EQ(D6O_IA_NA, out.readUint16());
 
     // if option length is correct
-    EXPECT_EQ(12, simple_buf[62]*256 + simple_buf[63]);
+    EXPECT_EQ(12, out.readUint16());
 
     // if iaid is correct
-    unsigned int iaid = htonl(*(unsigned int*)&simple_buf[64]);
-    EXPECT_EQ(0xa1a2a3a4, iaid );
+    EXPECT_EQ(0xa1a2a3a4, out.readUint32() );
 
    // if T1 is correct
-    EXPECT_EQ(0x81020304, (simple_buf[68] << 24) +
-                          (simple_buf[69] << 16) +
-                          (simple_buf[70] << 8) +
-                          (simple_buf[71]) );
+    EXPECT_EQ(0x81020304, out.readUint32() );
 
     // if T1 is correct
-    EXPECT_EQ(0x84030201, (simple_buf[72] << 24) +
-                          (simple_buf[73] << 16) +
-                          (simple_buf[74] << 8) +
-                          (simple_buf[75]) );
+    EXPECT_EQ(0x84030201, out.readUint32() );
 
     EXPECT_NO_THROW(
         delete opt;
@@ -114,10 +107,6 @@ TEST_F(Option6IATest, basic) {
 }
 
 TEST_F(Option6IATest, simple) {
-    boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
-    for (int i = 0; i < 128; i++)
-        simple_buf[i] = 0;
-
     Option6IA * ia = new Option6IA(D6O_IA_NA, 1234);
     ia->setT1(2345);
     ia->setT2(3456);
@@ -133,25 +122,21 @@ TEST_F(Option6IATest, simple) {
     );
 }
 
+
 // test if option can build suboptions
 TEST_F(Option6IATest, suboptions_pack) {
-    boost::shared_array<uint8_t> buf(new uint8_t[128]);
-    for (int i=0; i<128; i++)
-        buf[i] = 0;
-    buf[0] = 0xff;
-    buf[1] = 0xfe;
-    buf[2] = 0xfc;
+    buf_[0] = 0xff;
+    buf_[1] = 0xfe;
+    buf_[2] = 0xfc;
 
     Option6IA * ia = new Option6IA(D6O_IA_NA, 0x13579ace);
     ia->setT1(0x2345);
     ia->setT2(0x3456);
 
-    boost::shared_ptr<Option> sub1(new Option(Option::V6,
-                                              0xcafe));
+    OptionPtr sub1(new Option(Option::V6, 0xcafe));
 
     boost::shared_ptr<Option6IAAddr> addr1(
-        new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"),
-                          0x5000, 0x7000));
+        new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"), 0x5000, 0x7000));
 
     ia->addOption(sub1);
     ia->addOption(addr1);
@@ -180,29 +165,29 @@ TEST_F(Option6IATest, suboptions_pack) {
         0, 0 // len
     };
 
-    int offset = ia->pack(buf, 128, 10);
-    ASSERT_EQ(offset, 10 + 48);
+    ia->pack(outBuf_);
+    ASSERT_EQ(48, outBuf_.getLength());
 
-    EXPECT_EQ(0, memcmp(&buf[10], expected, 48));
+    EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 48));
 
     EXPECT_NO_THROW(
         delete ia;
     );
 }
 
+
 // test if option can parse suboptions
 TEST_F(Option6IATest, suboptions_unpack) {
-
-
+    // sizeof (expected) = 48 bytes
     uint8_t expected[] = {
-        D6O_IA_NA/256, D6O_IA_NA%256, // type
+        D6O_IA_NA / 256, D6O_IA_NA % 256, // type
         0, 28, // length
         0x13, 0x57, 0x9a, 0xce, // iaid
         0, 0, 0x23, 0x45,  // T1
         0, 0, 0x34, 0x56,  // T2
 
         // iaaddr suboption
-        D6O_IAADDR/256, D6O_IAADDR%256, // type
+        D6O_IAADDR / 256, D6O_IAADDR % 256, // type
         0, 24, // len
         0x20, 0x01, 0xd, 0xb8, 0x12,0x34, 0x56, 0x78,
         0, 0, 0, 0, 0, 0, 0xab, 0xcd, // IP address
@@ -213,18 +198,13 @@ TEST_F(Option6IATest, suboptions_unpack) {
         0xca, 0xfe, // type
         0, 0 // len
     };
+    ASSERT_EQ(48, sizeof(expected));
 
-    boost::shared_array<uint8_t> buf(new uint8_t[128]);
-    for (int i = 0; i < 128; i++)
-        buf[i] = 0;
-    memcpy(&buf[0], expected, 48);
+    memcpy(&buf_[0], expected, sizeof(expected));
 
     Option6IA* ia = 0;
     EXPECT_NO_THROW({
-        ia = new Option6IA(D6O_IA_NA, buf, 128, 4, 44);
-
-        // let's limit verbosity of this test
-        // cout << "Parsed option:" << endl << ia->toText() << endl;
+            ia = new Option6IA(D6O_IA_NA, buf_.begin() + 4, buf_.begin() + sizeof(expected));
     });
     ASSERT_TRUE(ia);
 
@@ -233,8 +213,8 @@ TEST_F(Option6IATest, suboptions_unpack) {
     EXPECT_EQ(0x2345, ia->getT1());
     EXPECT_EQ(0x3456, ia->getT2());
 
-    boost::shared_ptr<Option> subopt = ia->getOption(D6O_IAADDR);
-    ASSERT_NE(boost::shared_ptr<Option>(), subopt); // non-NULL
+    OptionPtr subopt = ia->getOption(D6O_IAADDR);
+    ASSERT_NE(OptionPtr(), subopt); // non-NULL
 
     // checks for address option
     Option6IAAddr * addr = dynamic_cast<Option6IAAddr*>(subopt.get());
diff --git a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
index 81c3eb3..e351d17 100644
--- a/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
+++ b/src/lib/dhcp/tests/option6_iaaddr_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -19,61 +19,62 @@
 #include <arpa/inet.h>
 #include <gtest/gtest.h>
 
-#include "dhcp/dhcp6.h"
-#include "dhcp/option.h"
-#include "dhcp/option6_iaaddr.h"
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaaddr.h>
+#include <util/buffer.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
+using namespace isc::util;
 
 namespace {
 class Option6IAAddrTest : public ::testing::Test {
 public:
-    Option6IAAddrTest() {
+    Option6IAAddrTest() : buf_(255), outBuf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
     }
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
 };
 
-/// TODO reenable this once ticket #1313 is implemented.
 TEST_F(Option6IAAddrTest, basic) {
-
-    boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
-    for (int i = 0; i < 128; i++)
-        simple_buf[i] = 0;
-
-    simple_buf[0] = 0x20;
-    simple_buf[1] = 0x01;
-    simple_buf[2] = 0x0d;
-    simple_buf[3] = 0xb8;
-    simple_buf[4] = 0x00;
-    simple_buf[5] = 0x01;
-    simple_buf[12] = 0xde;
-    simple_buf[13] = 0xad;
-    simple_buf[14] = 0xbe;
-    simple_buf[15] = 0xef; // 2001:db8:1::dead:beef
-
-    simple_buf[16] = 0x00;
-    simple_buf[17] = 0x00;
-    simple_buf[18] = 0x03;
-    simple_buf[19] = 0xe8; // 1000
-
-    simple_buf[20] = 0xb2;
-    simple_buf[21] = 0xd0;
-    simple_buf[22] = 0x5e;
-    simple_buf[23] = 0x00; // 3,000,000,000
+    for (int i = 0; i < 255; i++) {
+        buf_[i] = 0;
+    }
+    buf_[0] = 0x20;
+    buf_[1] = 0x01;
+    buf_[2] = 0x0d;
+    buf_[3] = 0xb8;
+    buf_[4] = 0x00;
+    buf_[5] = 0x01;
+    buf_[12] = 0xde;
+    buf_[13] = 0xad;
+    buf_[14] = 0xbe;
+    buf_[15] = 0xef; // 2001:db8:1::dead:beef
+
+    buf_[16] = 0x00;
+    buf_[17] = 0x00;
+    buf_[18] = 0x03;
+    buf_[19] = 0xe8; // 1000
+
+    buf_[20] = 0xb2;
+    buf_[21] = 0xd0;
+    buf_[22] = 0x5e;
+    buf_[23] = 0x00; // 3,000,000,000
 
     // create an option (unpack content)
     Option6IAAddr* opt = new Option6IAAddr(D6O_IAADDR,
-                                           simple_buf,
-                                           128,
-                                           0,
-                                           24);
+                                           buf_.begin(),
+                                           buf_.begin() + 24);
 
-    // pack this option again in the same buffer, but in
-    // different place
-    int offset = opt->pack(simple_buf, 128, 50);
+    // pack this option
+    opt->pack(outBuf_);
 
-    EXPECT_EQ(78, offset);
+    EXPECT_EQ(28, outBuf_.getLength());
 
     EXPECT_EQ(Option::V6, opt->getUniverse());
 
@@ -88,18 +89,18 @@ TEST_F(Option6IAAddrTest, basic) {
               opt->len());
 
     // check if pack worked properly:
+    const uint8_t* out = (const uint8_t*)outBuf_.getData();
+
     // if option type is correct
-    EXPECT_EQ(D6O_IAADDR, simple_buf[50]*256 + simple_buf[51]);
+    EXPECT_EQ(D6O_IAADDR, out[0]*256 + out[1]);
 
     // if option length is correct
-    EXPECT_EQ(24, simple_buf[52]*256 + simple_buf[53]);
+    EXPECT_EQ(24, out[2]*256 + out[3]);
 
     // if option content is correct
-    EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[54],24));
+    EXPECT_EQ(0, memcmp(out + 4, &buf_[0], 24));
 
-    EXPECT_NO_THROW(
-        delete opt;
-    );
+    EXPECT_NO_THROW( delete opt );
 }
 
 }
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index c83a839..5daf75d 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -33,14 +33,13 @@ using namespace isc::util;
 namespace {
 class OptionTest : public ::testing::Test {
 public:
-    OptionTest(): outBuffer_(255) {
-        buf_ = boost::shared_array<uint8_t>(new uint8_t[255]);
+    OptionTest(): buf_(255), outBuf_(255) {
         for (int i = 0; i < 255; i++) {
             buf_[i] = 255 - i;
         }
     }
-    boost::shared_array<uint8_t> buf_;
-    OutputBuffer outBuffer_;
+    OptionBuffer buf_;
+    OutputBuffer outBuf_;
 };
 
 // v4 is not really implemented yet. A simple test will do for now
@@ -66,10 +65,27 @@ TEST_F(OptionTest, v4_basic) {
         opt = new Option(Option::V4, 256),
         BadValue
     );
-    if (opt) {
-        delete opt;
-        opt = 0;
-    }
+
+    delete opt;
+    opt = 0;
+
+    // 0 is a special PAD option
+    EXPECT_THROW(
+        opt = new Option(Option::V4, 0),
+        BadValue
+    );
+
+    delete opt;
+    opt = 0;
+
+    // 255 is a special END option
+    EXPECT_THROW(
+        opt = new Option(Option::V4, 255),
+        BadValue
+    );
+
+    delete opt;
+    opt = 0;
 }
 
 const uint8_t dummyPayload[] =
@@ -84,9 +100,7 @@ TEST_F(OptionTest, v4_data1) {
     // create DHCPv4 option of type 123
     // that contains 4 bytes of data
     ASSERT_NO_THROW(
-        opt= new Option(Option::V4,
-                        123, // type
-                        data);
+        opt= new Option(Option::V4, 123, data);
     );
 
     // check that content is reported properly
@@ -143,10 +157,7 @@ TEST_F(OptionTest, v4_data2) {
     // Create DHCPv4 option of type 123 that contains
     // 4 bytes (sizeof(dummyPayload).
     ASSERT_NO_THROW(
-        opt= new Option(Option::V4,
-                        123, // type
-                        data.begin() + 1,
-                        data.end() - 1);
+        opt= new Option(Option::V4, 123, data.begin() + 1, data.end() - 1);
     );
 
     // check that content is reported properly
@@ -210,30 +221,29 @@ TEST_F(OptionTest, v6_basic) {
 // tests contructor used in pkt reception
 // option contains actual data
 TEST_F(OptionTest, v6_data1) {
-    boost::shared_array<uint8_t> buf(new uint8_t[32]);
     for (int i = 0; i < 32; i++)
-        buf[i] = 100+i;
-    Option* opt = new Option(Option::V6, 333, //type
-                             buf,
-                             3, // offset
-                             7); // 7 bytes of data
+        buf_[i] = 100+i;
+    Option* opt = new Option(Option::V6, 333,   //type
+                             buf_.begin() + 3,  // begin offset
+                             buf_.begin() + 10); // end offset (7 bytes of data)
     EXPECT_EQ(333, opt->getType());
 
     ASSERT_EQ(11, opt->len());
     ASSERT_EQ(7, opt->getData().size());
-    EXPECT_EQ(0, memcmp(&buf[3], &opt->getData()[0], 7) );
+    EXPECT_EQ(0, memcmp(&buf_[3], &opt->getData()[0], 7) );
 
-    int offset = opt->pack(buf, 32, 20);
-    EXPECT_EQ(31, offset);
+    opt->pack(outBuf_);
+    EXPECT_EQ(11, outBuf_.getLength());
 
-    EXPECT_EQ(buf[20], 333/256); // type
-    EXPECT_EQ(buf[21], 333%256);
+    const uint8_t* out = (const uint8_t*)outBuf_.getData();
+    EXPECT_EQ(out[0], 333/256); // type
+    EXPECT_EQ(out[1], 333%256);
 
-    EXPECT_EQ(buf[22], 0); // len
-    EXPECT_EQ(buf[23], 7);
+    EXPECT_EQ(out[2], 0); // len
+    EXPECT_EQ(out[3], 7);
 
     // payload
-    EXPECT_EQ(0, memcmp(&buf[3], &buf[24], 7) );
+    EXPECT_EQ(0, memcmp(&buf_[3], out+4, 7) );
 
     EXPECT_NO_THROW(
         delete opt;
@@ -244,40 +254,37 @@ TEST_F(OptionTest, v6_data1) {
 // with different input parameters
 TEST_F(OptionTest, v6_data2) {
 
-    boost::shared_array<uint8_t> simple_buf(new uint8_t[128]);
-    for (int i = 0; i < 128; i++)
-        simple_buf[i] = 0;
-    simple_buf[0] = 0xa1;
-    simple_buf[1] = 0xa2;
-    simple_buf[2] = 0xa3;
-    simple_buf[3] = 0xa4;
+    buf_[0] = 0xa1;
+    buf_[1] = 0xa2;
+    buf_[2] = 0xa3;
+    buf_[3] = 0xa4;
 
     // create an option (unpack content)
     Option* opt = new Option(Option::V6,
                              D6O_CLIENTID,
-                             simple_buf,
-                             0,
-                             4);
+                             buf_.begin(),
+                             buf_.begin() + 4);
 
-    // pack this option again in the same buffer, but in
-    // different place
-    int offset18 = opt->pack(simple_buf, 128, 10);
+    // pack this option
+    opt->pack(outBuf_);
 
     // 4 bytes header + 4 bytes content
     EXPECT_EQ(8, opt->len());
     EXPECT_EQ(D6O_CLIENTID, opt->getType());
 
-    EXPECT_EQ(offset18, 18);
+    EXPECT_EQ(8, outBuf_.getLength());
 
     // check if pack worked properly:
     // if option type is correct
-    EXPECT_EQ(D6O_CLIENTID, simple_buf[10]*256 + simple_buf[11]);
+    const uint8_t* out = (const uint8_t*)outBuf_.getData();
+
+    EXPECT_EQ(D6O_CLIENTID, out[0]*256 + out[1]);
 
     // if option length is correct
-    EXPECT_EQ(4, simple_buf[12]*256 + simple_buf[13]);
+    EXPECT_EQ(4, out[2]*256 + out[3]);
 
     // if option content is correct
-    EXPECT_EQ(0, memcmp(&simple_buf[0], &simple_buf[14],4));
+    EXPECT_EQ(0, memcmp(&buf_[0], out + 4, 4));
 
     EXPECT_NO_THROW(
         delete opt;
@@ -291,18 +298,15 @@ TEST_F(OptionTest, v6_data2) {
 //  +----opt3
 //
 TEST_F(OptionTest, v6_suboptions1) {
-    boost::shared_array<uint8_t> buf(new uint8_t[128]);
     for (int i=0; i<128; i++)
-        buf[i] = 100+i;
+        buf_[i] = 100+i;
     Option* opt1 = new Option(Option::V6, 65535, //type
-                              buf,
-                              0, // offset
-                              3); // 3 bytes of data
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
-                                              buf,
-                                              3, // offset
-                                              5)); // 5 bytes of data
+                              buf_.begin(), // 3 bytes of data
+                              buf_.begin() + 3);
+    OptionPtr opt2(new Option(Option::V6, 13));
+    OptionPtr opt3(new Option(Option::V6, 7,
+                              buf_.begin() + 3,
+                              buf_.begin() + 8)); // 5 bytes of data
     opt1->addOption(opt2);
     opt1->addOption(opt3);
     // opt2 len = 4 (just header)
@@ -319,11 +323,11 @@ TEST_F(OptionTest, v6_suboptions1) {
         0, 13, 0, 0 // no data at all
     };
 
-    int offset = opt1->pack(buf, 128, 20);
-    EXPECT_EQ(40, offset);
+    opt1->pack(outBuf_);
+    EXPECT_EQ(20, outBuf_.getLength());
 
     // payload
-    EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
+    EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 20) );
 
     EXPECT_NO_THROW(
         delete opt1;
@@ -337,18 +341,15 @@ TEST_F(OptionTest, v6_suboptions1) {
 //        +----opt3
 //
 TEST_F(OptionTest, v6_suboptions2) {
-    boost::shared_array<uint8_t> buf(new uint8_t[128]);
     for (int i=0; i<128; i++)
-        buf[i] = 100+i;
+        buf_[i] = 100+i;
     Option* opt1 = new Option(Option::V6, 65535, //type
-                              buf,
-                              0, // offset
-                              3); // 3 bytes of data
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 13));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 7,
-                                              buf,
-                                              3, // offset
-                                              5)); // 5 bytes of data
+                              buf_.begin(),
+                              buf_.begin() + 3);
+    OptionPtr opt2(new Option(Option::V6, 13));
+    OptionPtr opt3(new Option(Option::V6, 7,
+                                              buf_.begin() + 3,
+                                              buf_.begin() + 8));
     opt1->addOption(opt2);
     opt2->addOption(opt3);
     // opt3 len = 9 4(header)+5(data)
@@ -361,11 +362,11 @@ TEST_F(OptionTest, v6_suboptions2) {
         0, 7, 0, 5, 103, 104, 105, 106, 107,
     };
 
-    int offset = opt1->pack(buf, 128, 20);
-    EXPECT_EQ(40, offset);
+    opt1->pack(outBuf_);
+    EXPECT_EQ(20, outBuf_.getLength());
 
     // payload
-    EXPECT_EQ(0, memcmp(&buf[20], expected, 20) );
+    EXPECT_EQ(0, memcmp(outBuf_.getData(), expected, 20) );
 
     EXPECT_NO_THROW(
         delete opt1;
@@ -373,13 +374,12 @@ TEST_F(OptionTest, v6_suboptions2) {
 }
 
 TEST_F(OptionTest, v6_addgetdel) {
-    boost::shared_array<uint8_t> buf(new uint8_t[128]);
     for (int i=0; i<128; i++)
-        buf[i] = 100+i;
+        buf_[i] = 100+i;
     Option* parent = new Option(Option::V6, 65535); //type
-    boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 2));
+    OptionPtr opt1(new Option(Option::V6, 1));
+    OptionPtr opt2(new Option(Option::V6, 2));
+    OptionPtr opt3(new Option(Option::V6, 2));
 
     parent->addOption(opt1);
     parent->addOption(opt2);
@@ -389,7 +389,7 @@ TEST_F(OptionTest, v6_addgetdel) {
     EXPECT_EQ(opt2, parent->getOption(2));
 
     // expect NULL
-    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(4));
+    EXPECT_EQ(OptionPtr(), parent->getOption(4));
 
     // now there are 2 options of type 2
     parent->addOption(opt3);
@@ -398,13 +398,13 @@ TEST_F(OptionTest, v6_addgetdel) {
     EXPECT_EQ(true, parent->delOption(2));
 
     // there still should be the other option 2
-    EXPECT_NE(boost::shared_ptr<Option>(), parent->getOption(2));
+    EXPECT_NE(OptionPtr(), parent->getOption(2));
 
     // let's delete the other option 2
     EXPECT_EQ(true, parent->delOption(2));
 
     // no more options with type=2
-    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(2));
+    EXPECT_EQ(OptionPtr(), parent->getOption(2));
 
     // let's try to delete - should fail
     EXPECT_TRUE(false ==  parent->delOption(2));
@@ -412,36 +412,31 @@ TEST_F(OptionTest, v6_addgetdel) {
     delete parent;
 }
 
-}
-
 TEST_F(OptionTest, v6_toText) {
-    boost::shared_array<uint8_t> buf(new uint8_t[3]);
-    buf[0] = 0;
-    buf[1] = 0xf;
-    buf[2] = 0xff;
+    buf_[0] = 0;
+    buf_[1] = 0xf;
+    buf_[2] = 0xff;
 
-    boost::shared_ptr<Option> opt(new Option(Option::V6, 258,
-                                             buf, 0, 3));
+    OptionPtr opt(new Option(Option::V6, 258,  buf_.begin(), buf_.begin() + 3 ));
 
     EXPECT_EQ("type=258, len=3: 00:0f:ff", opt->toText());
 }
 
+
 TEST_F(OptionTest, getUintX) {
 
-    // TODO: Update this test to use buf_ instead of buf
-    boost::shared_array<uint8_t> buf(new uint8_t[5]);
-    buf[0] = 0x5;
-    buf[1] = 0x4;
-    buf[2] = 0x3;
-    buf[3] = 0x2;
-    buf[4] = 0x1;
+    buf_[0] = 0x5;
+    buf_[1] = 0x4;
+    buf_[2] = 0x3;
+    buf_[3] = 0x2;
+    buf_[4] = 0x1;
 
     // five options with varying lengths
-    boost::shared_ptr<Option> opt1(new Option(Option::V6, 258, buf, 0, 1));
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 258, buf, 0, 2));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 258, buf, 0, 3));
-    boost::shared_ptr<Option> opt4(new Option(Option::V6, 258, buf, 0, 4));
-    boost::shared_ptr<Option> opt5(new Option(Option::V6, 258, buf, 0, 5));
+    OptionPtr opt1(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 1));
+    OptionPtr opt2(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 2));
+    OptionPtr opt3(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 3));
+    OptionPtr opt4(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 4));
+    OptionPtr opt5(new Option(Option::V6, 258, buf_.begin(), buf_.begin() + 5));
 
     EXPECT_EQ(5, opt1->getUint8());
     EXPECT_THROW(opt1->getUint16(), OutOfRange);
@@ -467,36 +462,37 @@ TEST_F(OptionTest, getUintX) {
 }
 
 TEST_F(OptionTest, setUintX) {
-    boost::shared_ptr<Option> opt1(new Option(Option::V4, 125));
-    boost::shared_ptr<Option> opt2(new Option(Option::V4, 125));
-    boost::shared_ptr<Option> opt4(new Option(Option::V4, 125));
+    OptionPtr opt1(new Option(Option::V4, 125));
+    OptionPtr opt2(new Option(Option::V4, 125));
+    OptionPtr opt4(new Option(Option::V4, 125));
 
     // verify setUint8
     opt1->setUint8(255);
     EXPECT_EQ(255, opt1->getUint8());
-    opt1->pack4(outBuffer_);
+    opt1->pack4(outBuf_);
     EXPECT_EQ(3, opt1->len());
-    EXPECT_EQ(3, outBuffer_.getLength());
+    EXPECT_EQ(3, outBuf_.getLength());
     uint8_t exp1[] = {125, 1, 255};
-    EXPECT_TRUE(0 == memcmp(exp1, outBuffer_.getData(), 3));
+    EXPECT_TRUE(0 == memcmp(exp1, outBuf_.getData(), 3));
 
     // verify getUint16
-    outBuffer_.clear();
+    outBuf_.clear();
     opt2->setUint16(12345);
-    opt2->pack4(outBuffer_);
+    opt2->pack4(outBuf_);
     EXPECT_EQ(12345, opt2->getUint16());
     EXPECT_EQ(4, opt2->len());
-    EXPECT_EQ(4, outBuffer_.getLength());
+    EXPECT_EQ(4, outBuf_.getLength());
     uint8_t exp2[] = {125, 2, 12345/256, 12345%256};
-    EXPECT_TRUE(0 == memcmp(exp2, outBuffer_.getData(), 4));
+    EXPECT_TRUE(0 == memcmp(exp2, outBuf_.getData(), 4));
 
     // verity getUint32
-    outBuffer_.clear();
+    outBuf_.clear();
     opt4->setUint32(0x12345678);
-    opt4->pack4(outBuffer_);
+    opt4->pack4(outBuf_);
     EXPECT_EQ(0x12345678, opt4->getUint32());
     EXPECT_EQ(6, opt4->len());
-    EXPECT_EQ(6, outBuffer_.getLength());
+    EXPECT_EQ(6, outBuf_.getLength());
     uint8_t exp4[] = {125, 4, 0x12, 0x34, 0x56, 0x78};
-    EXPECT_TRUE(0 == memcmp(exp4, outBuffer_.getData(), 6));
+    EXPECT_TRUE(0 == memcmp(exp4, outBuf_.getData(), 6));
+}
 }
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index 968b24c..e07ea9f 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -37,56 +37,67 @@ public:
 };
 
 TEST_F(Pkt6Test, constructor) {
-    Pkt6 * pkt1 = new Pkt6(17);
+    uint8_t data[] = { 0, 1, 2, 3, 4, 5 };
+    Pkt6 * pkt1 = new Pkt6(data, sizeof(data) );
 
-    EXPECT_EQ(pkt1->data_len_, 17);
+    EXPECT_EQ(6, pkt1->getData().size());
+    EXPECT_EQ(0, memcmp( &pkt1->getData()[0], data, sizeof(data)));
 
     delete pkt1;
 }
 
-// captured actual SOLICIT packet: transid=0x3d79fb
-// options: client-id, in_na, dns-server, elapsed-time, option-request
-// this code is autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c)
-Pkt6 *capture1() {
+/// @brief returns captured actual SOLICIT packet
+///
+/// Captured SOLICIT packet with transid=0x3d79fb and options: client-id,
+/// in_na, dns-server, elapsed-time, option-request
+/// This code was autogenerated (see src/bin/dhcp6/tests/iface_mgr_unittest.c),
+/// but we spent some time to make is less ugly than it used to be.
+///
+/// @return pointer to Pkt6 that represents received SOLICIT
+Pkt6* capture1() {
     Pkt6* pkt;
-    pkt = new Pkt6(98);
-    pkt->remote_port_ = 546;
-    pkt->remote_addr_ = IOAddress("fe80::21e:8cff:fe9b:7349");
-    pkt->local_port_ = 0;
-    pkt->local_addr_ = IOAddress("ff02::1:2");
-    pkt->ifindex_ = 2;
-    pkt->iface_ = "eth0";
-    pkt->data_[0]=1;
-    pkt->data_[1]=01;     pkt->data_[2]=02;     pkt->data_[3]=03;     pkt->data_[4]=0;
-    pkt->data_[5]=1;     pkt->data_[6]=0;     pkt->data_[7]=14;     pkt->data_[8]=0;
-    pkt->data_[9]=1;     pkt->data_[10]=0;     pkt->data_[11]=1;     pkt->data_[12]=21;
-    pkt->data_[13]=158;     pkt->data_[14]=60;     pkt->data_[15]=22;     pkt->data_[16]=0;
-    pkt->data_[17]=30;     pkt->data_[18]=140;     pkt->data_[19]=155;     pkt->data_[20]=115;
-    pkt->data_[21]=73;     pkt->data_[22]=0;     pkt->data_[23]=3;     pkt->data_[24]=0;
-    pkt->data_[25]=40;     pkt->data_[26]=0;     pkt->data_[27]=0;     pkt->data_[28]=0;
-    pkt->data_[29]=1;     pkt->data_[30]=255;     pkt->data_[31]=255;     pkt->data_[32]=255;
-    pkt->data_[33]=255;     pkt->data_[34]=255;     pkt->data_[35]=255;     pkt->data_[36]=255;
-    pkt->data_[37]=255;     pkt->data_[38]=0;     pkt->data_[39]=5;     pkt->data_[40]=0;
-    pkt->data_[41]=24;     pkt->data_[42]=32;     pkt->data_[43]=1;     pkt->data_[44]=13;
-    pkt->data_[45]=184;     pkt->data_[46]=0;     pkt->data_[47]=1;     pkt->data_[48]=0;
-    pkt->data_[49]=0;     pkt->data_[50]=0;     pkt->data_[51]=0;     pkt->data_[52]=0;
-    pkt->data_[53]=0;     pkt->data_[54]=0;     pkt->data_[55]=0;     pkt->data_[56]=18;
-    pkt->data_[57]=52;     pkt->data_[58]=255;     pkt->data_[59]=255;     pkt->data_[60]=255;
-    pkt->data_[61]=255;     pkt->data_[62]=255;     pkt->data_[63]=255;     pkt->data_[64]=255;
-    pkt->data_[65]=255;     pkt->data_[66]=0;     pkt->data_[67]=23;     pkt->data_[68]=0;
-    pkt->data_[69]=16;     pkt->data_[70]=32;     pkt->data_[71]=1;     pkt->data_[72]=13;
-    pkt->data_[73]=184;     pkt->data_[74]=0;     pkt->data_[75]=1;     pkt->data_[76]=0;
-    pkt->data_[77]=0;     pkt->data_[78]=0;     pkt->data_[79]=0;     pkt->data_[80]=0;
-    pkt->data_[81]=0;     pkt->data_[82]=0;     pkt->data_[83]=0;     pkt->data_[84]=221;
-    pkt->data_[85]=221;     pkt->data_[86]=0;     pkt->data_[87]=8;     pkt->data_[88]=0;
-    pkt->data_[89]=2;     pkt->data_[90]=0;     pkt->data_[91]=100;     pkt->data_[92]=0;
-    pkt->data_[93]=6;     pkt->data_[94]=0;     pkt->data_[95]=2;     pkt->data_[96]=0;
-    pkt->data_[97]=23;
+    uint8_t data[98];
+    data[0]  = 1;
+    data[1]  = 1;       data[2]  = 2;     data[3] = 3;      data[4]  = 0;
+    data[5]  = 1;       data[6]  = 0;     data[7] = 14;     data[8]  = 0;
+    data[9]  = 1;       data[10] = 0;     data[11] = 1;     data[12] = 21;
+    data[13] = 158;     data[14] = 60;    data[15] = 22;    data[16] = 0;
+    data[17] = 30;      data[18] = 140;   data[19] = 155;   data[20] = 115;
+    data[21] = 73;      data[22] = 0;     data[23] = 3;     data[24] = 0;
+    data[25] = 40;      data[26] = 0;     data[27] = 0;     data[28] = 0;
+    data[29] = 1;       data[30] = 255;   data[31] = 255;   data[32] = 255;
+    data[33] = 255;     data[34] = 255;   data[35] = 255;   data[36] = 255;
+    data[37] = 255;     data[38] = 0;     data[39] = 5;     data[40] = 0;
+    data[41] = 24;      data[42] = 32;    data[43] = 1;     data[44] = 13;
+    data[45] = 184;     data[46] = 0;     data[47] = 1;     data[48] = 0;
+    data[49] = 0;       data[50] = 0;     data[51] = 0;     data[52] = 0;
+    data[53] = 0;       data[54] = 0;     data[55] = 0;     data[56] = 18;
+    data[57] = 52;      data[58] = 255;   data[59] = 255;   data[60] = 255;
+    data[61] = 255;     data[62] = 255;   data[63] = 255;   data[64] = 255;
+    data[65] = 255;     data[66] = 0;     data[67] = 23;    data[68] = 0;
+    data[69] = 16;      data[70] = 32;    data[71] = 1;     data[72] = 13;
+    data[73] = 184;     data[74] = 0;     data[75] = 1;     data[76] = 0;
+    data[77] = 0;       data[78] = 0;     data[79] = 0;     data[80] = 0;
+    data[81] = 0;       data[82] = 0;     data[83] = 0;     data[84] = 221;
+    data[85] = 221;     data[86] = 0;     data[87] = 8;     data[88] = 0;
+    data[89] = 2;       data[90] = 0;     data[91] = 100;   data[92] = 0;
+    data[93] = 6;       data[94] = 0;     data[95] = 2;     data[96] = 0;
+    data[97] = 23;
+
+    pkt = new Pkt6(data, sizeof(data));
+    pkt->setRemotePort(546);
+    pkt->setRemoteAddr(IOAddress("fe80::21e:8cff:fe9b:7349"));
+    pkt->setLocalPort(0);
+    pkt->setLocalAddr(IOAddress("ff02::1:2"));
+    pkt->setIndex(2);
+    pkt->setIface("eth0");
+
     return (pkt);
 }
 
+
 TEST_F(Pkt6Test, unpack_solicit1) {
-    Pkt6 * sol = capture1();
+    Pkt6* sol = capture1();
 
     ASSERT_EQ(true, sol->unpack());
 
@@ -108,21 +119,16 @@ TEST_F(Pkt6Test, unpack_solicit1) {
     EXPECT_FALSE(sol->getOption(D6O_IA_TA));
     EXPECT_FALSE(sol->getOption(D6O_IAADDR));
 
-    // let's limit verbosity of this test
-    // std::cout << sol->toText();
-
     delete sol;
 }
 
 TEST_F(Pkt6Test, packUnpack) {
 
-    Pkt6 * parent = new Pkt6(100);
+    Pkt6* parent = new Pkt6(DHCPV6_SOLICIT, 0x020304);
 
-    parent->setType(DHCPV6_SOLICIT);
-
-    boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 100));
+    OptionPtr opt1(new Option(Option::V6, 1));
+    OptionPtr opt2(new Option(Option::V6, 2));
+    OptionPtr opt3(new Option(Option::V6, 100));
     // let's not use zero-length option type 3 as it is IA_NA
 
     parent->addOption(opt1);
@@ -130,47 +136,42 @@ TEST_F(Pkt6Test, packUnpack) {
     parent->addOption(opt3);
 
     EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
-    int transid = parent->getTransid();
-    // transaction-id was randomized, let's remember it
 
     // calculated length should be 16
-    EXPECT_EQ( Pkt6::DHCPV6_PKT_HDR_LEN + 3*Option::OPTION6_HDR_LEN, 
-               parent->len() );
-
-    EXPECT_TRUE( parent->pack() );
+    EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
+              parent->len());
 
-    //
-    EXPECT_EQ( Pkt6::DHCPV6_PKT_HDR_LEN + 3*Option::OPTION6_HDR_LEN, 
-               parent->len() );
+    EXPECT_TRUE(parent->pack());
 
-    // let's delete options from options_ collection
-    // they still be defined in packed 
-    parent->options_.clear();
+    EXPECT_EQ(Pkt6::DHCPV6_PKT_HDR_LEN + 3 * Option::OPTION6_HDR_LEN,
+              parent->len());
 
-    // that that removed options are indeed are gone
-    EXPECT_EQ( 4, parent->len() );
+    // create second packet,based on assembled data from the first one
+    Pkt6* clone = new Pkt6(static_cast<const uint8_t*>(parent->getBuffer().getData()),
+                           parent->getBuffer().getLength());
 
     // now recreate options list
-    EXPECT_TRUE( parent->unpack() );
+    EXPECT_TRUE( clone->unpack() );
 
     // transid, message-type should be the same as before
-    EXPECT_EQ(transid, parent->getTransid());
-    EXPECT_EQ(DHCPV6_SOLICIT, parent->getType());
-    
-    EXPECT_TRUE( parent->getOption(1));
-    EXPECT_TRUE( parent->getOption(2));
-    EXPECT_TRUE( parent->getOption(100));
-    EXPECT_FALSE( parent->getOption(4));
-    
+    EXPECT_EQ(parent->getTransid(), parent->getTransid());
+    EXPECT_EQ(DHCPV6_SOLICIT, clone->getType());
+
+    EXPECT_TRUE( clone->getOption(1));
+    EXPECT_TRUE( clone->getOption(2));
+    EXPECT_TRUE( clone->getOption(100));
+    EXPECT_FALSE( clone->getOption(4));
+
     delete parent;
+    delete clone;
 }
 
 TEST_F(Pkt6Test, addGetDelOptions) {
-    Pkt6 * parent = new Pkt6(100);
+    Pkt6* parent = new Pkt6(DHCPV6_SOLICIT, random() );
 
-    boost::shared_ptr<Option> opt1(new Option(Option::V6, 1));
-    boost::shared_ptr<Option> opt2(new Option(Option::V6, 2));
-    boost::shared_ptr<Option> opt3(new Option(Option::V6, 2));
+    OptionPtr opt1(new Option(Option::V6, 1));
+    OptionPtr opt2(new Option(Option::V6, 2));
+    OptionPtr opt3(new Option(Option::V6, 2));
 
     parent->addOption(opt1);
     parent->addOption(opt2);
@@ -180,7 +181,7 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     EXPECT_EQ(opt2, parent->getOption(2));
 
     // expect NULL
-    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(4));
+    EXPECT_EQ(OptionPtr(), parent->getOption(4));
 
     // now there are 2 options of type 2
     parent->addOption(opt3);
@@ -189,13 +190,13 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     EXPECT_EQ(true, parent->delOption(2));
 
     // there still should be the other option 2
-    EXPECT_NE(boost::shared_ptr<Option>(), parent->getOption(2));
+    EXPECT_NE(OptionPtr(), parent->getOption(2));
 
     // let's delete the other option 2
     EXPECT_EQ(true, parent->delOption(2));
 
     // no more options with type=2
-    EXPECT_EQ(boost::shared_ptr<Option>(), parent->getOption(2));
+    EXPECT_EQ(OptionPtr(), parent->getOption(2));
 
     // let's try to delete - should fail
     EXPECT_TRUE(false ==  parent->delOption(2));
@@ -203,5 +204,4 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     delete parent;
 }
 
-
 }
diff --git a/src/lib/dns/.gitignore b/src/lib/dns/.gitignore
new file mode 100644
index 0000000..cdf707c
--- /dev/null
+++ b/src/lib/dns/.gitignore
@@ -0,0 +1,6 @@
+/gen-rdatacode.py
+/rdataclass.cc
+/rdataclass.h
+/rrclass.h
+/rrparamregistry.cc
+/rrtype.h
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 9ed70ba..2cd889d 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -61,6 +61,8 @@ EXTRA_DIST += rdata/generic/soa_6.cc
 EXTRA_DIST += rdata/generic/soa_6.h
 EXTRA_DIST += rdata/generic/spf_99.cc
 EXTRA_DIST += rdata/generic/spf_99.h
+EXTRA_DIST += rdata/generic/sshfp_44.cc
+EXTRA_DIST += rdata/generic/sshfp_44.h
 EXTRA_DIST += rdata/generic/txt_16.cc
 EXTRA_DIST += rdata/generic/txt_16.h
 EXTRA_DIST += rdata/generic/minfo_14.cc
@@ -86,15 +88,17 @@ BUILT_SOURCES += rdataclass.h rdataclass.cc
 
 lib_LTLIBRARIES = libdns++.la
 
-libdns___la_LDFLAGS = -no-undefined -version-info 1:0:1
+libdns___la_LDFLAGS = -no-undefined -version-info 2:0:0
 
 libdns___la_SOURCES =
 libdns___la_SOURCES += edns.h edns.cc
 libdns___la_SOURCES += exceptions.h exceptions.cc
+libdns___la_SOURCES += labelsequence.h labelsequence.cc
 libdns___la_SOURCES += masterload.h masterload.cc
 libdns___la_SOURCES += message.h message.cc
 libdns___la_SOURCES += messagerenderer.h messagerenderer.cc
 libdns___la_SOURCES += name.h name.cc
+libdns___la_SOURCES += name_internal.h
 libdns___la_SOURCES += nsec3hash.h nsec3hash.cc
 libdns___la_SOURCES += opcode.h opcode.cc
 libdns___la_SOURCES += rcode.h rcode.cc
@@ -136,10 +140,11 @@ rrparamregistry.cc: rrparamregistry-placeholder.cc
 rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: Makefile
 	./gen-rdatacode.py
 
-libdns___includedir = $(includedir)/dns
+libdns___includedir = $(includedir)/$(PACKAGE_NAME)/dns
 libdns___include_HEADERS = \
 	edns.h \
 	exceptions.h \
+	labelsequence.h \
 	message.h \
 	messagerenderer.h \
 	name.h \
@@ -152,7 +157,7 @@ libdns___include_HEADERS = \
 	rrttl.h \
 	tsigkey.h
 # Purposely not installing these headers:
-# util/*.h: used only internally, and not actually DNS specific
+# name_internal.h: used only internally, and not actually DNS specific
 # rdata/*/detail/*.h: these are internal use only
 # rrclass-placeholder.h
 # rrtype-placeholder.h
diff --git a/src/lib/dns/benchmarks/.gitignore b/src/lib/dns/benchmarks/.gitignore
new file mode 100644
index 0000000..c1c840b
--- /dev/null
+++ b/src/lib/dns/benchmarks/.gitignore
@@ -0,0 +1,2 @@
+/message_renderer_bench
+/rdatarender_bench
diff --git a/src/lib/dns/benchmarks/Makefile.am b/src/lib/dns/benchmarks/Makefile.am
index 0d7856f..ff591cc 100644
--- a/src/lib/dns/benchmarks/Makefile.am
+++ b/src/lib/dns/benchmarks/Makefile.am
@@ -9,10 +9,16 @@ endif
 
 CLEANFILES = *.gcno *.gcda
 
-noinst_PROGRAMS = rdatarender_bench
+noinst_PROGRAMS = rdatarender_bench message_renderer_bench
+
 rdatarender_bench_SOURCES = rdatarender_bench.cc
 
 rdatarender_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
 rdatarender_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
 rdatarender_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-rdatarender_bench_LDADD += $(SQLITE_LIBS)
+
+message_renderer_bench_SOURCES = message_renderer_bench.cc
+message_renderer_bench_SOURCES += oldmessagerenderer.h oldmessagerenderer.cc
+message_renderer_bench_LDADD = $(top_builddir)/src/lib/dns/libdns++.la
+message_renderer_bench_LDADD += $(top_builddir)/src/lib/util/libutil.la
+message_renderer_bench_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/dns/benchmarks/message_renderer_bench.cc b/src/lib/dns/benchmarks/message_renderer_bench.cc
new file mode 100644
index 0000000..33cd65b
--- /dev/null
+++ b/src/lib/dns/benchmarks/message_renderer_bench.cc
@@ -0,0 +1,176 @@
+// 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 <bench/benchmark.h>
+
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <oldmessagerenderer.h>
+
+#include <cassert>
+#include <vector>
+
+using namespace std;
+using namespace isc::util;
+using namespace isc::bench;
+using namespace isc::dns;
+
+namespace {
+// This templated test performs rendering given set of names using
+// a given (templated) MessageRenderer implementation.  We can check the
+// performance when we modify the renderer implementation by comparing the
+// old and new implementation for the same data.
+template <typename T>
+class MessageRendererBenchMark {
+public:
+    MessageRendererBenchMark(const vector<Name>& names) :
+        names_(names)
+    {}
+    unsigned int run() {
+        renderer_.clear();
+        vector<Name>::const_iterator it = names_.begin();
+        const vector<Name>::const_iterator it_end = names_.end();
+        for (; it != it_end; ++it) {
+            renderer_.writeName(*it);
+        }
+        // Make sure truncation didn't accidentally happen.
+        assert(!renderer_.isTruncated());
+        return (names_.size());
+    }
+private:
+    T renderer_;
+    const vector<Name>& names_;
+};
+
+//
+// Builtin benchmark data.
+//
+// This consists of all names contained in a response from a root server for
+// the query for "www.example.com" (as of this implementing).
+const char* const root_to_com_names[] = {
+    // question section
+    "www.example.com",
+    // authority section
+    "com", "a.gtld-servers.net", "com", "b.gtld-servers.net",
+    "com", "c.gtld-servers.net", "com", "d.gtld-servers.net",
+    "com", "e.gtld-servers.net", "com", "f.gtld-servers.net",
+    "com", "g.gtld-servers.net", "com", "h.gtld-servers.net",
+    "com", "i.gtld-servers.net", "com", "j.gtld-servers.net",
+    "com", "k.gtld-servers.net", "com", "l.gtld-servers.net",
+    "com",                      // owner name of DS
+    "com",                      // owner name of RRSIG(DS)
+    // additional section.  a and b has both AAAA and A; others have A only.
+    "a.gtld-servers.net", "a.gtld-servers.net",
+    "b.gtld-servers.net", "b.gtld-servers.net",
+    "c.gtld-servers.net", "d.gtld-servers.net", "e.gtld-servers.net",
+    "f.gtld-servers.net", "g.gtld-servers.net", "h.gtld-servers.net",
+    "i.gtld-servers.net", "j.gtld-servers.net", "k.gtld-servers.net",
+    "l.gtld-servers.net", "m.gtld-servers.net",
+    NULL
+};
+
+// Names contained a typical "NXDOMAIN" response: the question, the owner
+// name of SOA, and its MNAME and RNAME.
+const char* const example_nxdomain_names[] = {
+    "www.example.com", "example.com", "ns.example.com", "root.example.com",
+    NULL
+};
+
+// Names contained a typical "SERVFAIL" response: only the question.
+const char* const example_servfail_names[] = {
+    "www.example.com", NULL
+};
+
+// An experimental "dumb" renderer for comparison.  It doesn't do any name
+// compression.  It simply ignores all setter method, returns a dummy value
+// for getter methods, and write names to the internal buffer as plain binary
+// data.
+class DumbMessageRenderer : public AbstractMessageRenderer {
+public:
+    virtual void clear() {}
+    virtual size_t getLengthLimit() const { return (512); }
+    virtual void setLengthLimit(const size_t) {}
+    virtual bool isTruncated() const { return (false); }
+    virtual void setTruncated() {}
+    virtual CompressMode getCompressMode() const { return (CASE_INSENSITIVE); }
+    virtual void setCompressMode(const CompressMode) {}
+    virtual void writeName(const Name& name, const bool = false) {
+        name.toWire(getBuffer());
+    }
+};
+
+void
+usage() {
+    cerr << "Usage: message_renderer_bench [-n iterations]" << endl;
+    exit (1);
+}
+}
+
+int
+main(int argc, char* argv[]) {
+    int ch;
+    int iteration = 100000;
+    while ((ch = getopt(argc, argv, "n:")) != -1) {
+        switch (ch) {
+        case 'n':
+            iteration = atoi(optarg);
+            break;
+        case '?':
+        default:
+            usage();
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc != 0) {
+        usage();
+    }
+
+    cout << "Parameters:" << endl;
+    cout << "  Iterations: " << iteration << endl;
+
+    typedef pair<const char* const*, string> DataSpec;
+    vector<DataSpec> spec_list;
+    spec_list.push_back(DataSpec(root_to_com_names, "(positive response)"));
+    spec_list.push_back(DataSpec(example_nxdomain_names,
+                                 "(NXDOMAIN response)"));
+    spec_list.push_back(DataSpec(example_servfail_names,
+                                 "(SERVFAIL response)"));
+    for (vector<DataSpec>::const_iterator it = spec_list.begin();
+         it != spec_list.end();
+         ++it) {
+        vector<Name> names;
+        for (size_t i = 0; it->first[i] != NULL; ++i) {
+            names.push_back(Name(it->first[i]));
+        }
+
+        typedef MessageRendererBenchMark<OldMessageRenderer>
+            OldRendererBenchMark;
+        cout << "Benchmark for old MessageRenderer " << it->second << endl;
+        BenchMark<OldRendererBenchMark>(iteration,
+                                        OldRendererBenchMark(names));
+
+        typedef MessageRendererBenchMark<DumbMessageRenderer>
+            DumbRendererBenchMark;
+        cout << "Benchmark for dumb MessageRenderer " << it->second << endl;
+        BenchMark<DumbRendererBenchMark>(iteration,
+                                         DumbRendererBenchMark(names));
+
+        typedef MessageRendererBenchMark<MessageRenderer> RendererBenchMark;
+        cout << "Benchmark for new MessageRenderer " << it->second << endl;
+        BenchMark<RendererBenchMark>(iteration, RendererBenchMark(names));
+    }
+
+    return (0);
+}
diff --git a/src/lib/dns/benchmarks/oldmessagerenderer.cc b/src/lib/dns/benchmarks/oldmessagerenderer.cc
new file mode 100644
index 0000000..cd5c4e2
--- /dev/null
+++ b/src/lib/dns/benchmarks/oldmessagerenderer.cc
@@ -0,0 +1,278 @@
+// 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.
+
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+#include <dns/name.h>
+#include <oldmessagerenderer.h>
+
+#include <cctype>
+#include <cassert>
+#include <set>
+
+using namespace isc::util;
+
+namespace isc {
+namespace dns {
+
+namespace {     // hide internal-only names from the public namespaces
+///
+/// \brief The \c NameCompressNode class represents a pointer to a name
+/// rendered in the internal buffer for the \c MessageRendererImpl object.
+///
+/// A \c MessageRendererImpl object maintains a set of the \c NameCompressNode
+/// objects, and searches the set for the position of the longest match
+/// (ancestor) name against each new name to be rendered into the buffer.
+struct NameCompressNode {
+    NameCompressNode(const OldMessageRenderer& renderer,
+                     const OutputBuffer& buffer, const size_t pos,
+                     const size_t len) :
+        renderer_(renderer), buffer_(buffer), pos_(pos), len_(len) {}
+    /// The renderer that performs name compression using the node.
+    /// This is kept in each node to detect the compression mode
+    /// (case-sensitive or not) in the comparison functor (\c NameCompare).
+    const OldMessageRenderer& renderer_;
+    /// The buffer in which the corresponding name is rendered.
+    const OutputBuffer& buffer_;
+    /// The position (offset from the beginning) in the buffer where the
+    /// name starts.
+    uint16_t pos_;
+    /// The length of the corresponding name.
+    uint16_t len_;
+};
+
+///
+/// \brief The \c NameCompare class is a functor that gives ordering among
+/// \c NameCompressNode objects stored in \c MessageRendererImpl::nodeset_.
+///
+/// Its only public method as a functor, \c operator(), gives the ordering
+/// between two \c NameCompressNode objects in terms of equivalence, that is,
+/// returns whether one is "less than" the other.
+/// For our purpose we only need to distinguish two different names, so the
+/// ordering is different from the canonical DNS name order used in DNSSEC;
+/// basically, it gives the case-insensitive ordering of the two names as their
+/// textual representation.
+struct NameCompare : public std::binary_function<NameCompressNode,
+                                                 NameCompressNode,
+                                                 bool> {
+    ///
+    /// Returns true if n1 < n2 as a result of case-insensitive comparison;
+    /// otherwise return false.
+    ///
+    /// The name corresponding to \c n1 or \c n2 may be compressed, in which
+    /// case we must follow the compression pointer in the associated buffer.
+    /// The helper private method \c nextPosition() gives the position in the
+    /// buffer for the next character, taking into account compression.
+    ///
+    bool operator()(const NameCompressNode& n1,
+                    const NameCompressNode& n2) const
+    {
+        if (n1.len_ < n2.len_) {
+            return (true);
+        } else if (n1.len_ > n2.len_) {
+            return (false);
+        }
+
+        const bool case_sensitive =
+            (n1.renderer_.getCompressMode() == OldMessageRenderer::CASE_SENSITIVE);
+
+        uint16_t pos1 = n1.pos_;
+        uint16_t pos2 = n2.pos_;
+        uint16_t l1 = 0;
+        uint16_t l2 = 0;
+        for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) {
+            pos1 = nextPosition(n1.buffer_, pos1, l1);
+            pos2 = nextPosition(n2.buffer_, pos2, l2);
+            if (case_sensitive) {
+                if (n1.buffer_[pos1] < n2.buffer_[pos2]) {
+                    return (true);
+                } else if (n1.buffer_[pos1] > n2.buffer_[pos2]) {
+                    return (false);
+                }
+            } else {
+                if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) {
+                    return (true);
+                } else if (tolower(n1.buffer_[pos1]) >
+                           tolower(n2.buffer_[pos2])) {
+                    return (false);
+                }
+            }
+        }
+
+        return (false);
+    }
+
+private:
+    uint16_t nextPosition(const OutputBuffer& buffer,
+                          uint16_t pos, uint16_t& llen) const
+    {
+        if (llen == 0) {
+            size_t i = 0;
+
+            while ((buffer[pos] & Name::COMPRESS_POINTER_MARK8) ==
+                   Name::COMPRESS_POINTER_MARK8) {
+                pos = (buffer[pos] & ~Name::COMPRESS_POINTER_MARK8) *
+                    256 + buffer[pos + 1];
+
+                // This loop should stop as long as the buffer has been
+                // constructed validly and the search/insert argument is based
+                // on a valid name, which is an assumption for this class.
+                // But we'll abort if a bug could cause an infinite loop.
+                i += 2;
+                assert(i < Name::MAX_WIRE);
+            }
+            llen = buffer[pos];
+        } else {
+            --llen;
+        }
+        return (pos);
+    }
+};
+}
+
+///
+/// \brief The \c MessageRendererImpl class is the actual implementation of
+/// \c MessageRenderer.
+///
+/// The implementation is hidden from applications.  We can refer to specific
+/// members of this class only within the implementation source file.
+///
+struct OldMessageRenderer::MessageRendererImpl {
+    /// \brief Constructor from an output buffer.
+    ///
+    MessageRendererImpl() :
+        nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
+        truncated_(false), compress_mode_(OldMessageRenderer::CASE_INSENSITIVE)
+    {}
+    /// A local working buffer to convert each given name into wire format.
+    /// This could be a local variable of the \c writeName() method, but
+    /// we keep it in the class so that we can reuse it and avoid construction
+    /// overhead.
+    OutputBuffer nbuffer_;
+    /// A set of compression pointers.
+    std::set<NameCompressNode, NameCompare> nodeset_;
+    /// The maximum length of rendered data that can fit without
+    /// truncation.
+    uint16_t msglength_limit_;
+    /// A boolean flag that indicates truncation has occurred while rendering
+    /// the data.
+    bool truncated_;
+    /// The name compression mode.
+    CompressMode compress_mode_;
+};
+
+OldMessageRenderer::OldMessageRenderer() :
+    AbstractMessageRenderer(),
+    impl_(new MessageRendererImpl)
+{}
+
+OldMessageRenderer::~OldMessageRenderer() {
+    delete impl_;
+}
+
+void
+OldMessageRenderer::clear() {
+    AbstractMessageRenderer::clear();
+    impl_->nbuffer_.clear();
+    impl_->nodeset_.clear();
+    impl_->msglength_limit_ = 512;
+    impl_->truncated_ = false;
+    impl_->compress_mode_ = CASE_INSENSITIVE;
+}
+
+size_t
+OldMessageRenderer::getLengthLimit() const {
+    return (impl_->msglength_limit_);
+}
+
+void
+OldMessageRenderer::setLengthLimit(const size_t len) {
+    impl_->msglength_limit_ = len;
+}
+
+bool
+OldMessageRenderer::isTruncated() const {
+    return (impl_->truncated_);
+}
+
+void
+OldMessageRenderer::setTruncated() {
+    impl_->truncated_ = true;
+}
+
+OldMessageRenderer::CompressMode
+OldMessageRenderer::getCompressMode() const {
+    return (impl_->compress_mode_);
+}
+
+void
+OldMessageRenderer::setCompressMode(const CompressMode mode) {
+    impl_->compress_mode_ = mode;
+}
+
+void
+OldMessageRenderer::writeName(const Name& name, const bool compress) {
+    impl_->nbuffer_.clear();
+    name.toWire(impl_->nbuffer_);
+
+    unsigned int i;
+    std::set<NameCompressNode, NameCompare>::const_iterator notfound =
+        impl_->nodeset_.end();
+    std::set<NameCompressNode, NameCompare>::const_iterator n = notfound;
+
+    // Find the longest ancestor name in the rendered set that matches the
+    // given name.
+    for (i = 0; i < impl_->nbuffer_.getLength(); i += impl_->nbuffer_[i] + 1) {
+        // skip the trailing null label
+        if (impl_->nbuffer_[i] == 0) {
+            continue;
+        }
+        n = impl_->nodeset_.find(NameCompressNode(*this, impl_->nbuffer_, i,
+                                                  impl_->nbuffer_.getLength() -
+                                                  i));
+        if (n != notfound) {
+            break;
+        }
+    }
+
+    // Record the current offset before extending the buffer.
+    const size_t offset = getLength();
+    // Write uncompress part...
+    writeData(impl_->nbuffer_.getData(),
+              compress ? i : impl_->nbuffer_.getLength());
+    if (compress && n != notfound) {
+        // ...and compression pointer if available.
+        uint16_t pointer = (*n).pos_;
+        pointer |= Name::COMPRESS_POINTER_MARK16;
+        writeUint16(pointer);
+    }
+
+    // Finally, add to the set the newly rendered name and its ancestors that
+    // have not been in the set.
+    for (unsigned int j = 0; j < i; j += impl_->nbuffer_[j] + 1) {
+        if (impl_->nbuffer_[j] == 0) {
+            continue;
+        }
+        if (offset + j > Name::MAX_COMPRESS_POINTER) {
+            break;
+        }
+        impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
+                                                offset + j,
+                                                impl_->nbuffer_.getLength() -
+                                                j));
+    }
+}
+
+}
+}
diff --git a/src/lib/dns/benchmarks/oldmessagerenderer.h b/src/lib/dns/benchmarks/oldmessagerenderer.h
new file mode 100644
index 0000000..14e7aee
--- /dev/null
+++ b/src/lib/dns/benchmarks/oldmessagerenderer.h
@@ -0,0 +1,55 @@
+// 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.
+
+#ifndef __OLDMESSAGERENDERER_H
+#define __OLDMESSAGERENDERER_H 1
+
+//
+// This is a copy of an older version of MessageRenderer class.  It is kept
+// here to provide a benchmark target.
+//
+
+#include <dns/messagerenderer.h>
+
+namespace isc {
+namespace dns {
+
+class OldMessageRenderer : public AbstractMessageRenderer {
+public:
+    using AbstractMessageRenderer::CASE_INSENSITIVE;
+    using AbstractMessageRenderer::CASE_SENSITIVE;
+
+    /// \brief Constructor from an output buffer.
+    OldMessageRenderer();
+
+    virtual ~OldMessageRenderer();
+    virtual bool isTruncated() const;
+    virtual size_t getLengthLimit() const;
+    virtual CompressMode getCompressMode() const;
+    virtual void setTruncated();
+    virtual void setLengthLimit(size_t len);
+    virtual void setCompressMode(CompressMode mode);
+    virtual void clear();
+    virtual void writeName(const Name& name, bool compress = true);
+private:
+    struct MessageRendererImpl;
+    MessageRendererImpl* impl_;
+};
+}
+}
+#endif // __OLDMESSAGERENDERER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/benchmarks/rdatarender_bench.cc b/src/lib/dns/benchmarks/rdatarender_bench.cc
index d1fb0f2..368ea6a 100644
--- a/src/lib/dns/benchmarks/rdatarender_bench.cc
+++ b/src/lib/dns/benchmarks/rdatarender_bench.cc
@@ -42,7 +42,7 @@ template <typename T>
 class RdataRenderBenchMark {
 public:
     RdataRenderBenchMark(const vector<T>& dataset) :
-        dataset_(dataset), buffer_(4096), renderer_(buffer_)
+        dataset_(dataset)
     {}
     unsigned int run() {
         typename vector<T>::const_iterator data;
@@ -55,7 +55,6 @@ public:
     }
 private:
     const vector<T>& dataset_;
-    OutputBuffer buffer_;
     MessageRenderer renderer_;
 };
 
diff --git a/src/lib/dns/labelsequence.cc b/src/lib/dns/labelsequence.cc
new file mode 100644
index 0000000..0ec450f
--- /dev/null
+++ b/src/lib/dns/labelsequence.cc
@@ -0,0 +1,114 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/labelsequence.h>
+#include <dns/name_internal.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/functional/hash.hpp>
+
+namespace isc {
+namespace dns {
+
+const char*
+LabelSequence::getData(size_t *len) const {
+    *len = getDataLength();
+    return (&name_.ndata_[name_.offsets_[first_label_]]);
+}
+
+size_t
+LabelSequence::getDataLength() const {
+    // If the labelsequence is absolute, the current last_label_ falls
+    // out of the vector (since it points to the 'label' after the
+    // root label, which doesn't exist; in that case, return
+    // the length for the 'previous' label (the root label) plus
+    // one (for the root label zero octet)
+    if (isAbsolute()) {
+        return (name_.offsets_[last_label_ - 1] -
+                name_.offsets_[first_label_] + 1);
+    } else {
+        return (name_.offsets_[last_label_] - name_.offsets_[first_label_]);
+    }
+}
+
+bool
+LabelSequence::equals(const LabelSequence& other, bool case_sensitive) const {
+    size_t len, other_len;
+    const char* data = getData(&len);
+    const char* other_data = other.getData(&other_len);
+
+    if (len != other_len) {
+        return (false);
+    }
+    if (case_sensitive) {
+        return (strncmp(data, other_data, len) == 0);
+    }
+
+    // As long as the data was originally validated as (part of) a name,
+    // label length must never be a capital ascii character, so we can
+    // simply compare them after converting to lower characters.
+    for (size_t i = 0; i < len; ++i) {
+        const unsigned char ch = data[i];
+        const unsigned char other_ch = other_data[i];
+        if (isc::dns::name::internal::maptolower[ch] !=
+            isc::dns::name::internal::maptolower[other_ch]) {
+            return (false);
+        }
+    }
+    return (true);
+}
+
+void
+LabelSequence::stripLeft(size_t i) {
+    if (i >= getLabelCount()) {
+        isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+                              " (labelcount: " << getLabelCount() << ")");
+    }
+    first_label_ += i;
+}
+
+void
+LabelSequence::stripRight(size_t i) {
+    if (i >= getLabelCount()) {
+        isc_throw(OutOfRange, "Cannot strip to zero or less labels; " << i <<
+                              " (labelcount: " << getLabelCount() << ")");
+    }
+    last_label_ -= i;
+}
+
+bool
+LabelSequence::isAbsolute() const {
+    return (last_label_ == name_.offsets_.size());
+}
+
+size_t
+LabelSequence::getHash(bool case_sensitive) const {
+    size_t length;
+    const char* s = getData(&length);
+    if (length > 16) {
+        length = 16;
+    }
+
+    size_t hash_val = 0;
+    while (length > 0) {
+        const unsigned char c = *s++;
+        boost::hash_combine(hash_val, case_sensitive ? c :
+                            isc::dns::name::internal::maptolower[c]);
+        --length;
+    }
+    return (hash_val);
+}
+
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/labelsequence.h b/src/lib/dns/labelsequence.h
new file mode 100644
index 0000000..6b10b67
--- /dev/null
+++ b/src/lib/dns/labelsequence.h
@@ -0,0 +1,177 @@
+// 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 __LABELSEQUENCE_H
+#define __LABELSEQUENCE_H 1
+
+#include <dns/name.h>
+#include <util/buffer.h>
+
+namespace isc {
+namespace dns {
+
+/// \brief Light-weight Accessor to Name object
+///
+/// The purpose of this class is to easily match Names and parts of Names,
+/// without needing to copy the underlying data on each label strip.
+///
+/// It can only work on existing Name objects, and the Name object MUST
+/// remain in scope during the entire lifetime of its associated
+/// LabelSequence(s).
+///
+/// Upon creation of a LabelSequence, it records the offsets of the
+/// labels in the wireformat data of the Name. When stripLeft() or
+/// stripRight() is called on the LabelSequence, no changes in the
+/// Name's data occur, but the internal pointers of the
+/// LabelSequence are modified.
+///
+/// LabelSequences can be compared to other LabelSequences, and their
+/// data can be requested (which then points to part of the original
+/// data of the associated Name object).
+///
+class LabelSequence {
+public:
+    /// \brief Constructs a LabelSequence for the given name
+    ///
+    /// \note The associated Name MUST remain in scope during the lifetime
+    /// of this LabelSequence, since getData() refers to data from the
+    /// Name object (the only data the LabelSequence stores are pointers
+    /// to the labels in the Name object).
+    ///
+    /// \param name The Name to construct a LabelSequence for
+    LabelSequence(const Name& name): name_(name),
+                                     first_label_(0),
+                                     last_label_(name.getLabelCount())
+    {}
+
+    /// \brief Return the wire-format data for this LabelSequence
+    ///
+    /// The data, is returned as a pointer to the original wireformat
+    /// data of the original Name object, and the given len value is
+    /// set to the number of octets that match this labelsequence.
+    ///
+    /// \note The data pointed to is only valid if the original Name
+    /// object is still in scope
+    ///
+    /// \param len Pointer to a size_t where the length of the data
+    ///        will be stored (in number of octets)
+    /// \return Pointer to the wire-format data of this label sequence
+    const char* getData(size_t* len) const;
+
+    /// \brief Return the length of the wire-format data of this LabelSequence
+    ///
+    /// This method returns the number of octets for the data that would
+    /// be returned by the \c getData() method.
+    ///
+    /// Note that the return value of this method is always positive.
+    /// Note also that if the return value of this method is 1, it means the
+    /// sequence consists of the null label, i.e., a single "dot", and vice
+    /// versa.
+    ///
+    /// \note The data pointed to is only valid if the original Name
+    /// object is still in scope
+    ///
+    /// \return The length of the data of the label sequence in octets.
+    size_t getDataLength() const;
+
+    /// \brief Compares two label sequences.
+    ///
+    /// Performs a (optionally case-insensitive) comparison between this
+    /// LabelSequence and another LabelSequence.
+    ///
+    /// \param other The LabelSequence to compare with
+    /// \param case_sensitive If true, comparison is case-insensitive
+    /// \return true if The label sequences consist are the same length,
+    ///         and contain the same data.
+    bool equals(const LabelSequence& other, bool case_sensitive = false) const;
+
+    /// \brief Remove labels from the front of this LabelSequence
+    ///
+    /// \note No actual memory is changed, this operation merely updates the
+    /// internal pointers based on the offsets in the Name object.
+    ///
+    /// \exeption OutOfRange if i is greater than or equal to the number
+    ///           of labels currently pointed to by this LabelSequence
+    ///
+    /// \param i The number of labels to remove.
+    void stripLeft(size_t i);
+
+    /// \brief Remove labels from the end of this LabelSequence
+    ///
+    /// \note No actual memory is changed, this operation merely updates the
+    /// internal pointers based on the offsets in the Name object.
+    ///
+    /// \exeption OutOfRange if i is greater than or equal to the number
+    ///           of labels currently pointed to by this LabelSequence
+    ///
+    /// \param i The number of labels to remove.
+    void stripRight(size_t i);
+
+    /// \brief Returns the current number of labels for this LabelSequence
+    ///
+    /// \return The number of labels
+    size_t getLabelCount() const { return (last_label_ - first_label_); }
+
+    /// \brief Returns the original Name object associated with this
+    ///        LabelSequence
+    ///
+    /// While the Name should still be in scope during the lifetime of
+    /// the LabelSequence, it can still be useful to have access to it,
+    /// for instance in helper functions that are only passed the
+    /// LabelSequence itself.
+    ///
+    /// \return Reference to the original Name object
+    const Name& getName() const { return (name_); }
+
+    /// \brief Calculate a simple hash for the label sequence.
+    ///
+    /// This method calculates a hash value for the label sequence as binary
+    /// data.  If \c case_sensitive is false, it ignores the case stored in
+    /// the labels; specifically, it normalizes the labels by converting all
+    /// upper case characters to lower case ones and calculates the hash value
+    /// for the result.
+    ///
+    /// This method is intended to provide a lightweight way to store a
+    /// relatively small number of label sequences in a hash table.
+    /// For this reason it only takes into account data up to 16 octets
+    /// (16 was derived from BIND 9's implementation).  Also, the function does
+    /// not provide any unpredictability; a specific sequence will always have
+    /// the same hash value.  It should therefore not be used in the context
+    /// where an untrusted third party can mount a denial of service attack by
+    /// forcing the application to create a very large number of label
+    /// sequences that have the same hash value and expected to be stored in
+    /// a hash table.
+    ///
+    /// \exception None
+    ///
+    /// \param case_sensitive
+    /// \return A hash value for this label sequence.
+    size_t getHash(bool case_sensitive) const;
+
+    /// \brief Checks whether the label sequence is absolute
+    ///
+    /// \return true if the last label is the root label
+    bool isAbsolute() const;
+
+private:
+    const Name& name_;
+    size_t first_label_;
+    size_t last_label_;
+};
+
+
+} // end namespace dns
+} // end namespace isc
+
+#endif
diff --git a/src/lib/dns/masterload.cc b/src/lib/dns/masterload.cc
index 000487c..0b195f6 100644
--- a/src/lib/dns/masterload.cc
+++ b/src/lib/dns/masterload.cc
@@ -37,6 +37,29 @@ using namespace isc::dns::rdata;
 
 namespace isc {
 namespace dns {
+namespace {
+// A helper function that strips off any comment or whitespace at the end of
+// an RR.
+// This is an incomplete implementation, and cannot handle all such comments;
+// it's considered a short term workaround to deal with some real world
+// cases.
+string
+stripLine(string& s, const Exception& ex) {
+    // Find any ';' in the text data, and locate the position of the last
+    // occurrence.  Note that unless/until we support empty RDATA it
+    // shouldn't be placed at the beginning of the data.
+    const size_t pos_semicolon = s.rfind(';');
+    if (pos_semicolon == 0) {
+        throw ex;
+    } else if (pos_semicolon != string::npos) {
+        s.resize(pos_semicolon);
+    }
+    // Remove any trailing whitespace return the resulting text.
+    s.resize(s.find_last_not_of(" \t") + 1);
+    return (s);
+}
+}
+
 void
 masterLoad(const char* const filename, const Name& origin,
            const RRClass& zone_class, MasterLoadCallback callback)
@@ -116,7 +139,15 @@ masterLoad(istream& input, const Name& origin, const RRClass& zone_class,
             ttl.reset(new RRTTL(ttl_txt));
             rrclass.reset(new RRClass(rrclass_txt));
             rrtype.reset(new RRType(rrtype_txt));
-            rdata = createRdata(*rrtype, *rrclass, rdatabuf.str());
+            string rdtext = rdatabuf.str();
+            try {
+                rdata = createRdata(*rrtype, *rrclass, rdtext);
+            } catch (const Exception& ex) {
+                // If the parse for the RDATA fails, check if it has comments
+                // or whitespace at the end, and if so, retry the conversion
+                // after stripping off the comment or whitespace
+                rdata = createRdata(*rrtype, *rrclass, stripLine(rdtext, ex));
+            }
         } catch (const Exception& ex) {
             isc_throw(MasterLoadError, "Invalid RR text at line " << line_count
                       << ": " << ex.what());
diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc
index 1378ba9..d5c7c69 100644
--- a/src/lib/dns/messagerenderer.cc
+++ b/src/lib/dns/messagerenderer.cc
@@ -12,104 +12,108 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <cctype>
-#include <cassert>
-#include <set>
-
+#include <exceptions/exceptions.h>
 #include <util/buffer.h>
 #include <dns/name.h>
+#include <dns/name_internal.h>
+#include <dns/labelsequence.h>
 #include <dns/messagerenderer.h>
 
+#include <boost/array.hpp>
+#include <boost/static_assert.hpp>
+
+#include <limits>
+#include <cassert>
+#include <vector>
+
+using namespace std;
 using namespace isc::util;
+using isc::dns::name::internal::maptolower;
 
 namespace isc {
 namespace dns {
 
 namespace {     // hide internal-only names from the public namespaces
 ///
-/// \brief The \c NameCompressNode class represents a pointer to a name
+/// \brief The \c OffsetItem class represents a pointer to a name
 /// rendered in the internal buffer for the \c MessageRendererImpl object.
 ///
-/// A \c MessageRendererImpl object maintains a set of the \c NameCompressNode
-/// objects, and searches the set for the position of the longest match
-/// (ancestor) name against each new name to be rendered into the buffer.
-struct NameCompressNode {
-    NameCompressNode(const MessageRenderer& renderer,
-                     const OutputBuffer& buffer, const size_t pos,
-                     const size_t len) :
-        renderer_(renderer), buffer_(buffer), pos_(pos), len_(len) {}
-    /// The renderer that performs name compression using the node.
-    /// This is kept in each node to detect the compression mode
-    /// (case-sensitive or not) in the comparison functor (\c NameCompare).
-    const MessageRenderer& renderer_;
-    /// The buffer in which the corresponding name is rendered.
-    const OutputBuffer& buffer_;
+/// A \c MessageRendererImpl object maintains a set of \c OffsetItem
+/// objects in a hash table, and searches the table for the position of the
+/// longest match (ancestor) name against each new name to be rendered into
+/// the buffer.
+struct OffsetItem {
+    OffsetItem(size_t hash, size_t pos, size_t len) :
+        hash_(hash), pos_(pos), len_(len)
+    {}
+
+    /// The hash value for the stored name calculated by LabelSequence.getHash.
+    /// This will help make name comparison in \c NameCompare more efficient.
+    size_t hash_;
+
     /// The position (offset from the beginning) in the buffer where the
     /// name starts.
     uint16_t pos_;
-    /// The length of the corresponding name.
+
+    /// The length of the corresponding sequence (which is a domain name).
     uint16_t len_;
 };
 
+/// \brief The \c NameCompare class is a functor that checks equality
+/// between the name corresponding to an \c OffsetItem object and the name
+/// consists of labels represented by a \c LabelSequence object.
 ///
-/// \brief The \c NameCompare class is a functor that gives ordering among
-/// \c NameCompressNode objects stored in \c MessageRendererImpl::nodeset_.
-///
-/// Its only public method as a functor, \c operator(), gives the ordering
-/// between two \c NameCompressNode objects in terms of equivalence, that is,
-/// returns whether one is "less than" the other.
-/// For our purpose we only need to distinguish two different names, so the
-/// ordering is different from the canonical DNS name order used in DNSSEC;
-/// basically, it gives the case-insensitive ordering of the two names as their
-/// textual representation.
-struct NameCompare : public std::binary_function<NameCompressNode,
-                                                 NameCompressNode,
-                                                 bool> {
+/// Template parameter CASE_SENSITIVE determines whether to ignore the case
+/// of the names.  This policy doesn't change throughout the lifetime of
+/// this object, so we separate these using template to avoid unnecessary
+/// condition check.
+template <bool CASE_SENSITIVE>
+struct NameCompare {
+    /// \brief Constructor
     ///
-    /// Returns true if n1 < n2 as a result of case-insensitive comparison;
-    /// otherwise return false.
-    ///
-    /// The name corresponding to \c n1 or \c n2 may be compressed, in which
-    /// case we must follow the compression pointer in the associated buffer.
-    /// The helper private method \c nextPosition() gives the position in the
-    /// buffer for the next character, taking into account compression.
-    ///
-    bool operator()(const NameCompressNode& n1,
-                    const NameCompressNode& n2) const
-    {
-        if (n1.len_ < n2.len_) {
-            return (true);
-        } else if (n1.len_ > n2.len_) {
+    /// \param buffer The buffer for rendering used in the caller renderer
+    /// \param name_buf An input buffer storing the wire-format data of the
+    /// name to be newly rendered (and only that data).
+    /// \param hash The hash value for the name.
+    NameCompare(const OutputBuffer& buffer, InputBuffer& name_buf,
+                size_t hash) :
+        buffer_(&buffer), name_buf_(&name_buf), hash_(hash)
+    {}
+
+    bool operator()(const OffsetItem& item) const {
+        // Trivial inequality check.  If either the hash or the total length
+        // doesn't match, the names are obviously different.
+        if (item.hash_  != hash_ || item.len_ != name_buf_->getLength()) {
             return (false);
         }
 
-        const bool case_sensitive =
-            (n1.renderer_.getCompressMode() == MessageRenderer::CASE_SENSITIVE);
-
-        uint16_t pos1 = n1.pos_;
-        uint16_t pos2 = n2.pos_;
-        uint16_t l1 = 0;
-        uint16_t l2 = 0;
-        for (uint16_t i = 0; i < n1.len_; i++, pos1++, pos2++) {
-            pos1 = nextPosition(n1.buffer_, pos1, l1);
-            pos2 = nextPosition(n2.buffer_, pos2, l2);
-            if (case_sensitive) {
-                if (n1.buffer_[pos1] < n2.buffer_[pos2]) {
-                    return (true);
-                } else if (n1.buffer_[pos1] > n2.buffer_[pos2]) {
+        // Compare the name data, character-by-character.
+        // item_pos keeps track of the position in the buffer corresponding to
+        // the character to compare.  item_label_len is the number of
+        // characters in the labels where the character pointed by item_pos
+        // belongs.  When it reaches zero, nextPosition() identifies the
+        // position for the subsequent label, taking into account name
+        // compression, and resets item_label_len to the length of the new
+        // label.
+        name_buf_->setPosition(0); // buffer can be reused, so reset position
+        uint16_t item_pos = item.pos_;
+        uint16_t item_label_len = 0;
+        for (size_t i = 0; i < item.len_; ++i, ++item_pos) {
+            item_pos = nextPosition(*buffer_, item_pos, item_label_len);
+            const unsigned char ch1 = (*buffer_)[item_pos];
+            const unsigned char ch2 = name_buf_->readUint8();
+            if (CASE_SENSITIVE) {
+                if (ch1 != ch2) {
                     return (false);
                 }
             } else {
-                if (tolower(n1.buffer_[pos1]) < tolower(n2.buffer_[pos2])) {
-                    return (true);
-                } else if (tolower(n1.buffer_[pos1]) >
-                           tolower(n2.buffer_[pos2])) {
+                if (maptolower[ch1] != maptolower[ch2]) {
                     return (false);
                 }
             }
         }
 
-        return (false);
+        return (true);
     }
 
 private:
@@ -137,6 +141,10 @@ private:
         }
         return (pos);
     }
+
+    const OutputBuffer* buffer_;
+    InputBuffer* name_buf_;
+    const size_t hash_;
 };
 }
 
@@ -147,20 +155,60 @@ private:
 /// The implementation is hidden from applications.  We can refer to specific
 /// members of this class only within the implementation source file.
 ///
+/// It internally holds a hash table for OffsetItem objects corresponding
+/// to portions of names rendered in this renderer.  The offset information
+/// is used to compress subsequent names to be rendered.
 struct MessageRenderer::MessageRendererImpl {
-    /// \brief Constructor from an output buffer.
-    ///
+    // The size of hash buckets and number of hash entries per bucket for
+    // which space is preallocated and kept reserved for subsequent rendering
+    // to provide better performance.  These values are derived from the
+    // BIND 9 implementation that uses a similar hash table.
+    static const size_t BUCKETS = 64;
+    static const size_t RESERVED_ITEMS = 16;
+    static const uint16_t NO_OFFSET = 65535; // used as a marker of 'not found'
+
+    /// \brief Constructor
     MessageRendererImpl() :
-        nbuffer_(Name::MAX_WIRE), msglength_limit_(512),
-        truncated_(false), compress_mode_(MessageRenderer::CASE_INSENSITIVE)
-    {}
-    /// A local working buffer to convert each given name into wire format.
-    /// This could be a local variable of the \c writeName() method, but
-    /// we keep it in the class so that we can reuse it and avoid construction
-    /// overhead.
-    OutputBuffer nbuffer_;
-    /// A set of compression pointers.
-    std::set<NameCompressNode, NameCompare> nodeset_;
+        msglength_limit_(512), truncated_(false),
+        compress_mode_(MessageRenderer::CASE_INSENSITIVE)
+    {
+        // Reserve some spaces for hash table items.
+        for (size_t i = 0; i < BUCKETS; ++i) {
+            table_[i].reserve(RESERVED_ITEMS);
+        }
+    }
+
+    uint16_t findOffset(const OutputBuffer& buffer, InputBuffer& name_buf,
+                        size_t hash, bool case_sensitive) const
+    {
+        // Find a matching entry, if any.  We use some heuristics here: often
+        // the same name appers consecutively (like repeating the same owner
+        // name for a single RRset), so in case there's a collision in the
+        // bucket it will be more likely to find it in the tail side of the
+        // bucket.
+        const size_t bucket_id = hash % BUCKETS;
+        vector<OffsetItem>::const_reverse_iterator found;
+        if (case_sensitive) {
+            found = find_if(table_[bucket_id].rbegin(),
+                            table_[bucket_id].rend(),
+                            NameCompare<true>(buffer, name_buf, hash));
+        } else {
+            found = find_if(table_[bucket_id].rbegin(),
+                            table_[bucket_id].rend(),
+                            NameCompare<false>(buffer, name_buf, hash));
+        }
+        if (found != table_[bucket_id].rend()) {
+            return (found->pos_);
+        }
+        return (NO_OFFSET);
+    }
+
+    void addOffset(size_t hash, size_t offset, size_t len) {
+        table_[hash % BUCKETS].push_back(OffsetItem(hash, offset, len));
+    }
+
+    // The hash table for the (offset + position in the buffer) entries
+    vector<OffsetItem> table_[BUCKETS];
     /// The maximum length of rendered data that can fit without
     /// truncation.
     uint16_t msglength_limit_;
@@ -169,10 +217,15 @@ struct MessageRenderer::MessageRendererImpl {
     bool truncated_;
     /// The name compression mode.
     CompressMode compress_mode_;
+
+    // Placeholder for hash values as they are calculated in writeName().
+    // Note: we may want to make it a local variable of writeName() if it
+    // works more efficiently.
+    boost::array<size_t, Name::MAX_LABELS> seq_hashes_;
 };
 
-MessageRenderer::MessageRenderer(OutputBuffer& buffer) :
-    AbstractMessageRenderer(buffer),
+MessageRenderer::MessageRenderer() :
+    AbstractMessageRenderer(),
     impl_(new MessageRendererImpl)
 {}
 
@@ -183,11 +236,22 @@ MessageRenderer::~MessageRenderer() {
 void
 MessageRenderer::clear() {
     AbstractMessageRenderer::clear();
-    impl_->nbuffer_.clear();
-    impl_->nodeset_.clear();
     impl_->msglength_limit_ = 512;
     impl_->truncated_ = false;
     impl_->compress_mode_ = CASE_INSENSITIVE;
+
+    // Clear the hash table.  We reserve the minimum space for possible
+    // subsequent use of the renderer.
+    for (size_t i = 0; i < MessageRendererImpl::BUCKETS; ++i) {
+        if (impl_->table_[i].size() > MessageRendererImpl::RESERVED_ITEMS) {
+            // Trim excessive capacity: swap ensures the new capacity is only
+            // reasonably large for the reserved space.
+            vector<OffsetItem> new_table;
+            new_table.reserve(MessageRendererImpl::RESERVED_ITEMS);
+            new_table.swap(impl_->table_[i]);
+        }
+        impl_->table_[i].clear();
+    }
 }
 
 size_t
@@ -217,65 +281,112 @@ MessageRenderer::getCompressMode() const {
 
 void
 MessageRenderer::setCompressMode(const CompressMode mode) {
+    if (getLength() != 0) {
+        isc_throw(isc::InvalidParameter,
+                  "compress mode cannot be changed during rendering");
+    }
     impl_->compress_mode_ = mode;
 }
 
 void
 MessageRenderer::writeName(const Name& name, const bool compress) {
-    impl_->nbuffer_.clear();
-    name.toWire(impl_->nbuffer_);
-
-    unsigned int i;
-    std::set<NameCompressNode, NameCompare>::const_iterator notfound =
-        impl_->nodeset_.end();
-    std::set<NameCompressNode, NameCompare>::const_iterator n = notfound;
-
-    // Find the longest ancestor name in the rendered set that matches the
-    // given name.
-    for (i = 0; i < impl_->nbuffer_.getLength(); i += impl_->nbuffer_[i] + 1) {
-        // skip the trailing null label
-        if (impl_->nbuffer_[i] == 0) {
-            continue;
+    LabelSequence sequence(name);
+    const size_t nlabels = sequence.getLabelCount();
+    size_t data_len;
+    const char* data;
+
+    // Find the offset in the offset table whose name gives the longest
+    // match against the name to be rendered.
+    size_t nlabels_uncomp;
+    uint16_t ptr_offset = MessageRendererImpl::NO_OFFSET;
+    const bool case_sensitive = (impl_->compress_mode_ ==
+                                 MessageRenderer::CASE_SENSITIVE);
+    for (nlabels_uncomp = 0; nlabels_uncomp < nlabels; ++nlabels_uncomp) {
+        data = sequence.getData(&data_len);
+        if (data_len == 1) { // trailing dot.
+            ++nlabels_uncomp;
+            break;
         }
-        n = impl_->nodeset_.find(NameCompressNode(*this, impl_->nbuffer_, i,
-                                                  impl_->nbuffer_.getLength() -
-                                                  i));
-        if (n != notfound) {
+        // write with range check for safety
+        impl_->seq_hashes_.at(nlabels_uncomp) =
+            sequence.getHash(impl_->compress_mode_);
+        InputBuffer name_buf(data, data_len);
+        ptr_offset = impl_->findOffset(getBuffer(), name_buf,
+                                       impl_->seq_hashes_[nlabels_uncomp],
+                                       case_sensitive);
+        if (ptr_offset != MessageRendererImpl::NO_OFFSET) {
             break;
         }
+        sequence.stripLeft(1);
     }
 
-    // Record the current offset before extending the buffer.
-    const size_t offset = getLength();
-    // Write uncompress part...
-    writeData(impl_->nbuffer_.getData(),
-              compress ? i : impl_->nbuffer_.getLength());
-    if (compress && n != notfound) {
-        // ...and compression pointer if available.
-        uint16_t pointer = (*n).pos_;
-        pointer |= Name::COMPRESS_POINTER_MARK16;
-        writeUint16(pointer);
+    // Record the current offset before updating the offset table
+    size_t offset = getLength();
+    // Write uncompress part:
+    if (nlabels_uncomp > 0 || !compress) {
+        LabelSequence uncomp_sequence(name);
+        if (compress && nlabels > nlabels_uncomp) {
+            // If there's compressed part, strip off that part.
+            uncomp_sequence.stripRight(nlabels - nlabels_uncomp);
+        }
+        data = uncomp_sequence.getData(&data_len);
+        writeData(data, data_len);
+    }
+    // And write compression pointer if available:
+    if (compress && ptr_offset != MessageRendererImpl::NO_OFFSET) {
+        ptr_offset |= Name::COMPRESS_POINTER_MARK16;
+        writeUint16(ptr_offset);
     }
 
-    // Finally, add to the set the newly rendered name and its ancestors that
-    // have not been in the set.
-    for (unsigned int j = 0; j < i; j += impl_->nbuffer_[j] + 1) {
-        if (impl_->nbuffer_[j] == 0) {
-            continue;
+    // Finally, record the offset and length for each uncompressed sequence
+    // in the hash table.  The renderer's buffer has just stored the
+    // corresponding data, so we use the rendered data to get the length
+    // of each label of the names.
+    size_t seqlen = name.getLength();
+    for (size_t i = 0; i < nlabels_uncomp; ++i) {
+        const uint8_t label_len = getBuffer()[offset];
+        if (label_len == 0) { // offset for root doesn't need to be stored.
+            break;
         }
-        if (offset + j > Name::MAX_COMPRESS_POINTER) {
+        if (offset > Name::MAX_COMPRESS_POINTER) {
             break;
         }
-        impl_->nodeset_.insert(NameCompressNode(*this, getBuffer(),
-                                                offset + j,
-                                                impl_->nbuffer_.getLength() -
-                                                j));
+        // Store the tuple of <hash, offset, len> to the table.  Note that we
+        // already know the hash value for each name.
+        impl_->addOffset(impl_->seq_hashes_[i], offset, seqlen);
+        offset += (label_len + 1);
+        seqlen -= (label_len + 1);
+    }
+}
+
+AbstractMessageRenderer::AbstractMessageRenderer() :
+    local_buffer_(0), buffer_(&local_buffer_)
+{
+}
+
+void
+AbstractMessageRenderer::setBuffer(OutputBuffer* buffer) {
+    if (buffer != NULL && buffer_->getLength() != 0) {
+        isc_throw(isc::InvalidParameter,
+                  "MessageRenderer buffer cannot be set when in use");
+    } if (buffer == NULL && buffer_ == &local_buffer_) {
+        isc_throw(isc::InvalidParameter,
+                  "Default MessageRenderer buffer cannot be reset");
+    }
+
+    if (buffer == NULL) {
+        // Reset to the default buffer, then clear other internal resources.
+        // The order is important; we need to keep the used buffer intact.
+        buffer_ = &local_buffer_;
+        clear();
+    } else {
+        buffer_ = buffer;
     }
 }
 
 void
 AbstractMessageRenderer::clear() {
-    buffer_.clear();
+    buffer_->clear();
 }
 
 }
diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h
index 52d9245..4c1c92a 100644
--- a/src/lib/dns/messagerenderer.h
+++ b/src/lib/dns/messagerenderer.h
@@ -37,9 +37,15 @@ class Name;
 /// comprehensive \c Message class internally; normal applications won't have
 /// to care about details of this class.
 ///
-/// Once a renderer class object is constructed with a buffer, it is
-/// generally expected that all rendering operations are performed via that
-/// object.  If the application modifies the buffer in
+/// By default any (derived) renderer class object is associated with
+/// an internal buffer, and subsequent write operations will be performed
+/// on that buffer.  The rendering result can be retrieved via the
+/// \c getData() method.
+///
+/// If an application wants a separate buffer can be (normally temporarily)
+/// set for rendering operations via the \c setBuffer() method.  In that case,
+/// it is generally expected that all rendering operations are performed via
+/// that object.  If the application modifies the buffer in
 /// parallel with the renderer, the result will be undefined.
 ///
 /// Note to developers: we introduced a separate class for name compression
@@ -101,30 +107,30 @@ protected:
     ///
     /// This is intentionally defined as \c protected as this base class should
     /// never be instantiated (except as part of a derived class).
-    /// \param buffer The buffer where the data should be rendered into.
-    /// \todo We might want to revisit this API at some point and remove the
-    ///     buffer parameter. In that case it would create it's own buffer and
-    ///     a function to extract the data would be available instead. It seems
-    ///     like a cleaner design, but it's left undone until we would actually
-    ///     benefit from the change.
-    AbstractMessageRenderer(isc::util::OutputBuffer& buffer) :
-        buffer_(buffer)
-    {}
+    AbstractMessageRenderer();
+
 public:
     /// \brief The destructor.
     virtual ~AbstractMessageRenderer() {}
     //@}
 protected:
     /// \brief Return the output buffer we render into.
-    const isc::util::OutputBuffer& getBuffer() const { return (buffer_); }
-    isc::util::OutputBuffer& getBuffer() { return (buffer_); }
+    const isc::util::OutputBuffer& getBuffer() const { return (*buffer_); }
+    isc::util::OutputBuffer& getBuffer() { return (*buffer_); }
 private:
-    /// \short Buffer to store data
+    /// \brief Local (default) buffer to store data.
+    isc::util::OutputBuffer local_buffer_;
+
+    /// \brief Buffer to store data.
+    ///
+    /// Note that the class interface ensures this pointer is never NULL;
+    /// it either refers to \c local_buffer_ or to an application-supplied
+    /// buffer by \c setBuffer().
     ///
     /// It was decided that there's no need to have this in every subclass,
-    /// at least not now, and this reduces code size and gives compiler a better
-    /// chance to optimise.
-    isc::util::OutputBuffer& buffer_;
+    /// at least not now, and this reduces code size and gives compiler a
+    /// better chance to optimise.
+    isc::util::OutputBuffer* buffer_;
 public:
     ///
     /// \name Getter Methods
@@ -136,12 +142,12 @@ public:
     /// This method works exactly same as the same method of the \c OutputBuffer
     /// class; all notes for \c OutputBuffer apply.
     const void* getData() const {
-        return (buffer_.getData());
+        return (buffer_->getData());
     }
 
     /// \brief Return the length of data written in the internal buffer.
     size_t getLength() const {
-        return (buffer_.getLength());
+        return (buffer_->getLength());
     }
 
     /// \brief Return whether truncation has occurred while rendering.
@@ -175,6 +181,35 @@ public:
     /// \name Setter Methods
     ///
     //@{
+    /// \brief Set or reset a temporary output buffer.
+    ///
+    /// This method can be used for an application that manages an output
+    /// buffer separately from the message renderer and wants to keep reusing
+    /// the renderer.  When the renderer is associated with the default buffer
+    /// and the given pointer is non NULL, the given buffer will be
+    /// (temporarily) used for subsequent message rendering; if the renderer
+    /// is associated with a temporary buffer and the given pointer is NULL,
+    /// the renderer will be reset with the default buffer.  In the latter
+    /// case any additional resources (possibly specific to a derived renderer
+    /// class) will be cleared, but the temporary buffer is kept as the latest
+    /// state (which would normally store the rendering result).
+    ///
+    /// This method imposes some restrictions to prevent accidental misuse
+    /// that could cause disruption such as dereferencing an invalid object.
+    /// First, a temporary buffer must not be set when the associated buffer
+    /// is in use, that is, any data are stored in the buffer.  Also, the
+    /// default buffer cannot be "reset"; when NULL is specified a temporary
+    /// buffer must have been set beforehand.  If these conditions aren't met
+    /// an isc::InvalidParameter exception will be thrown.  This method is
+    /// exception free otherwise.
+    ///
+    /// \throw isc::InvalidParameter A restrictions of the method usage isn't
+    /// met.
+    ///
+    /// \param buffer A pointer to a temporary output buffer or NULL for reset
+    /// it.
+    void setBuffer(isc::util::OutputBuffer* buffer);
+
     /// \brief Mark the renderer to indicate truncation has occurred while
     /// rendering.
     ///
@@ -209,7 +244,7 @@ public:
     ///
     /// \param len The length of the gap to be inserted in bytes.
     void skip(size_t len) {
-        buffer_.skip(len);
+        buffer_->skip(len);
     }
 
     /// \brief Trim the specified length of data from the end of the internal
@@ -223,7 +258,7 @@ public:
     ///
     /// \param len The length of data that should be trimmed.
     void trim(size_t len) {
-        buffer_.trim(len);
+        buffer_->trim(len);
     }
 
     /// \brief Clear the internal buffer and other internal resources.
@@ -236,7 +271,7 @@ public:
     ///
     /// \param data The 8-bit integer to be written into the internal buffer.
     void writeUint8(const uint8_t data) {
-        buffer_.writeUint8(data);
+        buffer_->writeUint8(data);
     }
 
     /// \brief Write an unsigned 16-bit integer in host byte order into the
@@ -244,7 +279,7 @@ public:
     ///
     /// \param data The 16-bit integer to be written into the buffer.
     void writeUint16(uint16_t data) {
-        buffer_.writeUint16(data);
+        buffer_->writeUint16(data);
     }
 
     /// \brief Write an unsigned 16-bit integer in host byte order at the
@@ -259,7 +294,7 @@ public:
     /// \param data The 16-bit integer to be written into the internal buffer.
     /// \param pos The beginning position in the buffer to write the data.
     void writeUint16At(uint16_t data, size_t pos) {
-        buffer_.writeUint16At(data, pos);
+        buffer_->writeUint16At(data, pos);
     }
 
     /// \brief Write an unsigned 32-bit integer in host byte order into the
@@ -267,7 +302,7 @@ public:
     ///
     /// \param data The 32-bit integer to be written into the buffer.
     void writeUint32(uint32_t data) {
-        buffer_.writeUint32(data);
+        buffer_->writeUint32(data);
     }
 
     /// \brief Copy an arbitrary length of data into the internal buffer
@@ -278,7 +313,7 @@ public:
     /// \param data A pointer to the data to be copied into the internal buffer.
     /// \param len The length of the data in bytes.
     void writeData(const void *data, size_t len) {
-        buffer_.writeData(data, len);
+        buffer_->writeData(data, len);
     }
 
     /// \brief Write a \c Name object into the internal buffer in wire format,
@@ -316,10 +351,7 @@ public:
     using AbstractMessageRenderer::CASE_SENSITIVE;
 
     /// \brief Constructor from an output buffer.
-    ///
-    /// \param buffer An \c OutputBuffer object to which wire format data is
-    /// written.
-    MessageRenderer(isc::util::OutputBuffer& buffer);
+    MessageRenderer();
 
     virtual ~MessageRenderer();
     virtual bool isTruncated() const;
@@ -327,7 +359,17 @@ public:
     virtual CompressMode getCompressMode() const;
     virtual void setTruncated();
     virtual void setLengthLimit(size_t len);
+
+    /// This implementation does not allow this call in the middle of
+    /// rendering (i.e. after at least one name is rendered) due to
+    /// restriction specific to the internal implementation.  Such attempts
+    /// will result in an \c isc::InvalidParameter exception.
+    ///
+    /// This shouldn't be too restrictive in practice; there's no known
+    /// practical case for such a mixed compression policy in a single
+    /// message.
     virtual void setCompressMode(CompressMode mode);
+
     virtual void clear();
     virtual void writeName(const Name& name, bool compress = true);
 private:
diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc
index 772417f..d642e97 100644
--- a/src/lib/dns/name.cc
+++ b/src/lib/dns/name.cc
@@ -23,11 +23,13 @@
 #include <util/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/name.h>
+#include <dns/name_internal.h>
 #include <dns/messagerenderer.h>
 
 using namespace std;
 using namespace isc::util;
 using isc::dns::NameComparisonResult;
+using namespace isc::dns::name::internal;
 
 namespace isc {
 namespace dns {
@@ -46,12 +48,12 @@ namespace {
 /// we chose the naive but simple hardcoding approach.
 ///
 /// These definitions are derived from BIND 9's libdns module.
-/// Note: it was not clear why the maptolower array was needed rather than
-/// using the standard tolower() function.  It was perhaps due performance
-/// concern, but we were not sure.  Even if it was performance reasons, we
-/// should carefully assess the effect via benchmarking to avoid the pitfall of 
-/// premature optimization.  We should revisit this point later.
-static const char digitvalue[256] = {
+/// Note: we could use the standard tolower() function instead of the
+/// maptolower array, but a benchmark indicated that the private array could
+/// improve the performance of message rendering (which internally uses the
+/// array heavily) about 27%.  Since we want to achieve very good performance
+/// for message rendering in some cases, we'll keep using it.
+const char digitvalue[256] = {
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 48
@@ -69,8 +71,11 @@ static const char digitvalue[256] = {
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 256
 };
+}
 
-static const unsigned char maptolower[] = {
+namespace name {
+namespace internal {
+const unsigned char maptolower[] = {
     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
     0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
@@ -104,7 +109,8 @@ static const unsigned char maptolower[] = {
     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
 };
-}
+} // end of internal
+} // end of name
 
 namespace {
 ///
@@ -163,7 +169,8 @@ Name::Name(const std::string &namestring, bool downcase) {
             //
             if (c == '.') {
                 if (s != send) {
-                    isc_throw(EmptyLabel, "non terminating empty label");
+                    isc_throw(EmptyLabel,
+                              "non terminating empty label in " << namestring);
                 }
                 is_root = true;
             } else if (c == '@' && s == send) {
@@ -191,7 +198,8 @@ Name::Name(const std::string &namestring, bool downcase) {
         case ft_ordinary:
             if (c == '.') {
                 if (count == 0) {
-                    isc_throw(EmptyLabel, "duplicate period");
+                    isc_throw(EmptyLabel,
+                              "duplicate period in " << namestring);
                 }
                 ndata.at(offsets.back()) = count;
                 offsets.push_back(ndata.size());
@@ -204,7 +212,8 @@ Name::Name(const std::string &namestring, bool downcase) {
                 state = ft_escape;
             } else {
                 if (++count > MAX_LABELLEN) {
-                    isc_throw(TooLongLabel, "label is too long");
+                    isc_throw(TooLongLabel,
+                              "label is too long in " << namestring);
                 }
                 ndata.push_back(downcase ? maptolower[c] : c);
             }
@@ -213,14 +222,16 @@ Name::Name(const std::string &namestring, bool downcase) {
             if (c == '[') {
                 // This looks like a bitstring label, which was deprecated.
                 // Intentionally drop it.
-                isc_throw(BadLabelType, "invalid label type");
+                isc_throw(BadLabelType,
+                          "invalid label type in " << namestring);
             }
             state = ft_escape;
             // FALLTHROUGH
         case ft_escape:
             if (!isdigit(c & 0xff)) {
                 if (++count > MAX_LABELLEN) {
-                    isc_throw(TooLongLabel, "label is too long");
+                    isc_throw(TooLongLabel,
+                              "label is too long in " << namestring);
                 }
                 ndata.push_back(downcase ? maptolower[c] : c);
                 state = ft_ordinary;
@@ -232,17 +243,22 @@ Name::Name(const std::string &namestring, bool downcase) {
             // FALLTHROUGH
         case ft_escdecimal:
             if (!isdigit(c & 0xff)) {
-                isc_throw(BadEscape, "mixture of escaped digit and non-digit");
+                isc_throw(BadEscape,
+                          "mixture of escaped digit and non-digit in "
+                          << namestring);
             }
             value *= 10;
             value += digitvalue[c];
             digits++;
             if (digits == 3) {
                 if (value > 255) {
-                    isc_throw(BadEscape, "escaped decimal is too large");
+                    isc_throw(BadEscape,
+                              "escaped decimal is too large in "
+                              << namestring);
                 }
                 if (++count > MAX_LABELLEN) {
-                    isc_throw(TooLongLabel, "label is too long");
+                    isc_throw(TooLongLabel,
+                              "label is too long in " << namestring);
                 }
                 ndata.push_back(downcase ? maptolower[value] : value);
                 state = ft_ordinary;
@@ -256,11 +272,14 @@ Name::Name(const std::string &namestring, bool downcase) {
 
     if (!done) {                // no trailing '.' was found.
         if (ndata.size() == Name::MAX_WIRE) {
-            isc_throw(TooLongName, "name is too long for termination");
+            isc_throw(TooLongName,
+                      "name is too long for termination in " << namestring);
         }
         assert(s == send);
         if (state != ft_ordinary && state != ft_at) {
-            isc_throw(IncompleteName, "incomplete textual name");
+            isc_throw(IncompleteName,
+                      "incomplete textual name in " <<
+                      (namestring.empty() ? "<empty>" : namestring));
         }
         if (state == ft_ordinary) {
             assert(count != 0);
diff --git a/src/lib/dns/name.h b/src/lib/dns/name.h
index 4ff7fe5..ef32f90 100644
--- a/src/lib/dns/name.h
+++ b/src/lib/dns/name.h
@@ -32,33 +32,42 @@ namespace dns {
 class AbstractMessageRenderer;
 
 ///
+/// \brief Base class for name parser exceptions.
+///
+class NameParserException : public Exception {
+public:
+    NameParserException(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+///
 /// \brief A standard DNS module exception that is thrown if the name parser
 /// encounters an empty label in the middle of a name.
 ///
-class EmptyLabel : public Exception {
+class EmptyLabel : public NameParserException {
 public:
     EmptyLabel(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
+        NameParserException(file, line, what) {}
 };
 
 ///
 /// \brief A standard DNS module exception that is thrown if the name parser
 /// encounters too long a name.
 ///
-class TooLongName : public Exception {
+class TooLongName : public NameParserException {
 public:
     TooLongName(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
+        NameParserException(file, line, what) {}
 };
 
 ///
 /// \brief A standard DNS module exception that is thrown if the name parser
 /// encounters too long a label.
 ///
-class TooLongLabel : public Exception {
+class TooLongLabel : public NameParserException {
 public:
     TooLongLabel(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
+        NameParserException(file, line, what) {}
 };
 
 ///
@@ -67,20 +76,20 @@ public:
 /// applies to bitstring labels, which would begin with "\[".  Incomplete cases
 /// include an incomplete escaped sequence such as "\12".
 ///
-class BadLabelType : public Exception {
+class BadLabelType : public NameParserException {
 public:
     BadLabelType(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
+        NameParserException(file, line, what) {}
 };
 
 ///
 /// \brief A standard DNS module exception that is thrown if the name parser
 /// fails to decode a "\"-escaped sequence.
 ///
-class BadEscape : public Exception {
+class BadEscape : public NameParserException {
 public:
     BadEscape(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
+        NameParserException(file, line, what) {}
 };
 
 ///
@@ -90,10 +99,10 @@ public:
 /// An attempt of constructing a name from an empty string will trigger this
 /// exception.
 ///
-class IncompleteName : public Exception {
+class IncompleteName : public NameParserException {
 public:
     IncompleteName(const char* file, size_t line, const char* what) :
-        isc::Exception(file, line, what) {}
+        NameParserException(file, line, what) {}
 };
 
 ///
@@ -210,6 +219,11 @@ private:
 /// names as a special case.
 ///
 class Name {
+    // LabelSequences use knowledge about the internal data structure
+    // of this class for efficiency (they use the offsets_ vector and
+    // the ndata_ string)
+    friend class LabelSequence;
+
     ///
     /// \name Constructors and Destructor
     ///
@@ -298,6 +312,7 @@ public:
         }
         return (ndata_[pos]);
     }
+
     /// \brief Gets the length of the <code>Name</code> in its wire format.
     ///
     /// This method never throws an exception.
diff --git a/src/lib/dns/name_internal.h b/src/lib/dns/name_internal.h
new file mode 100644
index 0000000..e1eab8c
--- /dev/null
+++ b/src/lib/dns/name_internal.h
@@ -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.
+
+#ifndef __NAME_INTERNAL_H
+#define __NAME_INTERNAL_H 1
+
+// This is effectively a "private" namespace for the Name class implementation,
+// but exposed publicly so the definitions in it can be shared with other
+// modules of the library (as of its introduction, used by LabelSequence and
+// MessageRenderer).  It's not expected to be used even by normal applications.
+// This header file is therefore not expected to be installed as part of the
+// library.
+//
+// Note: if it turns out that we need this shortcut for many other places
+// we may even want to make it expose to other BIND 10 modules, but for now
+// we'll keep it semi-private (note also that except for very performance
+// sensitive applications the standard std::tolower() function should be just
+// sufficient).
+namespace isc {
+namespace dns {
+namespace name {
+namespace internal {
+extern const unsigned char maptolower[];
+} // end of internal
+} // end of name
+} // end of dns
+} // end of isc
+#endif // __NAME_INTERNAL_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/python/Makefile.am b/src/lib/dns/python/Makefile.am
index 780b464..2846659 100644
--- a/src/lib/dns/python/Makefile.am
+++ b/src/lib/dns/python/Makefile.am
@@ -46,7 +46,7 @@ EXTRA_DIST += nsec3hash_python_inc.cc
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-pydnspp_la_LDFLAGS += -module
+pydnspp_la_LDFLAGS += -module -avoid-version
 pydnspp_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
 pydnspp_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 pydnspp_la_LIBADD += libpydnspp.la
diff --git a/src/lib/dns/python/message_python.cc b/src/lib/dns/python/message_python.cc
index 2f1e2e5..c7ad2ff 100644
--- a/src/lib/dns/python/message_python.cc
+++ b/src/lib/dns/python/message_python.cc
@@ -377,8 +377,9 @@ Message_getTSIGRecord(s_Message* self) {
 
         if (tsig_record == NULL) {
             Py_RETURN_NONE;
+        } else {
+            return (createTSIGRecordObject(*tsig_record));
         }
-        return (createTSIGRecordObject(*tsig_record));
     } catch (const InvalidMessageOperation& ex) {
         PyErr_SetString(po_InvalidMessageOperation, ex.what());
     } catch (const exception& ex) {
diff --git a/src/lib/dns/python/messagerenderer_python.cc b/src/lib/dns/python/messagerenderer_python.cc
index bb89622..5561c12 100644
--- a/src/lib/dns/python/messagerenderer_python.cc
+++ b/src/lib/dns/python/messagerenderer_python.cc
@@ -36,7 +36,6 @@ namespace {
 class s_MessageRenderer : public PyObject {
 public:
     s_MessageRenderer();
-    isc::util::OutputBuffer* outputbuffer;
     MessageRenderer* cppobj;
 };
 
@@ -78,17 +77,14 @@ PyMethodDef MessageRenderer_methods[] = {
 
 int
 MessageRenderer_init(s_MessageRenderer* self) {
-    self->outputbuffer = new OutputBuffer(4096);
-    self->cppobj = new MessageRenderer(*self->outputbuffer);
+    self->cppobj = new MessageRenderer;
     return (0);
 }
 
 void
 MessageRenderer_destroy(s_MessageRenderer* self) {
     delete self->cppobj;
-    delete self->outputbuffer;
     self->cppobj = NULL;
-    self->outputbuffer = NULL;
     Py_TYPE(self)->tp_free(self);
 }
 
diff --git a/src/lib/dns/rdata/generic/sshfp_44.cc b/src/lib/dns/rdata/generic/sshfp_44.cc
new file mode 100644
index 0000000..6320fd9
--- /dev/null
+++ b/src/lib/dns/rdata/generic/sshfp_44.cc
@@ -0,0 +1,164 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+
+using namespace std;
+using namespace boost;
+using namespace isc::util;
+using namespace isc::util::encode;
+
+// BEGIN_ISC_NAMESPACE
+// BEGIN_RDATA_NAMESPACE
+
+SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len)
+{
+    if (rdata_len < 2) {
+      isc_throw(InvalidRdataLength, "SSHFP record too short");
+    }
+
+    algorithm_ = buffer.readUint8();
+    fingerprint_type_ = buffer.readUint8();
+
+    rdata_len -= 2;
+    fingerprint_.resize(rdata_len);
+    buffer.readData(&fingerprint_[0], rdata_len);
+}
+
+SSHFP::SSHFP(const std::string& sshfp_str)
+{
+    std::istringstream iss(sshfp_str);
+    // peekc should be of iss's char_type for isspace to work
+    std::istringstream::char_type peekc;
+    std::stringbuf fingerprintbuf;
+    uint32_t algorithm, fingerprint_type;
+
+    iss >> algorithm >> fingerprint_type;
+    if (iss.bad() || iss.fail()) {
+      isc_throw(InvalidRdataText, "Invalid SSHFP text");
+    }
+    if ((algorithm < 1) || (algorithm > 2)) {
+      isc_throw(InvalidRdataText, "SSHFP algorithm number out of range");
+    }
+    if (fingerprint_type != 1) {
+      isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range");
+    }
+
+    iss.read(&peekc, 1);
+    if (!iss.good() || !isspace(peekc, iss.getloc())) {
+      isc_throw(InvalidRdataText, "SSHFP presentation format error");
+    }
+
+    iss >> &fingerprintbuf;
+
+    algorithm_ = algorithm;
+    fingerprint_type_ = fingerprint_type;
+    decodeHex(fingerprintbuf.str(), fingerprint_);
+}
+
+SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const string& fingerprint)
+{
+    if ((algorithm < 1) || (algorithm > 2)) {
+      isc_throw(InvalidRdataText, "SSHFP algorithm number out of range");
+    }
+    if (fingerprint_type != 1) {
+      isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range");
+    }
+
+    algorithm_ = algorithm;
+    fingerprint_type_ = fingerprint_type;
+    decodeHex(fingerprint, fingerprint_);
+}
+
+SSHFP::SSHFP(const SSHFP& other) :
+  Rdata(), algorithm_(other.algorithm_), fingerprint_type_(other.fingerprint_type_), fingerprint_(other.fingerprint_)
+{}
+
+void
+SSHFP::toWire(OutputBuffer& buffer) const {
+    buffer.writeUint8(algorithm_);
+    buffer.writeUint8(fingerprint_type_);
+    buffer.writeData(&fingerprint_[0], fingerprint_.size());
+}
+
+void
+SSHFP::toWire(AbstractMessageRenderer& renderer) const {
+    renderer.writeUint8(algorithm_);
+    renderer.writeUint8(fingerprint_type_);
+    renderer.writeData(&fingerprint_[0], fingerprint_.size());
+}
+
+string
+SSHFP::toText() const {
+    return (lexical_cast<string>(static_cast<int>(algorithm_)) +
+            " " + lexical_cast<string>(static_cast<int>(fingerprint_type_)) +
+            " " + encodeHex(fingerprint_));
+}
+
+int
+SSHFP::compare(const Rdata& other) const {
+    const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(other);
+
+    /* This doesn't really make any sort of sense, but in the name of
+       consistency... */
+
+    if (algorithm_ < other_sshfp.algorithm_) {
+        return (-1);
+    } else if (algorithm_ > other_sshfp.algorithm_) {
+        return (1);
+    }
+
+    if (fingerprint_type_ < other_sshfp.fingerprint_type_) {
+        return (-1);
+    } else if (fingerprint_type_ > other_sshfp.fingerprint_type_) {
+        return (1);
+    }
+
+    size_t this_len = fingerprint_.size();
+    size_t other_len = other_sshfp.fingerprint_.size();
+    size_t cmplen = min(this_len, other_len);
+    int cmp = memcmp(&fingerprint_[0], &other_sshfp.fingerprint_[0], cmplen);
+    if (cmp != 0) {
+      return (cmp);
+    } else {
+      return ((this_len == other_len)
+	      ? 0 : (this_len < other_len) ? -1 : 1);
+    }
+}
+
+uint8_t
+SSHFP::getSSHFPAlgorithmNumber() const {
+    return (algorithm_);
+}
+
+uint8_t
+SSHFP::getSSHFPFingerprintType() const {
+    return (fingerprint_type_);
+}
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
diff --git a/src/lib/dns/rdata/generic/sshfp_44.h b/src/lib/dns/rdata/generic/sshfp_44.h
new file mode 100644
index 0000000..c3ba944
--- /dev/null
+++ b/src/lib/dns/rdata/generic/sshfp_44.h
@@ -0,0 +1,58 @@
+// 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.
+
+// BEGIN_HEADER_GUARD
+
+#include <stdint.h>
+
+#include <string>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+
+// BEGIN_ISC_NAMESPACE
+
+// BEGIN_COMMON_DECLARATIONS
+// END_COMMON_DECLARATIONS
+
+// BEGIN_RDATA_NAMESPACE
+
+class SSHFP : public Rdata {
+public:
+    // BEGIN_COMMON_MEMBERS
+    // END_COMMON_MEMBERS
+
+    SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const std::string& fingerprint);
+
+    ///
+    /// Specialized methods
+    ///
+    uint8_t getSSHFPAlgorithmNumber() const;
+    uint8_t getSSHFPFingerprintType() const;
+
+private:
+    /// Note: this is a prototype version; we may reconsider
+    /// this representation later.
+    uint8_t algorithm_;
+    uint8_t fingerprint_type_;
+    std::vector<uint8_t> fingerprint_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc
index e85f82c..ee1097e 100644
--- a/src/lib/dns/rdata/template.cc
+++ b/src/lib/dns/rdata/template.cc
@@ -58,6 +58,7 @@ MyType::toWire(AbstractMessageRenderer& renderer) const {
 int
 MyType::compare(const Rdata& other) const {
     // The compare method normally begins with this dynamic cast.
+    // cppcheck-suppress unreadVariable
     const MyType& other_mytype = dynamic_cast<const MyType&>(other);
     // ...
 }
diff --git a/src/lib/dns/rdatafields.cc b/src/lib/dns/rdatafields.cc
index af9ba2e..94c9dbf 100644
--- a/src/lib/dns/rdatafields.cc
+++ b/src/lib/dns/rdatafields.cc
@@ -70,8 +70,7 @@ namespace {
 // it's hopefully an acceptable practice.
 class RdataFieldComposer : public AbstractMessageRenderer {
 public:
-    RdataFieldComposer(OutputBuffer& buffer) :
-        AbstractMessageRenderer(buffer),
+    RdataFieldComposer() :
         truncated_(false), length_limit_(65535),
         mode_(CASE_INSENSITIVE), last_data_pos_(0)
     {}
@@ -128,8 +127,7 @@ public:
 }
 
 RdataFields::RdataFields(const Rdata& rdata) {
-    OutputBuffer buffer(0);
-    RdataFieldComposer field_composer(buffer);
+    RdataFieldComposer field_composer;
     rdata.toWire(field_composer);
     nfields_ = field_composer.getFields().size();
     data_length_ = field_composer.getLength();
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
index 776d49f..9a55f5f 100644
--- a/src/lib/dns/rrset.cc
+++ b/src/lib/dns/rrset.cc
@@ -113,6 +113,16 @@ AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
     return (rrs_written);
 }
 
+bool
+AbstractRRset::isSameKind(const AbstractRRset& other) const {
+  // Compare classes last as they're likely to be identical. Compare
+  // names late in the list too, as these are expensive. So we compare
+  // types first, names second and classes last.
+  return (getType() == other.getType() &&
+	  getName() == other.getName() &&
+	  getClass() == other.getClass());
+}
+
 ostream&
 operator<<(ostream& os, const AbstractRRset& rrset) {
     os << rrset.toText();
diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h
index 5042a98..43ade58 100644
--- a/src/lib/dns/rrset.h
+++ b/src/lib/dns/rrset.h
@@ -475,7 +475,15 @@ public:
     /// \brief Clear the RRSIGs for this RRset
     virtual void removeRRsig() = 0;
 
+    /// \brief Check whether two RRsets are of the same kind
+    ///
+    /// Checks if two RRsets have the same name, RR type, and RR class.
+    ///
+    /// \param other Pointer to another AbstractRRset to compare
+    ///              against.
+    virtual bool isSameKind(const AbstractRRset& other) const;
     //@}
+
 };
 
 /// \brief The \c RdataIterator class is an abstract base class that
diff --git a/src/lib/dns/tests/.gitignore b/src/lib/dns/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/dns/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 6e6f7f4..26b4630 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -19,6 +19,7 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES = unittest_util.h unittest_util.cc
 run_unittests_SOURCES += edns_unittest.cc
+run_unittests_SOURCES += labelsequence_unittest.cc
 run_unittests_SOURCES += messagerenderer_unittest.cc
 run_unittests_SOURCES += name_unittest.cc
 run_unittests_SOURCES += nsec3hash_unittest.cc
@@ -32,6 +33,7 @@ run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_txt_like_unittest.cc
 run_unittests_SOURCES += rdata_mx_unittest.cc
+run_unittests_SOURCES += rdata_sshfp_unittest.cc
 run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc
 run_unittests_SOURCES += rdata_dname_unittest.cc
 run_unittests_SOURCES += rdata_afsdb_unittest.cc
diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc
index 26cc01c..de2d244 100644
--- a/src/lib/dns/tests/edns_unittest.cc
+++ b/src/lib/dns/tests/edns_unittest.cc
@@ -43,9 +43,7 @@ const uint8_t EDNS::SUPPORTED_VERSION;
 namespace {
 class EDNSTest : public ::testing::Test {
 protected:
-    EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0),
-                 renderer(obuffer), rcode(0)
-    {
+    EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0), rcode(0) {
         opt_rdata = ConstRdataPtr(new generic::OPT());
         edns_base.setUDPSize(4096);
     }
diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc
new file mode 100644
index 0000000..98bb99c
--- /dev/null
+++ b/src/lib/dns/tests/labelsequence_unittest.cc
@@ -0,0 +1,348 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/labelsequence.h>
+#include <dns/name.h>
+#include <exceptions/exceptions.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/functional/hash.hpp>
+
+#include <string>
+#include <set>
+
+using namespace isc::dns;
+using namespace std;
+
+namespace {
+
+class LabelSequenceTest : public ::testing::Test {
+public:
+    LabelSequenceTest() : n1("example.org"), n2("example.com"),
+                          n3("example.org"), n4("foo.bar.test.example"),
+                          n5("example.ORG"), n6("ExAmPlE.org"),
+                          n7("."), n8("foo.example.org.bar"),
+                          ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5),
+                          ls6(n6), ls7(n7), ls8(n8)
+    {};
+    // Need to keep names in scope for at least the lifetime of
+    // the labelsequences
+    Name n1, n2, n3, n4, n5, n6, n7, n8;
+
+    LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8;
+};
+
+// Basic equality tests
+TEST_F(LabelSequenceTest, equals_sensitive) {
+    EXPECT_TRUE(ls1.equals(ls1, true));
+    EXPECT_FALSE(ls1.equals(ls2, true));
+    EXPECT_TRUE(ls1.equals(ls3, true));
+    EXPECT_FALSE(ls1.equals(ls4, true));
+    EXPECT_FALSE(ls1.equals(ls5, true));
+    EXPECT_FALSE(ls1.equals(ls6, true));
+    EXPECT_FALSE(ls1.equals(ls7, true));
+    EXPECT_FALSE(ls1.equals(ls8, true));
+
+    EXPECT_FALSE(ls2.equals(ls1, true));
+    EXPECT_TRUE(ls2.equals(ls2, true));
+    EXPECT_FALSE(ls2.equals(ls3, true));
+    EXPECT_FALSE(ls2.equals(ls4, true));
+    EXPECT_FALSE(ls2.equals(ls5, true));
+    EXPECT_FALSE(ls2.equals(ls6, true));
+    EXPECT_FALSE(ls2.equals(ls7, true));
+    EXPECT_FALSE(ls2.equals(ls8, true));
+
+    EXPECT_FALSE(ls4.equals(ls1, true));
+    EXPECT_FALSE(ls4.equals(ls2, true));
+    EXPECT_FALSE(ls4.equals(ls3, true));
+    EXPECT_TRUE(ls4.equals(ls4, true));
+    EXPECT_FALSE(ls4.equals(ls5, true));
+    EXPECT_FALSE(ls4.equals(ls6, true));
+    EXPECT_FALSE(ls4.equals(ls7, true));
+    EXPECT_FALSE(ls4.equals(ls8, true));
+
+    EXPECT_FALSE(ls5.equals(ls1, true));
+    EXPECT_FALSE(ls5.equals(ls2, true));
+    EXPECT_FALSE(ls5.equals(ls3, true));
+    EXPECT_FALSE(ls5.equals(ls4, true));
+    EXPECT_TRUE(ls5.equals(ls5, true));
+    EXPECT_FALSE(ls5.equals(ls6, true));
+    EXPECT_FALSE(ls5.equals(ls7, true));
+    EXPECT_FALSE(ls5.equals(ls8, true));
+}
+
+TEST_F(LabelSequenceTest, equals_insensitive) {
+    EXPECT_TRUE(ls1.equals(ls1));
+    EXPECT_FALSE(ls1.equals(ls2));
+    EXPECT_TRUE(ls1.equals(ls3));
+    EXPECT_FALSE(ls1.equals(ls4));
+    EXPECT_TRUE(ls1.equals(ls5));
+    EXPECT_TRUE(ls1.equals(ls6));
+    EXPECT_FALSE(ls1.equals(ls7));
+
+    EXPECT_FALSE(ls2.equals(ls1));
+    EXPECT_TRUE(ls2.equals(ls2));
+    EXPECT_FALSE(ls2.equals(ls3));
+    EXPECT_FALSE(ls2.equals(ls4));
+    EXPECT_FALSE(ls2.equals(ls5));
+    EXPECT_FALSE(ls2.equals(ls6));
+    EXPECT_FALSE(ls2.equals(ls7));
+
+    EXPECT_TRUE(ls3.equals(ls1));
+    EXPECT_FALSE(ls3.equals(ls2));
+    EXPECT_TRUE(ls3.equals(ls3));
+    EXPECT_FALSE(ls3.equals(ls4));
+    EXPECT_TRUE(ls3.equals(ls5));
+    EXPECT_TRUE(ls3.equals(ls6));
+    EXPECT_FALSE(ls3.equals(ls7));
+
+    EXPECT_FALSE(ls4.equals(ls1));
+    EXPECT_FALSE(ls4.equals(ls2));
+    EXPECT_FALSE(ls4.equals(ls3));
+    EXPECT_TRUE(ls4.equals(ls4));
+    EXPECT_FALSE(ls4.equals(ls5));
+    EXPECT_FALSE(ls4.equals(ls6));
+    EXPECT_FALSE(ls4.equals(ls7));
+
+    EXPECT_TRUE(ls5.equals(ls1));
+    EXPECT_FALSE(ls5.equals(ls2));
+    EXPECT_TRUE(ls5.equals(ls3));
+    EXPECT_FALSE(ls5.equals(ls4));
+    EXPECT_TRUE(ls5.equals(ls5));
+    EXPECT_TRUE(ls5.equals(ls6));
+    EXPECT_FALSE(ls5.equals(ls7));
+}
+
+void
+getDataCheck(const char* expected_data, size_t expected_len,
+             const LabelSequence& ls)
+{
+    size_t len;
+    const char* data = ls.getData(&len);
+    ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data <<
+                                    " name: " << ls.getName().toText();
+    EXPECT_EQ(expected_len, ls.getDataLength()) <<
+        "Expected data: " << expected_data <<
+        " name: " << ls.getName().toText();
+    for (size_t i = 0; i < len; ++i) {
+        EXPECT_EQ(expected_data[i], data[i]) << "Difference at pos " << i <<
+                                                ": Expected data: " <<
+                                                expected_data <<
+                                                " name: " <<
+                                                ls.getName().toText();;
+    }
+}
+
+TEST_F(LabelSequenceTest, getData) {
+    getDataCheck("\007example\003org\000", 13, ls1);
+    getDataCheck("\007example\003com\000", 13, ls2);
+    getDataCheck("\007example\003org\000", 13, ls3);
+    getDataCheck("\003foo\003bar\004test\007example\000", 22, ls4);
+    getDataCheck("\007example\003ORG\000", 13, ls5);
+    getDataCheck("\007ExAmPlE\003org\000", 13, ls6);
+    getDataCheck("\000", 1, ls7);
+};
+
+TEST_F(LabelSequenceTest, stripLeft) {
+    EXPECT_TRUE(ls1.equals(ls3));
+    ls1.stripLeft(0);
+    getDataCheck("\007example\003org\000", 13, ls1);
+    EXPECT_TRUE(ls1.equals(ls3));
+    ls1.stripLeft(1);
+    getDataCheck("\003org\000", 5, ls1);
+    EXPECT_FALSE(ls1.equals(ls3));
+    ls1.stripLeft(1);
+    getDataCheck("\000", 1, ls1);
+    EXPECT_TRUE(ls1.equals(ls7));
+
+    ls2.stripLeft(2);
+    getDataCheck("\000", 1, ls2);
+    EXPECT_TRUE(ls2.equals(ls7));
+}
+
+TEST_F(LabelSequenceTest, stripRight) {
+    EXPECT_TRUE(ls1.equals(ls3));
+    ls1.stripRight(1);
+    getDataCheck("\007example\003org", 12, ls1);
+    EXPECT_FALSE(ls1.equals(ls3));
+    ls1.stripRight(1);
+    getDataCheck("\007example", 8, ls1);
+    EXPECT_FALSE(ls1.equals(ls3));
+
+    ASSERT_FALSE(ls1.equals(ls2));
+    ls2.stripRight(2);
+    getDataCheck("\007example", 8, ls2);
+    EXPECT_TRUE(ls1.equals(ls2));
+}
+
+TEST_F(LabelSequenceTest, stripOutOfRange) {
+    EXPECT_THROW(ls1.stripLeft(100), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripLeft(5), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripLeft(4), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripLeft(3), isc::OutOfRange);
+    getDataCheck("\007example\003org\000", 13, ls1);
+
+    EXPECT_THROW(ls1.stripRight(100), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripRight(5), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripRight(4), isc::OutOfRange);
+    EXPECT_THROW(ls1.stripRight(3), isc::OutOfRange);
+    getDataCheck("\007example\003org\000", 13, ls1);
+}
+
+TEST_F(LabelSequenceTest, getLabelCount) {
+    EXPECT_EQ(3, ls1.getLabelCount());
+    ls1.stripLeft(0);
+    EXPECT_EQ(3, ls1.getLabelCount());
+    ls1.stripLeft(1);
+    EXPECT_EQ(2, ls1.getLabelCount());
+    ls1.stripLeft(1);
+    EXPECT_EQ(1, ls1.getLabelCount());
+
+    EXPECT_EQ(3, ls2.getLabelCount());
+    ls2.stripRight(1);
+    EXPECT_EQ(2, ls2.getLabelCount());
+    ls2.stripRight(1);
+    EXPECT_EQ(1, ls2.getLabelCount());
+
+    EXPECT_EQ(3, ls3.getLabelCount());
+    ls3.stripRight(2);
+    EXPECT_EQ(1, ls3.getLabelCount());
+
+    EXPECT_EQ(5, ls4.getLabelCount());
+    ls4.stripRight(3);
+    EXPECT_EQ(2, ls4.getLabelCount());
+
+    EXPECT_EQ(3, ls5.getLabelCount());
+    ls5.stripLeft(2);
+    EXPECT_EQ(1, ls5.getLabelCount());
+}
+
+TEST_F(LabelSequenceTest, comparePart) {
+    EXPECT_FALSE(ls1.equals(ls8));
+
+    // strip root label from example.org.
+    ls1.stripRight(1);
+    // strip foo from foo.example.org.bar.
+    ls8.stripLeft(1);
+    // strip bar. (i.e. bar and root) too
+    ls8.stripRight(2);
+
+    EXPECT_TRUE(ls1.equals(ls8));
+
+    // Data comparison
+    size_t len;
+    const char* data = ls1.getData(&len);
+    getDataCheck(data, len, ls8);
+}
+
+TEST_F(LabelSequenceTest, isAbsolute) {
+    ASSERT_TRUE(ls1.isAbsolute());
+
+    ls1.stripLeft(1);
+    ASSERT_TRUE(ls1.isAbsolute());
+    ls1.stripRight(1);
+    ASSERT_FALSE(ls1.isAbsolute());
+
+    ASSERT_TRUE(ls2.isAbsolute());
+    ls2.stripRight(1);
+    ASSERT_FALSE(ls2.isAbsolute());
+
+    ASSERT_TRUE(ls3.isAbsolute());
+    ls3.stripLeft(2);
+    ASSERT_TRUE(ls3.isAbsolute());
+}
+
+// The following are test data used in the getHash test below.  Normally
+// we use example/documentation domain names for testing, but in this case
+// we'd specifically like to use more realistic data, and are intentionally
+// using real-world samples: They are the NS names of root and some top level
+// domains as of this test.
+const char* const root_servers[] = {
+    "a.root-servers.net", "b.root-servers.net", "c.root-servers.net",
+    "d.root-servers.net", "e.root-servers.net", "f.root-servers.net",
+    "g.root-servers.net", "h.root-servers.net", "i.root-servers.net",
+    "j.root-servers.net", "k.root-servers.net", "l.root-servers.net",
+    "m.root-servers.net", NULL
+};
+const char* const gtld_servers[] = {
+    "a.gtld-servers.net", "b.gtld-servers.net", "c.gtld-servers.net",
+    "d.gtld-servers.net", "e.gtld-servers.net", "f.gtld-servers.net",
+    "g.gtld-servers.net", "h.gtld-servers.net", "i.gtld-servers.net",
+    "j.gtld-servers.net", "k.gtld-servers.net", "l.gtld-servers.net",
+    "m.gtld-servers.net", NULL
+};
+const char* const jp_servers[] = {
+    "a.dns.jp", "b.dns.jp", "c.dns.jp", "d.dns.jp", "e.dns.jp",
+    "f.dns.jp", "g.dns.jp", NULL
+};
+const char* const cn_servers[] = {
+    "a.dns.cn", "b.dns.cn", "c.dns.cn", "d.dns.cn", "e.dns.cn",
+    "ns.cernet.net", NULL
+};
+const char* const ca_servers[] = {
+    "k.ca-servers.ca", "e.ca-servers.ca", "a.ca-servers.ca", "z.ca-servers.ca",
+    "tld.isc-sns.net", "c.ca-servers.ca", "j.ca-servers.ca", "l.ca-servers.ca",
+    "sns-pb.isc.org", "f.ca-servers.ca", NULL
+};
+
+// A helper function used in the getHash test below.
+void
+hashDistributionCheck(const char* const* servers) {
+    const size_t BUCKETS = 64;  // constant used in the MessageRenderer
+    set<Name> names;
+    vector<size_t> hash_counts(BUCKETS);
+
+    // Store all test names and their super domain names (excluding the
+    // "root" label) in the set, calculates their hash values, and increments
+    // the counter for the corresponding hash "bucket".
+    for (size_t i = 0; servers[i] != NULL; ++i) {
+        const Name name(servers[i]);
+        for (size_t l = 0; l < name.getLabelCount() - 1; ++l) {
+            pair<set<Name>::const_iterator, bool> ret =
+                names.insert(name.split(l));
+            if (ret.second) {
+                hash_counts[LabelSequence((*ret.first)).getHash(false) %
+                            BUCKETS]++;
+            }
+        }
+    }
+
+    // See how many conflicts we have in the buckets.  For the testing purpose
+    // we expect there's at most 2 conflicts in each set, which is an
+    // arbitrary choice (it should happen to succeed with the hash function
+    // and data we are using; if it's not the case, maybe with an update to
+    // the hash implementation, we should revise the test).
+    for (size_t i = 0; i < BUCKETS; ++i) {
+        EXPECT_GE(3, hash_counts[i]);
+    }
+}
+
+TEST_F(LabelSequenceTest, getHash) {
+    // Trivial case.  The same sequence should have the same hash.
+    EXPECT_EQ(ls1.getHash(true), ls1.getHash(true));
+
+    // Check the case-insensitive mode behavior.
+    EXPECT_EQ(ls1.getHash(false), ls5.getHash(false));
+
+    // Check that the distribution of hash values is "not too bad" (such as
+    // everything has the same hash value due to a stupid bug).  It's
+    // difficult to check such things reliably.  We do some ad hoc tests here.
+    hashDistributionCheck(root_servers);
+    hashDistributionCheck(jp_servers);
+    hashDistributionCheck(cn_servers);
+    hashDistributionCheck(ca_servers);
+}
+
+}
diff --git a/src/lib/dns/tests/masterload_unittest.cc b/src/lib/dns/tests/masterload_unittest.cc
index acb9d64..95ce6f3 100644
--- a/src/lib/dns/tests/masterload_unittest.cc
+++ b/src/lib/dns/tests/masterload_unittest.cc
@@ -25,6 +25,7 @@
 
 #include <dns/masterload.h>
 #include <dns/name.h>
+#include <dns/rdata.h>
 #include <dns/rrclass.h>
 #include <dns/rrset.h>
 
@@ -80,6 +81,11 @@ const char* const rrsig_rr2 =
     "www.example.com. 60 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 "
     "12345 example.com. FAKEFAKEFAKE\n";
 
+// Commonly used for some tests to check the constructed RR content.
+const char* const dnskey_rdata =
+    "256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+    "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=\n";
+
 TEST_F(MasterLoadTest, loadRRs) {
     // a simple case: loading 3 RRs, each consists of a single RRset.
     rr_stream << txt_rr << a_rr1 << soa_rr;
@@ -161,6 +167,98 @@ TEST_F(MasterLoadTest, loadRRsigs) {
     EXPECT_EQ(2, results.size());
 }
 
+TEST_F(MasterLoadTest, loadRRWithComment) {
+    // Comment at the end of line should be ignored and the RR should be
+    // accepted.
+    rr_stream << "example.com. 3600 IN DNSKEY	256 3 7 "
+        "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+        "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=  ; key id = 40430\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::DNSKEY(), zclass,
+                                      dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentNoSpace) {
+    // Similar to the previous one, but there's no space before comments.
+    // It should still work.
+    rr_stream << "example.com. 3600 IN DNSKEY	256 3 7 "
+        "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+        "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=; key id = 40430\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::DNSKEY(), zclass,
+                                      dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentEmptyComment) {
+    // Similar to the previous one, but there's no data after the ;
+    // It should still work.
+    rr_stream << "example.com. 3600 IN DNSKEY	256 3 7 "
+        "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+        "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE= ;\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::DNSKEY(), zclass,
+                                      dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithCommentEmptyCommentNoSpace) {
+    // Similar to the previous one, but there's no space before or after ;
+    // It should still work.
+    rr_stream << "example.com. 3600 IN DNSKEY	256 3 7 "
+        "AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH "
+        "zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=;\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::DNSKEY(), zclass,
+                                      dnskey_rdata)));
+}
+
+TEST_F(MasterLoadTest, loadRRWithEOLWhitespace) {
+    // Test with whitespace after rdata
+    // It should still work.
+    rr_stream << "example.com. 3600 IN NSEC3PARAM 1 0 1 beef \n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::NSEC3PARAM(), zclass,
+                                      "1 0 1 beef")));
+}
+
+TEST_F(MasterLoadTest, loadRRWithEOLWhitespaceTab) {
+    // Similar to the previous one, tab instead of space.
+    // It should still work.
+    rr_stream << "example.com. 3600 IN NSEC3PARAM 1 0 1 beef\t\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    ASSERT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::NSEC3PARAM(), zclass,
+                                      "1 0 1 beef")));
+}
+
+TEST_F(MasterLoadTest, loadRRNoComment) {
+    // A semicolon in a character-string shouldn't confuse the parser.
+    rr_stream << "example.com. 3600 IN TXT \"aaa;bbb\"\n";
+    masterLoad(rr_stream, origin, zclass, callback);
+    EXPECT_EQ(1, results.size());
+    EXPECT_EQ(0, results[0]->getRdataIterator()->getCurrent().compare(
+                  *rdata::createRdata(RRType::TXT(), zclass,
+                                      "\"aaa;bbb\"")));
+}
+
+TEST_F(MasterLoadTest, loadRREmptyAndComment) {
+    // There's no RDATA (invalid in this case) but a comment.  This position
+    // shouldn't cause any disruption and should be treated as a normal error.
+    rr_stream << "example.com. 3600 IN A ;\n";
+    EXPECT_THROW(masterLoad(rr_stream, origin, zclass, callback),
+                 MasterLoadError);
+}
+
 TEST_F(MasterLoadTest, loadWithNoEOF) {
     // the input stream doesn't end with a new line (and the following blank
     // line).  It should be accepted.
diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc
index c5bc7fd..3be1436 100644
--- a/src/lib/dns/tests/message_unittest.cc
+++ b/src/lib/dns/tests/message_unittest.cc
@@ -79,7 +79,6 @@ namespace {
 class MessageTest : public ::testing::Test {
 protected:
     MessageTest() : test_name("test.example.com"), obuffer(0),
-                    renderer(obuffer),
                     message_parse(Message::PARSE),
                     message_render(Message::RENDER),
                     bogus_section(static_cast<Message::Section>(
@@ -731,8 +730,8 @@ TEST_F(MessageTest, toWire) {
     message_render.toWire(renderer);
     vector<unsigned char> data;
     UnitTestUtil::readWireData("message_toWire1", data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
-                        obuffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(MessageTest, toWireInParseMode) {
@@ -961,9 +960,6 @@ TEST_F(MessageTest, toWireTSIGNoTruncation) {
 // rendering fail unexpectedly in the test that follows.
 class BadRenderer : public MessageRenderer {
 public:
-    BadRenderer(isc::util::OutputBuffer& buffer) :
-        MessageRenderer(buffer)
-    {}
     virtual void setLengthLimit(size_t len) {
         if (getLength() > 0) {
             MessageRenderer::setLengthLimit(getLength());
@@ -994,8 +990,7 @@ TEST_F(MessageTest, toWireTSIGLengthErrors) {
                  InvalidParameter);
 
     // Trying to render a message with TSIG using a buggy renderer.
-    obuffer.clear();
-    BadRenderer bad_renderer(obuffer);
+    BadRenderer bad_renderer;
     bad_renderer.setLengthLimit(512);
     message_render.clear(Message::RENDER);
     EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx,
diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc
index fe790fe..bc526af 100644
--- a/src/lib/dns/tests/messagerenderer_unittest.cc
+++ b/src/lib/dns/tests/messagerenderer_unittest.cc
@@ -12,8 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <vector>
-
+#include <exceptions/exceptions.h>
 #include <util/buffer.h>
 #include <dns/name.h>
 #include <dns/messagerenderer.h>
@@ -22,23 +21,27 @@
 
 #include <gtest/gtest.h>
 
+#include <boost/lexical_cast.hpp>
+
+#include <string>
+#include <vector>
+
 using isc::UnitTestUtil;
 using isc::dns::Name;
 using isc::dns::MessageRenderer;
 using isc::util::OutputBuffer;
+using boost::lexical_cast;
 
 namespace {
 class MessageRendererTest : public ::testing::Test {
 protected:
-    MessageRendererTest() : expected_size(0), buffer(0), renderer(buffer)
-    {
+    MessageRendererTest() : expected_size(0) {
         data16 = (2 << 8) | 3;
         data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
     }
     size_t expected_size;
     uint16_t data16;
     uint32_t data32;
-    OutputBuffer buffer;
     MessageRenderer renderer;
     std::vector<unsigned char> data;
     static const uint8_t testdata[5];
@@ -60,21 +63,22 @@ TEST_F(MessageRendererTest, writeName) {
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.example.com."));
     renderer.writeName(Name("a.example.org."));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(MessageRendererTest, writeNameInLargeBuffer) {
     size_t offset = 0x3fff;
-    buffer.skip(offset);
+    renderer.skip(offset);
 
     UnitTestUtil::readWireData("name_toWire2", data);
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.example.com."));
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t*>(buffer.getData()) + offset,
-                        buffer.getLength() - offset,
+                        static_cast<const uint8_t*>(renderer.getData()) +
+                        offset,
+                        renderer.getLength() - offset,
                         &data[0], data.size());
 }
 
@@ -83,8 +87,8 @@ TEST_F(MessageRendererTest, writeNameWithUncompressed) {
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.example.com."), false);
     renderer.writeName(Name("b.example.com."));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(MessageRendererTest, writeNamePointerChain) {
@@ -92,8 +96,8 @@ TEST_F(MessageRendererTest, writeNamePointerChain) {
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.example.com."));
     renderer.writeName(Name("b.example.com."));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(MessageRendererTest, compressMode) {
@@ -120,8 +124,8 @@ TEST_F(MessageRendererTest, writeNameCaseCompress) {
     // this should match the first name in terms of compression:
     renderer.writeName(Name("b.exAmple.CoM."));
     renderer.writeName(Name("a.example.org."));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
@@ -132,8 +136,8 @@ TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) {
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.eXample.com."));
     renderer.writeName(Name("c.eXample.com."));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
@@ -142,13 +146,15 @@ TEST_F(MessageRendererTest, writeNameMixedCaseCompress) {
     renderer.writeName(Name("a.example.com."));
     renderer.writeName(Name("b.eXample.com."));
 
-    // Change the compression mode in the middle of rendering.  This is an
-    // unusual operation and is unlikely to happen in practice, but is still
-    // allowed in this API.
-    renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE);
-    renderer.writeName(Name("c.b.EXAMPLE.com."));
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &data[0], data.size());
+    // Change the compression mode in the middle of rendering.  This is not
+    // allowed in this implementation.
+    EXPECT_THROW(renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE),
+                 isc::InvalidParameter);
+
+    // Once the renderer is cleared, it's okay again.
+    renderer.clear();
+    EXPECT_NO_THROW(renderer.setCompressMode(
+                        MessageRenderer::CASE_INSENSITIVE));
 }
 
 TEST_F(MessageRendererTest, writeRootName) {
@@ -164,9 +170,67 @@ TEST_F(MessageRendererTest, writeRootName) {
     renderer.writeName(Name("."));
     renderer.writeName(example_name);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t*>(buffer.getData()),
-                        buffer.getLength(),
+                        static_cast<const uint8_t*>(renderer.getData()),
+                        renderer.getLength(),
                         static_cast<const uint8_t*>(expected.getData()),
                         expected.getLength());
 }
+
+TEST_F(MessageRendererTest, setBuffer) {
+    OutputBuffer new_buffer(0);
+    renderer.setBuffer(&new_buffer);
+    EXPECT_EQ(0, new_buffer.getLength()); // the buffer should be still empty
+    renderer.writeUint32(42);
+    EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength());
+    EXPECT_EQ(sizeof(uint32_t), renderer.getLength());
+
+    // Change some other internal state for the reset test below.
+    EXPECT_EQ(512, renderer.getLengthLimit());
+    renderer.setLengthLimit(4096);
+    EXPECT_EQ(4096, renderer.getLengthLimit());
+
+    // Reset the buffer to the default again.  Other internal states and
+    // resources should be cleared.  The used buffer should be intact.
+    renderer.setBuffer(NULL);
+    EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength());
+    EXPECT_EQ(0, renderer.getLength());
+    EXPECT_EQ(512, renderer.getLengthLimit());
+}
+
+TEST_F(MessageRendererTest, setBufferErrors) {
+    OutputBuffer new_buffer(0);
+
+    // Buffer cannot be reset when the renderer is in use.
+    renderer.writeUint32(10);
+    EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter);
+
+    renderer.clear();
+    renderer.setBuffer(&new_buffer);
+    renderer.writeUint32(10);
+    EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter);
+
+    // Resetting the buffer isn't allowed for the default buffer.
+    renderer.setBuffer(NULL);
+    EXPECT_THROW(renderer.setBuffer(NULL), isc::InvalidParameter);
+
+    // It's okay to reset a temporary buffer without using it.
+    renderer.setBuffer(&new_buffer);
+    EXPECT_NO_THROW(renderer.setBuffer(NULL));
+}
+
+TEST_F(MessageRendererTest, manyRRs) {
+    // Render a large number of names, and the confirm the resulting wire
+    // data store the expected names in the correct order (1000 is an
+    // arbitrary choice).
+    for (size_t i = 0; i < 1000; ++i) {
+        renderer.writeName(Name(lexical_cast<std::string>(i) + ".example"));
+    }
+    isc::util::InputBuffer b(renderer.getData(), renderer.getLength());
+    for (size_t i = 0; i < 1000; ++i) {
+        EXPECT_EQ(Name(lexical_cast<std::string>(i) + ".example"), Name(b));
+    }
+    // This will trigger trimming excessive hash items.  It shouldn't cause
+    // any disruption.
+    EXPECT_NO_THROW(renderer.clear());
+}
 }
diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc
index 6434229..c327bdc 100644
--- a/src/lib/dns/tests/name_unittest.cc
+++ b/src/lib/dns/tests/name_unittest.cc
@@ -130,6 +130,15 @@ TEST_F(NameTest, nonlocalObject) {
     EXPECT_EQ("\\255.example.com.", downcased_global.toText());
 }
 
+template <typename ExceptionType>
+void
+checkBadTextName(const string& txt) {
+    // Check it results in the specified type of exception as well as
+    // NameParserException.
+    EXPECT_THROW(Name(txt, false), ExceptionType);
+    EXPECT_THROW(Name(txt, false), NameParserException);
+}
+
 TEST_F(NameTest, fromText) {
     vector<string> strnames;
     strnames.push_back("www.example.com");
@@ -151,45 +160,46 @@ TEST_F(NameTest, fromText) {
     EXPECT_EQ(Name("Www.eXample.coM", true).toText(), example_name.toText());
 
     //
-    // Tests for bogus names.  These should trigger an exception.
+    // Tests for bogus names.  These should trigger exceptions.
     //
     // empty label cannot be followed by another label
-    EXPECT_THROW(Name(".a"), EmptyLabel);
+    checkBadTextName<EmptyLabel>(".a");
     // duplicate period
-    EXPECT_THROW(Name("a.."), EmptyLabel);
+    checkBadTextName<EmptyLabel>("a..");
     // label length must be < 64
-    EXPECT_THROW(Name("012345678901234567890123456789"
-                      "012345678901234567890123456789"
-                      "0123"), TooLongLabel);
+    checkBadTextName<TooLongLabel>("012345678901234567890123456789"
+                                   "012345678901234567890123456789"
+                                   "0123");
     // now-unsupported bitstring labels
-    EXPECT_THROW(Name("\\[b11010000011101]"), BadLabelType);
+    checkBadTextName<BadLabelType>("\\[b11010000011101]");
     // label length must be < 64
-    EXPECT_THROW(Name("012345678901234567890123456789"
-                      "012345678901234567890123456789"
-                      "012\\x"), TooLongLabel);
+    checkBadTextName<TooLongLabel>("012345678901234567890123456789"
+                                   "012345678901234567890123456789"
+                                   "012\\x");
     // but okay as long as resulting len < 64 even if the original string is
     // "too long"
     EXPECT_NO_THROW(Name("012345678901234567890123456789"
                          "012345678901234567890123456789"
                          "01\\x"));
     // incomplete \DDD pattern (exactly 3 D's must appear)
-    EXPECT_THROW(Name("\\12abc"), BadEscape);
+    checkBadTextName<BadEscape>("\\12abc");
     // \DDD must not exceed 255
-    EXPECT_THROW(Name("\\256"), BadEscape);
+    checkBadTextName<BadEscape>("\\256");
     // Same tests for \111 as for \\x above
-    EXPECT_THROW(Name("012345678901234567890123456789"
-                      "012345678901234567890123456789"
-                      "012\\111"), TooLongLabel);
+    checkBadTextName<TooLongLabel>("012345678901234567890123456789"
+                                   "012345678901234567890123456789"
+                                   "012\\111");
     EXPECT_NO_THROW(Name("012345678901234567890123456789"
                          "012345678901234567890123456789"
                          "01\\111"));
     // A domain name must be 255 octets or less
-    EXPECT_THROW(Name("123456789.123456789.123456789.123456789.123456789."
-                      "123456789.123456789.123456789.123456789.123456789."
-                      "123456789.123456789.123456789.123456789.123456789."
-                      "123456789.123456789.123456789.123456789.123456789."
-                      "123456789.123456789.123456789.123456789.123456789."
-                      "1234"), TooLongName);
+    checkBadTextName<TooLongName>("123456789.123456789.123456789.123456789."
+                                  "123456789.123456789.123456789.123456789."
+                                  "123456789.123456789.123456789.123456789."
+                                  "123456789.123456789.123456789.123456789."
+                                  "123456789.123456789.123456789.123456789."
+                                  "123456789.123456789.123456789.123456789."
+                                  "123456789.1234");
     // This is a possible longest name and should be accepted
     EXPECT_NO_THROW(Name("123456789.123456789.123456789.123456789.123456789."
                          "123456789.123456789.123456789.123456789.123456789."
@@ -198,7 +208,7 @@ TEST_F(NameTest, fromText) {
                          "123456789.123456789.123456789.123456789.123456789."
                          "123"));
     // \DDD must consist of 3 digits.
-    EXPECT_THROW(Name("\\12"), IncompleteName);
+    checkBadTextName<IncompleteName>("\\12");
 
     // a name with the max number of labels.  should be constructed without
     // an error, and its length should be the max value.
@@ -357,13 +367,12 @@ TEST_F(NameTest, toWireBuffer) {
 //
 TEST_F(NameTest, toWireRenderer) {
     vector<unsigned char> data;
-    OutputBuffer buffer(0);
-    MessageRenderer renderer(buffer);
+    MessageRenderer renderer;
 
     UnitTestUtil::readWireData(string("01610376697803636f6d00"), data);
     Name("a.vix.com.").toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, &data[0], data.size(),
-                        buffer.getData(), buffer.getLength());
+                        renderer.getData(), renderer.getLength());
 }
 
 //
@@ -524,8 +533,8 @@ TEST_F(NameTest, at) {
 
     example_name.toWire(buffer_expected);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        &data[0], data.size(),
-                        buffer_expected.getData(), buffer_expected.getLength());
+                        &data[0], data.size(), buffer_expected.getData(),
+                        buffer_expected.getLength());
 
     // Out-of-range access: should trigger an exception.
     EXPECT_THROW(example_name.at(example_name.getLength()), OutOfRange);
@@ -543,7 +552,7 @@ TEST_F(NameTest, leq) {
 
     // small <= small is true
     EXPECT_TRUE(small_name.leq(small_name));
-    EXPECT_TRUE(small_name <= small_name);
+    EXPECT_LE(small_name, small_name);
 
     // large <= small is false
     EXPECT_FALSE(large_name.leq(small_name));
@@ -555,7 +564,7 @@ TEST_F(NameTest, geq) {
     EXPECT_TRUE(large_name >= small_name);
 
     EXPECT_TRUE(large_name.geq(large_name));
-    EXPECT_TRUE(large_name >= large_name);
+    EXPECT_GE(large_name, large_name);
 
     EXPECT_FALSE(small_name.geq(large_name));
     EXPECT_FALSE(small_name >= large_name);
@@ -566,6 +575,7 @@ TEST_F(NameTest, lthan) {
     EXPECT_TRUE(small_name < large_name);
 
     EXPECT_FALSE(small_name.lthan(small_name));
+    // cppcheck-suppress duplicateExpression
     EXPECT_FALSE(small_name < small_name);
 
     EXPECT_FALSE(large_name.lthan(small_name));
@@ -577,6 +587,7 @@ TEST_F(NameTest, gthan) {
     EXPECT_TRUE(large_name > small_name);
 
     EXPECT_FALSE(large_name.gthan(large_name));
+    // cppcheck-suppress duplicateExpression
     EXPECT_FALSE(large_name > large_name);
 
     EXPECT_FALSE(small_name.gthan(large_name));
diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc
index 1d483f2..54d0942 100644
--- a/src/lib/dns/tests/question_unittest.cc
+++ b/src/lib/dns/tests/question_unittest.cc
@@ -37,7 +37,7 @@ using namespace isc::util;
 namespace {
 class QuestionTest : public ::testing::Test {
 protected:
-    QuestionTest() : obuffer(0), renderer(obuffer),
+    QuestionTest() : obuffer(0),
                      example_name1(Name("foo.example.com")),
                      example_name2(Name("bar.example.com")),
                      test_question1(example_name1, RRClass::IN(),
@@ -102,8 +102,8 @@ TEST_F(QuestionTest, toWireRenderer) {
     test_question1.toWire(renderer);
     test_question2.toWire(renderer);
     UnitTestUtil::readWireData("question_toWire2", wiredata);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
-                        obuffer.getLength(), &wiredata[0], wiredata.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
 }
 
 TEST_F(QuestionTest, toWireTruncated) {
diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc
index 7df8d83..521bec5 100644
--- a/src/lib/dns/tests/rdata_afsdb_unittest.cc
+++ b/src/lib/dns/tests/rdata_afsdb_unittest.cc
@@ -162,7 +162,7 @@ TEST_F(Rdata_AFSDB_Test, toWireRenderer) {
     renderer.clear();
 
     // construct actual data
-    Name("example.com.").toWire(obuffer);
+    renderer.writeName(Name("example.com."));
     rdata_afsdb2.toWire(renderer);
 
     // construct expected data
diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc
index d6b02aa..2cce9bc 100644
--- a/src/lib/dns/tests/rdata_cname_unittest.cc
+++ b/src/lib/dns/tests/rdata_cname_unittest.cc
@@ -97,11 +97,11 @@ TEST_F(Rdata_CNAME_Test, toWireBuffer) {
 TEST_F(Rdata_CNAME_Test, toWireRenderer) {
     rdata_cname.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_cname, sizeof(wiredata_cname));
     rdata_cname2.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_cname2, sizeof(wiredata_cname2));
 }
 
diff --git a/src/lib/dns/tests/rdata_dhcid_unittest.cc b/src/lib/dns/tests/rdata_dhcid_unittest.cc
index 9df7043..38b1459 100644
--- a/src/lib/dns/tests/rdata_dhcid_unittest.cc
+++ b/src/lib/dns/tests/rdata_dhcid_unittest.cc
@@ -93,6 +93,7 @@ TEST_F(Rdata_DHCID_Test, getDHCIDDigest) {
 
 TEST_F(Rdata_DHCID_Test, compare) {
     // trivial case: self equivalence
+    // cppcheck-suppress uselessCallsCompare
     EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid));
 
     in::DHCID rdata_dhcid1("0YLQvtC/0L7Qu9GPINC00LLQsCDRgNGD0LHQu9GP");
diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc
index ebd9e0e..cf3001c 100644
--- a/src/lib/dns/tests/rdata_dname_unittest.cc
+++ b/src/lib/dns/tests/rdata_dname_unittest.cc
@@ -97,11 +97,11 @@ TEST_F(Rdata_DNAME_Test, toWireBuffer) {
 TEST_F(Rdata_DNAME_Test, toWireRenderer) {
     rdata_dname.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_dname, sizeof(wiredata_dname));
     rdata_dname2.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_dname2, sizeof(wiredata_dname2));
 }
 
diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc
index f0596ed..86b8f69 100644
--- a/src/lib/dns/tests/rdata_dnskey_unittest.cc
+++ b/src/lib/dns/tests/rdata_dnskey_unittest.cc
@@ -90,8 +90,8 @@ TEST_F(Rdata_DNSKEY_Test, toWireRenderer) {
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_dnskey_fromWire", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t *>(obuffer.getData()) + 2,
-                        obuffer.getLength() - 2, &data[2], data.size() - 2);
+                        static_cast<const uint8_t *>(renderer.getData()) + 2,
+                        renderer.getLength() - 2, &data[2], data.size() - 2);
 }
 
 TEST_F(Rdata_DNSKEY_Test, toWireBuffer) {
diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc
index 9b29446..6172431 100644
--- a/src/lib/dns/tests/rdata_ds_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_ds_like_unittest.cc
@@ -115,8 +115,8 @@ TYPED_TEST(Rdata_DS_LIKE_Test, toWireRenderer) {
     UnitTestUtil::readWireData("rdata_ds_fromWire", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
                         static_cast<const uint8_t*>
-                        (this->obuffer.getData()) + 2,
-                        this->obuffer.getLength() - 2,
+                        (this->renderer.getData()) + 2,
+                        this->renderer.getLength() - 2,
                         &data[2], data.size() - 2);
 }
 
diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc
index c52b2a0..c934a4f 100644
--- a/src/lib/dns/tests/rdata_hinfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_hinfo_unittest.cc
@@ -94,8 +94,9 @@ TEST_F(Rdata_HINFO_Test, toWireRenderer) {
     HINFO hinfo(hinfo_str);
 
     hinfo.toWire(renderer);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
-                        obuffer.getLength(), hinfo_rdata, sizeof(hinfo_rdata));
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), hinfo_rdata,
+                        sizeof(hinfo_rdata));
 }
 
 TEST_F(Rdata_HINFO_Test, compare) {
diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc
index 47e2bfa..2fea9a3 100644
--- a/src/lib/dns/tests/rdata_in_a_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_a_unittest.cc
@@ -78,7 +78,7 @@ TEST_F(Rdata_IN_A_Test, toWireBuffer) {
 TEST_F(Rdata_IN_A_Test, toWireRenderer) {
     rdata_in_a.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_in_a, sizeof(wiredata_in_a));
 }
 
@@ -95,6 +95,7 @@ TEST_F(Rdata_IN_A_Test, compare) {
     in::A large2("4.3.2.1");
 
     // trivial case: self equivalence
+    // cppcheck-suppress uselessCallsCompare
     EXPECT_EQ(0, small1.compare(small1));
 
     // confirm these are compared as unsigned values
diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
index 6fd4d0e..d8ed1d6 100644
--- a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
+++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc
@@ -76,7 +76,7 @@ TEST_F(Rdata_IN_AAAA_Test, toWireBuffer) {
 TEST_F(Rdata_IN_AAAA_Test, toWireRenderer) {
     rdata_in_aaaa.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_in_aaaa, sizeof(wiredata_in_aaaa));
 }
 
@@ -91,6 +91,7 @@ TEST_F(Rdata_IN_AAAA_Test, compare) {
     in::AAAA large2("8:7:6:5:4:3:2:1");
 
     // trivial case: self equivalence
+    // cppcheck-suppress uselessCallsCompare
     EXPECT_EQ(0, small1.compare(small1));
 
     // confirm these are compared as unsigned values
diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc
index 30c7c39..78e8325 100644
--- a/src/lib/dns/tests/rdata_minfo_unittest.cc
+++ b/src/lib/dns/tests/rdata_minfo_unittest.cc
@@ -142,15 +142,15 @@ TEST_F(Rdata_MINFO_Test, toWireRenderer) {
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_minfo_toWire1.wire", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t *>(obuffer.getData()),
-                        obuffer.getLength(), &data[0], data.size());
+                        static_cast<const uint8_t *>(renderer.getData()),
+                        renderer.getLength(), &data[0], data.size());
     renderer.clear();
     rdata_minfo2.toWire(renderer);
     vector<unsigned char> data2;
     UnitTestUtil::readWireData("rdata_minfo_toWire2.wire", data2);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t *>(obuffer.getData()),
-                        obuffer.getLength(), &data2[0], data2.size());
+                        static_cast<const uint8_t *>(renderer.getData()),
+                        renderer.getLength(), &data2[0], data2.size());
 }
 
 TEST_F(Rdata_MINFO_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc
index c4c9757..7dc774d 100644
--- a/src/lib/dns/tests/rdata_mx_unittest.cc
+++ b/src/lib/dns/tests/rdata_mx_unittest.cc
@@ -68,12 +68,12 @@ TEST_F(Rdata_MX_Test, toWireRenderer) {
 
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_mx_toWire1", data);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
-                        obuffer.getLength(), &data[0], data.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &data[0], data.size());
 }
 
 TEST_F(Rdata_MX_Test, toWireBuffer) {
-    renderer.writeName(Name("example.com"));
+    Name("example.com").toWire(obuffer);
     rdata_mx.toWire(obuffer);
 
     vector<unsigned char> data;
@@ -101,6 +101,7 @@ TEST_F(Rdata_MX_Test, compare) {
     generic::MX large2(256, Name("mx.example.com"));
 
     // trivial case: self equivalence
+    // cppcheck-suppress uselessCallsCompare
     EXPECT_EQ(0, small1.compare(small1));
 
     // confirm these are compared as unsigned values
diff --git a/src/lib/dns/tests/rdata_naptr_unittest.cc b/src/lib/dns/tests/rdata_naptr_unittest.cc
index f905943..5abcaef 100644
--- a/src/lib/dns/tests/rdata_naptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_naptr_unittest.cc
@@ -140,8 +140,9 @@ TEST_F(Rdata_NAPTR_Test, toWireRenderer) {
     NAPTR naptr(naptr_str);
 
     naptr.toWire(renderer);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, obuffer.getData(),
-                        obuffer.getLength(), naptr_rdata, sizeof(naptr_rdata));
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), naptr_rdata,
+                        sizeof(naptr_rdata));
 }
 
 TEST_F(Rdata_NAPTR_Test, toText) {
diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc
index b805783..47582ce 100644
--- a/src/lib/dns/tests/rdata_ns_unittest.cc
+++ b/src/lib/dns/tests/rdata_ns_unittest.cc
@@ -96,11 +96,11 @@ TEST_F(Rdata_NS_Test, toWireBuffer) {
 TEST_F(Rdata_NS_Test, toWireRenderer) {
     rdata_ns.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_ns, sizeof(wiredata_ns));
     rdata_ns2.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_ns2, sizeof(wiredata_ns2));
 }
 
diff --git a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
index 30110df..51fef01 100644
--- a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc
@@ -40,7 +40,7 @@ protected:
     NSEC3PARAMLikeTest() :
         salt_txt("1 1 1 D399EAAB" + getCommonText()),
         nosalt_txt("1 1 1 -" + getCommonText()),
-        obuffer(0), renderer(obuffer)
+        obuffer(0)
     {}
 
     RDATA_TYPE fromText(const string& rdata_text) {
diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
index 90313d0..7558b42 100644
--- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec3param_unittest.cc
@@ -94,8 +94,8 @@ TEST_F(Rdata_NSEC3PARAM_Test, toWireRenderer) {
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_nsec3param_fromWire1", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t *>(obuffer.getData()) + 2,
-                        obuffer.getLength() - 2, &data[2], data.size() - 2);
+                        static_cast<const uint8_t *>(renderer.getData()) + 2,
+                        renderer.getLength() - 2, &data[2], data.size() - 2);
 }
 
 TEST_F(Rdata_NSEC3PARAM_Test, toWireBuffer) {
diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc
index feb70c2..88c6201 100644
--- a/src/lib/dns/tests/rdata_nsec_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsec_unittest.cc
@@ -74,8 +74,8 @@ TEST_F(Rdata_NSEC_Test, toWireRenderer_NSEC) {
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_nsec_fromWire1", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t *>(obuffer.getData()) + 2,
-                        obuffer.getLength() - 2, &data[2], data.size() - 2);
+                        static_cast<const uint8_t *>(renderer.getData()) + 2,
+                        renderer.getLength() - 2, &data[2], data.size() - 2);
 }
 
 TEST_F(Rdata_NSEC_Test, toWireBuffer_NSEC) {
diff --git a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
index 426a69e..d7bce96 100644
--- a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
+++ b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc
@@ -264,7 +264,7 @@ TEST_F(NSEC3BitmapTest, emptyMap) {
 
     // Same for MessageRenderer.
     obuffer.clear();
-    MessageRenderer renderer(obuffer);
+    MessageRenderer renderer;
     renderer.writeUint16(rdlen);
     empty_nsec3.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc
index 7f5de20..86160fb 100644
--- a/src/lib/dns/tests/rdata_ptr_unittest.cc
+++ b/src/lib/dns/tests/rdata_ptr_unittest.cc
@@ -100,11 +100,11 @@ TEST_F(Rdata_PTR_Test, toWireBuffer) {
 TEST_F(Rdata_PTR_Test, toWireRenderer) {
     rdata_ptr.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_ptr, sizeof(wiredata_ptr));
     rdata_ptr2.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_ptr2, sizeof(wiredata_ptr2));
 }
 
diff --git a/src/lib/dns/tests/rdata_rp_unittest.cc b/src/lib/dns/tests/rdata_rp_unittest.cc
index 07f5a93..20f32b9 100644
--- a/src/lib/dns/tests/rdata_rp_unittest.cc
+++ b/src/lib/dns/tests/rdata_rp_unittest.cc
@@ -38,7 +38,7 @@ protected:
         // this also serves as a test for "from text" constructor in a normal
         // case.
         rdata_rp("root.example.com. rp-text.example.com."),
-        obuffer(0), renderer(obuffer)
+        obuffer(0)
     {}
 
     const Name mailbox_name, text_name;
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index 07c24d5..a9d782c 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -56,8 +56,8 @@ TEST_F(Rdata_SOA_Test, toWireRenderer) {
     vector<unsigned char> data;
     UnitTestUtil::readWireData("rdata_soa_fromWire", data);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        static_cast<const uint8_t *>(obuffer.getData()) + 2,
-                        obuffer.getLength() - 2, &data[2], data.size() - 2);
+                        static_cast<const uint8_t *>(renderer.getData()) + 2,
+                        renderer.getLength() - 2, &data[2], data.size() - 2);
 }
 
 TEST_F(Rdata_SOA_Test, toWireBuffer) {
diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc
index 3394f43..b194b1c 100644
--- a/src/lib/dns/tests/rdata_srv_unittest.cc
+++ b/src/lib/dns/tests/rdata_srv_unittest.cc
@@ -134,12 +134,12 @@ TEST_F(Rdata_SRV_Test, toWireBuffer) {
 TEST_F(Rdata_SRV_Test, toWireRenderer) {
     rdata_srv.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_srv, sizeof(wiredata_srv));
     renderer.clear();
     rdata_srv2.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_srv2, sizeof(wiredata_srv2));
 }
 
diff --git a/src/lib/dns/tests/rdata_sshfp_unittest.cc b/src/lib/dns/tests/rdata_sshfp_unittest.cc
new file mode 100644
index 0000000..dd133ce
--- /dev/null
+++ b/src/lib/dns/tests/rdata_sshfp_unittest.cc
@@ -0,0 +1,94 @@
+// 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 <algorithm>
+#include <string>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+#include <dns/tests/rdata_unittest.h>
+#include <boost/algorithm/string.hpp>
+
+using isc::UnitTestUtil;
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace {
+class Rdata_SSHFP_Test : public RdataTest {
+    // there's nothing to specialize
+};
+
+const string sshfp_txt("2 1 123456789abcdef67890123456789abcdef67890");
+const generic::SSHFP rdata_sshfp(2, 1, "123456789abcdef67890123456789abcdef67890");
+
+TEST_F(Rdata_SSHFP_Test, createFromText) {
+    // Basic test
+    const generic::SSHFP rdata_sshfp2(sshfp_txt);
+    EXPECT_EQ(0, rdata_sshfp2.compare(rdata_sshfp));
+
+    // With different spacing
+    const generic::SSHFP rdata_sshfp3("2 1   123456789abcdef67890123456789abcdef67890");
+    EXPECT_EQ(0, rdata_sshfp3.compare(rdata_sshfp));
+
+    // Combination of lowercase and uppercase
+    const generic::SSHFP rdata_sshfp4("2 1   123456789ABCDEF67890123456789abcdef67890");
+    EXPECT_EQ(0, rdata_sshfp4.compare(rdata_sshfp));
+}
+
+TEST_F(Rdata_SSHFP_Test, badText) {
+    EXPECT_THROW(const generic::SSHFP rdata_sshfp("1"), InvalidRdataText);
+    EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2"), InvalidRdataText);
+    EXPECT_THROW(const generic::SSHFP rdata_sshfp("BUCKLE MY SHOES"), InvalidRdataText);
+    EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2 foo bar"), InvalidRdataText);
+}
+
+TEST_F(Rdata_SSHFP_Test, copy) {
+    const generic::SSHFP rdata_sshfp2(rdata_sshfp);
+    EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2));
+}
+
+TEST_F(Rdata_SSHFP_Test, createFromWire) {
+    // Basic test
+    EXPECT_EQ(0, rdata_sshfp.compare(
+                  *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"),
+                                        "rdata_sshfp_fromWire")));
+    // Combination of lowercase and uppercase
+    EXPECT_EQ(0, rdata_sshfp.compare(
+                  *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"),
+                                        "rdata_sshfp_fromWire2")));
+    // TBD: more tests
+}
+
+TEST_F(Rdata_SSHFP_Test, toText) {
+    EXPECT_TRUE(boost::iequals(sshfp_txt, rdata_sshfp.toText()));
+}
+
+TEST_F(Rdata_SSHFP_Test, getSSHFPAlgorithmNumber) {
+    EXPECT_EQ(2, rdata_sshfp.getSSHFPAlgorithmNumber());
+}
+
+TEST_F(Rdata_SSHFP_Test, getSSHFPFingerprintType) {
+    EXPECT_EQ(1, rdata_sshfp.getSSHFPFingerprintType());
+}
+}
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index fa791dc..bf1f5f7 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -38,8 +38,7 @@ namespace isc {
 namespace dns {
 namespace rdata {
 RdataTest::RdataTest() :
-    obuffer(0), renderer(obuffer),
-    rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0"))
+    obuffer(0), rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0"))
 {}
 
 RdataPtr
@@ -245,12 +244,13 @@ TEST_F(Rdata_Unknown_Test, toWireBuffer) {
 TEST_F(Rdata_Unknown_Test, toWireRenderer) {
     rdata_unknown.toWire(renderer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata_unknown, sizeof(wiredata_unknown));
 }
 
 TEST_F(Rdata_Unknown_Test, compare) {
     // comparison as left-justified unsigned octet sequences:
+    // cppcheck-suppress uselessCallsCompare
     EXPECT_EQ(0, rdata_unknown.compare(rdata_unknown));
 
     generic::Generic rdata_unknown_small("\\# 4 00b2c3ff");
diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc
index d619220..ef83ed4 100644
--- a/src/lib/dns/tests/rdatafields_unittest.cc
+++ b/src/lib/dns/tests/rdatafields_unittest.cc
@@ -36,9 +36,7 @@ using isc::util::InputBuffer;
 namespace {
 class RdataFieldsTest : public ::testing::Test {
 protected:
-    RdataFieldsTest() : obuffer(0), renderer_buffer(0),
-                        renderer(renderer_buffer),
-                        ns_name("example.com"),
+    RdataFieldsTest() : obuffer(0), ns_name("example.com"),
                         other_name("www.example.com")
     {}
     void constructCommonTests(const RdataFields& fields,
@@ -49,7 +47,6 @@ protected:
     void constructCommonTestsRRSIG(const RdataFields& fields);
     void constructCommonTestsOPT(const RdataFields& fields);
     OutputBuffer obuffer;
-    OutputBuffer renderer_buffer;
     MessageRenderer renderer;
     const Name ns_name;
     const Name other_name;
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index 15f9a8c..6156be5 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -28,7 +28,7 @@ using namespace isc::util;
 namespace {
 class RRClassTest : public ::testing::Test {
 protected:
-    RRClassTest() : obuffer(0), renderer(obuffer) {}
+    RRClassTest() : obuffer(0) {}
 
     OutputBuffer obuffer;
     MessageRenderer renderer;
@@ -116,7 +116,7 @@ TEST_F(RRClassTest, toWireRenderer) {
     rrclass_max.toWire(renderer);
 
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata, sizeof(wiredata));
 }
 
diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc
index f951341..1603293 100644
--- a/src/lib/dns/tests/rrset_unittest.cc
+++ b/src/lib/dns/tests/rrset_unittest.cc
@@ -12,8 +12,6 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#include <stdexcept>
-
 #include <util/buffer.h>
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
@@ -24,9 +22,12 @@
 #include <dns/rrttl.h>
 #include <dns/rrset.h>
 
+#include <dns/tests/unittest_util.h>
+
 #include <gtest/gtest.h>
 
-#include <dns/tests/unittest_util.h>
+#include <stdexcept>
+#include <sstream>
 
 using isc::UnitTestUtil;
 
@@ -38,7 +39,7 @@ using namespace isc::dns::rdata;
 namespace {
 class RRsetTest : public ::testing::Test {
 protected:
-    RRsetTest() : buffer(0), renderer(buffer),
+    RRsetTest() : buffer(0),
                   test_name("test.example.com"),
                   test_domain("example.com"),
                   test_nsname("ns.example.com"),
@@ -112,6 +113,20 @@ TEST_F(RRsetTest, setName) {
     EXPECT_EQ(test_nsname, rrset_a.getName());
 }
 
+TEST_F(RRsetTest, isSameKind) {
+    RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
+    RRset rrset_x(test_name, RRClass::IN(), RRType::A(), RRTTL(3600));
+    RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600));
+    RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600));
+    RRset rrset_p(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600));
+
+    EXPECT_TRUE(rrset_w.isSameKind(rrset_w));
+    EXPECT_TRUE(rrset_w.isSameKind(rrset_x));
+    EXPECT_FALSE(rrset_w.isSameKind(rrset_y));
+    EXPECT_FALSE(rrset_w.isSameKind(rrset_z));
+    EXPECT_FALSE(rrset_w.isSameKind(rrset_p));
+}
+
 void
 addRdataTestCommon(const RRset& rrset) {
     EXPECT_EQ(2, rrset.getRdataCount());
@@ -201,8 +216,8 @@ TEST_F(RRsetTest, toWireRenderer) {
     rrset_ns.toWire(renderer);
 
     UnitTestUtil::readWireData("rrset_toWire2", wiredata);
-    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, buffer.getData(),
-                        buffer.getLength(), &wiredata[0], wiredata.size());
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, renderer.getData(),
+                        renderer.getLength(), &wiredata[0], wiredata.size());
 
     // toWire() cannot be performed for an empty RRset.
     renderer.clear();
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index fe75eb6..0e3ab44 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -28,7 +28,7 @@ using namespace isc::util;
 namespace {
 class RRTTLTest : public ::testing::Test {
 protected:
-    RRTTLTest() : obuffer(0), renderer(obuffer) {}       
+    RRTTLTest() : obuffer(0) {}
 
     OutputBuffer obuffer;
     MessageRenderer renderer;
@@ -114,7 +114,7 @@ TEST_F(RRTTLTest, toWireRenderer) {
     ttl_max.toWire(renderer);
 
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata, sizeof(wiredata));
 }
 
@@ -138,7 +138,7 @@ TEST_F(RRTTLTest, leq) {
 
     // small <= small is true
     EXPECT_TRUE(ttl_small.leq(ttl_small));
-    EXPECT_TRUE(ttl_small <= ttl_small);
+    EXPECT_LE(ttl_small, ttl_small);
 
     // large <= small is false
     EXPECT_FALSE(ttl_large.leq(ttl_small));
@@ -150,7 +150,7 @@ TEST_F(RRTTLTest, geq) {
     EXPECT_TRUE(ttl_large >= ttl_small);
 
     EXPECT_TRUE(ttl_large.geq(ttl_large));
-    EXPECT_TRUE(ttl_large >= ttl_large);
+    EXPECT_GE(ttl_large, ttl_large);
 
     EXPECT_FALSE(ttl_small.geq(ttl_large));
     EXPECT_FALSE(ttl_small >= ttl_large);
@@ -161,6 +161,7 @@ TEST_F(RRTTLTest, lthan) {
     EXPECT_TRUE(ttl_small < ttl_large);
 
     EXPECT_FALSE(ttl_small.lthan(ttl_small));
+    // cppcheck-suppress duplicateExpression
     EXPECT_FALSE(ttl_small < ttl_small);
 
     EXPECT_FALSE(ttl_large.lthan(ttl_small));
@@ -172,6 +173,7 @@ TEST_F(RRTTLTest, gthan) {
     EXPECT_TRUE(ttl_large > ttl_small);
 
     EXPECT_FALSE(ttl_large.gthan(ttl_large));
+    // cppcheck-suppress duplicateExpression
     EXPECT_FALSE(ttl_large > ttl_large);
 
     EXPECT_FALSE(ttl_small.gthan(ttl_large));
diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc
index 12f6001..28ecee6 100644
--- a/src/lib/dns/tests/rrtype_unittest.cc
+++ b/src/lib/dns/tests/rrtype_unittest.cc
@@ -28,7 +28,7 @@ using namespace isc::util;
 namespace {
 class RRTypeTest : public ::testing::Test {
 protected:
-    RRTypeTest() : obuffer(0), renderer(obuffer) {}
+    RRTypeTest() : obuffer(0) {}
 
     OutputBuffer obuffer;
     MessageRenderer renderer;
@@ -120,7 +120,7 @@ TEST_F(RRTypeTest, toWireRenderer) {
     rrtype_max.toWire(renderer);
 
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        obuffer.getData(), obuffer.getLength(),
+                        renderer.getData(), renderer.getLength(),
                         wiredata, sizeof(wiredata));
 }
 
diff --git a/src/lib/dns/tests/testdata/.gitignore b/src/lib/dns/tests/testdata/.gitignore
new file mode 100644
index 0000000..e56355b
--- /dev/null
+++ b/src/lib/dns/tests/testdata/.gitignore
@@ -0,0 +1,117 @@
+/edns_toWire1.wire
+/edns_toWire2.wire
+/edns_toWire3.wire
+/edns_toWire4.wire
+/message_fromWire10.wire
+/message_fromWire11.wire
+/message_fromWire12.wire
+/message_fromWire13.wire
+/message_fromWire14.wire
+/message_fromWire15.wire
+/message_fromWire16.wire
+/message_fromWire17.wire
+/message_fromWire18.wire
+/message_fromWire19.wire
+/message_fromWire20.wire
+/message_fromWire21.wire
+/message_fromWire22.wire
+/message_toText1.wire
+/message_toText2.wire
+/message_toText3.wire
+/message_toWire2.wire
+/message_toWire3.wire
+/message_toWire4.wire
+/message_toWire5.wire
+/name_toWire5.wire
+/name_toWire6.wire
+/rdata_afsdb_fromWire1.wire
+/rdata_afsdb_fromWire2.wire
+/rdata_afsdb_fromWire3.wire
+/rdata_afsdb_fromWire4.wire
+/rdata_afsdb_fromWire5.wire
+/rdata_afsdb_toWire1.wire
+/rdata_afsdb_toWire2.wire
+/rdata_minfo_fromWire1.wire
+/rdata_minfo_fromWire2.wire
+/rdata_minfo_fromWire3.wire
+/rdata_minfo_fromWire4.wire
+/rdata_minfo_fromWire5.wire
+/rdata_minfo_fromWire6.wire
+/rdata_minfo_toWire1.wire
+/rdata_minfo_toWire2.wire
+/rdata_minfo_toWireUncompressed1.wire
+/rdata_minfo_toWireUncompressed2.wire
+/rdata_nsec3_fromWire10.wire
+/rdata_nsec3_fromWire11.wire
+/rdata_nsec3_fromWire12.wire
+/rdata_nsec3_fromWire13.wire
+/rdata_nsec3_fromWire14.wire
+/rdata_nsec3_fromWire15.wire
+/rdata_nsec3_fromWire16.wire
+/rdata_nsec3_fromWire17.wire
+/rdata_nsec3_fromWire2.wire
+/rdata_nsec3_fromWire4.wire
+/rdata_nsec3_fromWire5.wire
+/rdata_nsec3_fromWire6.wire
+/rdata_nsec3_fromWire7.wire
+/rdata_nsec3_fromWire8.wire
+/rdata_nsec3_fromWire9.wire
+/rdata_nsec3param_fromWire11.wire
+/rdata_nsec3param_fromWire13.wire
+/rdata_nsec3param_fromWire2.wire
+/rdata_nsec_fromWire10.wire
+/rdata_nsec_fromWire16.wire
+/rdata_nsec_fromWire4.wire
+/rdata_nsec_fromWire5.wire
+/rdata_nsec_fromWire6.wire
+/rdata_nsec_fromWire7.wire
+/rdata_nsec_fromWire8.wire
+/rdata_nsec_fromWire9.wire
+/rdata_rp_fromWire1.wire
+/rdata_rp_fromWire2.wire
+/rdata_rp_fromWire3.wire
+/rdata_rp_fromWire4.wire
+/rdata_rp_fromWire5.wire
+/rdata_rp_fromWire6.wire
+/rdata_rp_toWire1.wire
+/rdata_rp_toWire2.wire
+/rdata_rrsig_fromWire2.wire
+/rdata_soa_toWireUncompressed.wire
+/rdata_sshfp_fromWire1.wire
+/rdata_sshfp_fromWire2.wire
+/rdata_tsig_fromWire1.wire
+/rdata_tsig_fromWire2.wire
+/rdata_tsig_fromWire3.wire
+/rdata_tsig_fromWire4.wire
+/rdata_tsig_fromWire5.wire
+/rdata_tsig_fromWire6.wire
+/rdata_tsig_fromWire7.wire
+/rdata_tsig_fromWire8.wire
+/rdata_tsig_fromWire9.wire
+/rdata_tsig_toWire1.wire
+/rdata_tsig_toWire2.wire
+/rdata_tsig_toWire3.wire
+/rdata_tsig_toWire4.wire
+/rdata_tsig_toWire5.wire
+/rdata_txt_fromWire2.wire
+/rdata_txt_fromWire3.wire
+/rdata_txt_fromWire4.wire
+/rdata_txt_fromWire5.wire
+/rdatafields1.wire
+/rdatafields2.wire
+/rdatafields3.wire
+/rdatafields4.wire
+/rdatafields5.wire
+/rdatafields6.wire
+/tsig_verify1.wire
+/tsig_verify10.wire
+/tsig_verify2.wire
+/tsig_verify3.wire
+/tsig_verify4.wire
+/tsig_verify5.wire
+/tsig_verify6.wire
+/tsig_verify7.wire
+/tsig_verify8.wire
+/tsig_verify9.wire
+/tsigrecord_toWire1.wire
+/tsigrecord_toWire2.wire
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index fdf1025..f450a59 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -44,6 +44,7 @@ BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire
 BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire
 BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire
 BUILT_SOURCES += rdata_rp_toWire1.wire rdata_rp_toWire2.wire
+BUILT_SOURCES += rdata_sshfp_fromWire1.wire rdata_sshfp_fromWire2.wire
 BUILT_SOURCES += rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire
 BUILT_SOURCES += rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire
 BUILT_SOURCES += rdata_afsdb_fromWire5.wire
@@ -125,6 +126,8 @@ EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec
 EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec
 EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec
 EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec
+EXTRA_DIST += rdata_sshfp_fromWire rdata_sshfp_fromWire2
+EXTRA_DIST += rdata_sshfp_fromWire1.spec rdata_sshfp_fromWire2.spec
 EXTRA_DIST += rdata_afsdb_fromWire1.spec rdata_afsdb_fromWire2.spec
 EXTRA_DIST += rdata_afsdb_fromWire3.spec rdata_afsdb_fromWire4.spec
 EXTRA_DIST += rdata_afsdb_fromWire5.spec
diff --git a/src/lib/dns/tests/testdata/rdata_mx_fromWire b/src/lib/dns/tests/testdata/rdata_mx_fromWire
index f56387d..407350f 100644
--- a/src/lib/dns/tests/testdata/rdata_mx_fromWire
+++ b/src/lib/dns/tests/testdata/rdata_mx_fromWire
@@ -11,5 +11,5 @@
   00 0a
 # EXCHANGE: non compressed
 # 4  5  6  7  8  9 10  1  2  3  4  5  6  7  8  9 (bytes)
-#(4) m  x (7) e  x  a  m  p  l  e (3) c  o  m  .
+#(2) m  x (7) e  x  a  m  p  l  e (3) c  o  m  .
  02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire
new file mode 100644
index 0000000..added40
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire
@@ -0,0 +1,4 @@
+# SSHFP RDATA, RDLEN=22
+0016
+# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890
+02 01 123456789abcdef67890123456789abcdef67890
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec
new file mode 100644
index 0000000..e28a62f
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec
@@ -0,0 +1,6 @@
+#
+# A simplest form of SSHFP: all default parameters
+#
+[custom]
+sections: sshfp
+[sshfp]
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2
new file mode 100644
index 0000000..a695548
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2
@@ -0,0 +1,4 @@
+# SSHFP RDATA, RDLEN=22
+0016
+# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789ABCDEF67890123456789abcdef67890
+02 01 123456789ABCDEF67890123456789abcdef67890
diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec
new file mode 100644
index 0000000..59a336e
--- /dev/null
+++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec
@@ -0,0 +1,7 @@
+#
+# SSHFP RDATA
+#
+[custom]
+sections: sshfp
+[sshfp]
+fingerprint: 123456789abcdef67890123456789abcdef67890
diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc
index 7944b29..ac503e5 100644
--- a/src/lib/dns/tests/tsig_unittest.cc
+++ b/src/lib/dns/tests/tsig_unittest.cc
@@ -71,7 +71,7 @@ protected:
     TSIGTest() :
         tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"),
         badkey_name("badkey.example.com"), test_class(RRClass::IN()),
-        test_ttl(86400), message(Message::RENDER), buffer(0), renderer(buffer),
+        test_ttl(86400), message(Message::RENDER),
         dummy_data(1024, 0xdd),  // should be sufficiently large for all tests
         dummy_record(badkey_name, any::TSIG(TSIGKey::HMACMD5_NAME(),
                                             0x4da8877a,
@@ -125,7 +125,6 @@ protected:
     const RRClass test_class;
     const RRTTL test_ttl;
     Message message;
-    OutputBuffer buffer;
     MessageRenderer renderer;
     vector<uint8_t> secret;
     vector<uint8_t> dummy_data;
diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc
index e1b3e93..532681a 100644
--- a/src/lib/dns/tests/tsigrecord_unittest.cc
+++ b/src/lib/dns/tests/tsigrecord_unittest.cc
@@ -46,7 +46,7 @@ protected:
                              test_mac.size(), &test_mac[0],
                              0x2d65, 0, 0, NULL)),
         test_record(test_name, test_rdata),
-        buffer(0), renderer(buffer)
+        buffer(0)
     {}
     const Name test_name;
     vector<unsigned char> test_mac;
diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc
index ca9d347..66d49a8 100644
--- a/src/lib/dns/tests/unittest_util.cc
+++ b/src/lib/dns/tests/unittest_util.cc
@@ -191,3 +191,21 @@ UnitTestUtil::createRequestMessage(Message& message,
     message.addQuestion(Question(name, rrclass, rrtype));
 }
 
+void
+UnitTestUtil::createDNSSECRequestMessage(Message& message,
+                                         const Opcode& opcode,
+                                         const uint16_t qid,
+                                         const Name& name,
+                                         const RRClass& rrclass,
+                                         const RRType& rrtype)
+{
+    message.clear(Message::RENDER);
+    message.setOpcode(opcode);
+    message.setRcode(Rcode::NOERROR());
+    message.setQid(qid);
+    message.addQuestion(Question(name, rrclass, rrtype));
+    EDNSPtr edns(new EDNS());
+    edns->setUDPSize(4096);
+    edns->setDNSSECAwareness(true);
+    message.setEDNS(edns);
+}
diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h
index f85a921..ebb514d 100644
--- a/src/lib/dns/tests/unittest_util.h
+++ b/src/lib/dns/tests/unittest_util.h
@@ -93,6 +93,22 @@ public:
                          const isc::dns::Name& name,
                          const isc::dns::RRClass& rrclass,
                          const isc::dns::RRType& rrtype);
+
+    ///
+    /// Populate a DNSSEC request message
+    ///
+    /// Create a request message in 'request_message' using the
+    /// opcode 'opcode' and the name/class/type query tuple specified in
+    /// 'name', 'rrclass' and 'rrtype.
+    /// EDNS will be added with DO=1 and bufsize 4096
+    static void
+    createDNSSECRequestMessage(isc::dns::Message& request_message,
+                               const isc::dns::Opcode& opcode,
+                               const uint16_t qid,
+                               const isc::dns::Name& name,
+                               const isc::dns::RRClass& rrclass,
+                               const isc::dns::RRType& rrtype);
+
 };
 }
 #endif // __UNITTEST_UTIL_H
diff --git a/src/lib/exceptions/Makefile.am b/src/lib/exceptions/Makefile.am
index eff83b2..1d0ce2d 100644
--- a/src/lib/exceptions/Makefile.am
+++ b/src/lib/exceptions/Makefile.am
@@ -8,5 +8,5 @@ libexceptions_la_SOURCES = exceptions.h exceptions.cc
 
 CLEANFILES = *.gcno *.gcda
 
-libexceptions_includedir = $(includedir)/exceptions
+libexceptions_includedir = $(includedir)/$(PACKAGE_NAME)/exceptions
 libexceptions_include_HEADERS = exceptions.h
diff --git a/src/lib/exceptions/exceptions.h b/src/lib/exceptions/exceptions.h
index b68f3c4..010fd39 100644
--- a/src/lib/exceptions/exceptions.h
+++ b/src/lib/exceptions/exceptions.h
@@ -197,6 +197,38 @@ public:
         throw type(__FILE__, __LINE__, oss__.str().c_str(), param1); \
     } while (1)
 
+///
+/// Similar as isc_throw, but allows the exception to have two additional
+/// parameters (the stream/text goes first)
+#define isc_throw_2(type, stream, param1, param2) \
+    do { \
+        std::ostringstream oss__; \
+        oss__ << stream; \
+        throw type(__FILE__, __LINE__, oss__.str().c_str(), param1, param2); \
+    } while (1)
+
+///
+/// Similar as isc_throw, but allows the exception to have three additional
+/// parameters (the stream/text goes first)
+#define isc_throw_3(type, stream, param1, param2, param3) \
+    do { \
+        std::ostringstream oss__; \
+        oss__ << stream; \
+        throw type(__FILE__, __LINE__, oss__.str().c_str(), param1, param2,\
+                   param3); \
+    } while (1)
+
+///
+/// Similar as isc_throw, but allows the exception to have four additional
+/// parameters (the stream/text goes first)
+#define isc_throw_4(type, stream, param1, param2, param3, param4) \
+    do { \
+        std::ostringstream oss__; \
+        oss__ << stream; \
+        throw type(__FILE__, __LINE__, oss__.str().c_str(), param1, param2,\
+                   param3, param4); \
+    } while (1)
+
 }
 #endif // __EXCEPTIONS_H
 
diff --git a/src/lib/exceptions/tests/.gitignore b/src/lib/exceptions/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/exceptions/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/log/compiler/.gitignore b/src/lib/log/compiler/.gitignore
new file mode 100644
index 0000000..b6afc24
--- /dev/null
+++ b/src/lib/log/compiler/.gitignore
@@ -0,0 +1 @@
+/message
diff --git a/src/lib/log/compiler/Makefile.am b/src/lib/log/compiler/Makefile.am
index 1f47ba9..84cd4f6 100644
--- a/src/lib/log/compiler/Makefile.am
+++ b/src/lib/log/compiler/Makefile.am
@@ -16,3 +16,4 @@ noinst_PROGRAMS = message
 message_SOURCES = message.cc
 message_LDADD   = $(top_builddir)/src/lib/log/liblog.la
 message_LDADD  += $(top_builddir)/src/lib/util/libutil.la
+message_LDADD  += $(top_builddir)/src/lib/exceptions/libexceptions.la
diff --git a/src/lib/log/compiler/message.cc b/src/lib/log/compiler/message.cc
index 3c8a3f2..66dc9c7 100644
--- a/src/lib/log/compiler/message.cc
+++ b/src/lib/log/compiler/message.cc
@@ -25,6 +25,8 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <exceptions/exceptions.h>
+
 #include <util/filename.h>
 #include <util/strutil.h>
 
@@ -245,7 +247,7 @@ writeClosingNamespace(ostream& output, const vector<string>& ns) {
     }
 }
 
-/// \breif Write python file
+/// \brief Write python file
 ///
 /// Writes the python file containing the symbol definitions as module level
 /// constants. These are objects which register themself at creation time,
@@ -301,8 +303,8 @@ writePythonFile(const string& file, MessageDictionary& dictionary,
 ///
 /// \param file Name of the message file.  The header file is written to a
 /// file of the same name but with a .h suffix.
-/// \param ns Namespace in which the definitions are to be placed.  An empty
-/// string indicates no namespace.
+/// \param ns_components Namespace in which the definitions are to be placed.
+/// An empty string indicates no namespace.
 /// \param dictionary Dictionary holding the message definitions.
 /// \param output_directory if not null NULL, output files are written
 ///     to the given directory. If NULL, they are written to the current
@@ -325,8 +327,9 @@ writeHeaderFile(const string& file, const vector<string>& ns_components,
     ofstream hfile(header_file.fullName().c_str());
 
     if (hfile.fail()) {
-        throw MessageException(LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
-            strerror(errno));
+        isc_throw_4(MessageException, "Failed to open output file",
+                    LOG_OPEN_OUTPUT_FAIL, header_file.fullName(),
+                    strerror(errno), 0);
     }
 
     // Write the header preamble.  If there is an error, we'll pick it up
@@ -359,8 +362,9 @@ writeHeaderFile(const string& file, const vector<string>& ns_components,
 
     // Report errors (if any) and exit
     if (hfile.fail()) {
-        throw MessageException(LOG_WRITE_ERROR, header_file.fullName(),
-            strerror(errno));
+        isc_throw_4(MessageException, "Error writing to output file",
+                    LOG_WRITE_ERROR, header_file.fullName(), strerror(errno),
+                    0);
     }
 
     hfile.close();
@@ -404,8 +408,8 @@ replaceNonAlphaNum(char c) {
 ///
 /// \param file Name of the message file.  The header file is written to a
 /// file of the same name but with a .h suffix.
-/// \param ns Namespace in which the definitions are to be placed.  An empty
-/// string indicates no namespace.
+/// \param ns_components Namespace in which the definitions are to be placed.
+/// An empty string indicates no namespace.
 /// \param dictionary Dictionary holding the message definitions.
 /// \param output_directory if not null NULL, output files are written
 ///     to the given directory. If NULL, they are written to the current
@@ -425,8 +429,9 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
     ofstream ccfile(program_file.fullName().c_str());
 
     if (ccfile.fail()) {
-        throw MessageException(LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
-            strerror(errno));
+        isc_throw_4(MessageException, "Error opening output file",
+                    LOG_OPEN_OUTPUT_FAIL, program_file.fullName(),
+                    strerror(errno), 0);
     }
 
     // Write the preamble.  If there is an error, we'll pick it up after
@@ -483,30 +488,31 @@ writeProgramFile(const string& file, const vector<string>& ns_components,
 
     // Report errors (if any) and exit
     if (ccfile.fail()) {
-        throw MessageException(LOG_WRITE_ERROR, program_file.fullName(),
-            strerror(errno));
+        isc_throw_4(MessageException, "Error writing to output file",
+                    LOG_WRITE_ERROR, program_file.fullName(), strerror(errno),
+                    0);
     }
 
     ccfile.close();
 }
 
 
-/// \brief Warn of Duplicate Entries
+/// \brief Error and exit if there are duplicate entries
 ///
-/// If the input file contained duplicate message IDs, only the first will be
-/// processed.  However, we should warn about it.
+/// If the input file contained duplicate message IDs, we print an
+/// error for each of them, then exit the program with a non-0 value.
 ///
 /// \param reader Message Reader used to read the file
 
 void
-warnDuplicates(MessageReader& reader) {
+errorDuplicates(MessageReader& reader) {
 
     // Get the duplicates (the overflow) and, if present, sort them into some
     // order and remove those which occur more than once (which mean that they
     // occur more than twice in the input file).
     MessageReader::MessageIDCollection duplicates = reader.getNotAdded();
-    if (duplicates.size() > 0) {
-        cout << "Warning: the following duplicate IDs were found:\n";
+    if (!duplicates.empty()) {
+        cout << "Error: the following duplicate IDs were found:\n";
 
         sort(duplicates.begin(), duplicates.end());
         MessageReader::MessageIDCollection::iterator new_end =
@@ -515,6 +521,7 @@ warnDuplicates(MessageReader& reader) {
             i != new_end; ++i) {
             cout << "    " << *i << "\n";
         }
+        exit(1);
     }
 }
 
@@ -580,6 +587,9 @@ main(int argc, char* argv[]) {
         MessageReader reader(&dictionary);
         reader.readFile(message_file);
 
+        // Error (and quit) if there are of any duplicates encountered.
+        errorDuplicates(reader);
+
         if (doPython) {
             // Warn in case of ignored directives
             if (!reader.getNamespace().empty()) {
@@ -604,8 +614,6 @@ main(int argc, char* argv[]) {
                              output_directory);
         }
 
-        // Finally, warn of any duplicates encountered.
-        warnDuplicates(reader);
     }
     catch (const MessageException& e) {
         // Create an error message from the ID and the text
diff --git a/src/lib/log/log_formatter.cc b/src/lib/log/log_formatter.cc
index 18c4741..c728cb5 100644
--- a/src/lib/log/log_formatter.cc
+++ b/src/lib/log/log_formatter.cc
@@ -12,8 +12,11 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "config.h"
 #include <log/log_formatter.h>
 
+#include <cassert>
+
 using namespace std;
 using namespace boost;
 
@@ -24,17 +27,42 @@ void
 replacePlaceholder(string* message, const string& arg,
                    const unsigned placeholder)
 {
-    string mark("%" + lexical_cast<string>(placeholder));
+    const string mark("%" + lexical_cast<string>(placeholder));
     size_t pos(message->find(mark));
     if (pos != string::npos) {
         do {
             message->replace(pos, mark.size(), arg);
             pos = message->find(mark, pos + arg.size());
         } while (pos != string::npos);
-    } else {
+    }
+#ifdef ENABLE_LOGGER_CHECKS
+    else {
+        // We're missing the placeholder, so throw an exception
+        isc_throw(MismatchedPlaceholders,
+		  "Missing logger placeholder in message: " << *message);
+    }
+#else
+    else {
         // We're missing the placeholder, so add some complain
-        message->append(" @@Missing placeholder " + mark + " for '" + arg +
-                        "'@@");
+        message->append(" @@Missing placeholder " + mark + " for '" + arg + "'@@");
+    }
+#endif /* ENABLE_LOGGER_CHECKS */
+}
+
+void
+checkExcessPlaceholders(string* message, unsigned int placeholder) {
+    const string mark("%" + lexical_cast<string>(placeholder));
+    const size_t pos(message->find(mark));
+    if (pos != string::npos) {
+        // Excess placeholders were found.  If we enable the harsh check,
+        // abort it.  Note: ideally we'd like to throw MismatchedPlaceholders,
+        // but we can't at least for now because this function is called from
+        // the Formatter's destructor.
+#ifdef ENABLE_LOGGER_CHECKS
+        assert("Excess logger placeholders still exist in message" == NULL);
+#else
+        message->append(" @@Excess logger placeholders still exist@@");
+#endif /* ENABLE_LOGGER_CHECKS */
     }
 }
 
diff --git a/src/lib/log/log_formatter.h b/src/lib/log/log_formatter.h
index 7a9e5fa..fc60203 100644
--- a/src/lib/log/log_formatter.h
+++ b/src/lib/log/log_formatter.h
@@ -39,6 +39,27 @@ public:
 };
 
 
+/// \brief Mismatched Placeholders
+///
+/// This exception is used when the number of placeholders do not match
+/// the number of arguments passed to the formatter.
+
+class MismatchedPlaceholders : public isc::Exception {
+public:
+    MismatchedPlaceholders(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what)
+    {}
+};
+
+
+///
+/// \brief Internal excess placeholder checker
+///
+/// This is used internally by the Formatter to check for excess
+/// placeholders (and fewer arguments).
+void
+checkExcessPlaceholders(std::string* message, unsigned int placeholder);
+
 ///
 /// \brief The internal replacement routine
 ///
@@ -142,6 +163,7 @@ public:
     /// This is the place where output happens if the formatter is active.
     ~ Formatter() {
         if (logger_) {
+            checkExcessPlaceholders(message_, ++nextPlaceholder_);
             logger_->output(severity_, *message_);
             delete message_;
         }
diff --git a/src/lib/log/logger.h b/src/lib/log/logger.h
index 96168c0..5715bc4 100644
--- a/src/lib/log/logger.h
+++ b/src/lib/log/logger.h
@@ -15,14 +15,17 @@
 #ifndef __LOGGER_H
 #define __LOGGER_H
 
+#include <cassert>
 #include <cstdlib>
 #include <string>
+#include <cstring>
 
 #include <exceptions/exceptions.h>
 #include <log/logger_level.h>
 #include <log/message_types.h>
 #include <log/log_formatter.h>
 
+
 namespace isc {
 namespace log {
 
@@ -68,9 +71,19 @@ namespace log {
 /// is referred to using a symbolic name.  The logging code uses this name as
 /// a key in a dictionary from which the message text is obtained.  Such a
 /// system allows for the optional replacement of message text at run time.
-/// More details about the message disction (and the compiler used to create
+/// More details about the message dictionary (and the compiler used to create
 /// the symbol definitions) can be found in other modules in the src/lib/log
 /// directory.
+///
+/// \section LoggingApiImplementationIssues Implementation Issues
+/// Owing to the way that the logging is implemented, notably that loggers can
+/// be declared as static external objects, there is a restriction on the
+/// length of the name of a logger component (i.e. the length of
+/// the string passed to the Logger constructor) to a maximum of 31 characters.
+/// There is no reason for this particular value other than limiting the amount
+/// of memory used.  It is defined by the constant Logger::MAX_LOGGER_NAME_SIZE,
+/// and can be made larger (or smaller) if so desired.  Note however, using a
+/// logger name larger than this limit will cause an assertion failure.
 
 class LoggerImpl;   // Forward declaration of the implementation class
 
@@ -99,6 +112,8 @@ public:
 
 class Logger {
 public:
+    /// Maximum size of a logger name
+    static const size_t MAX_LOGGER_NAME_SIZE = 31;
 
     /// \brief Constructor
     ///
@@ -107,8 +122,26 @@ public:
     /// \param name Name of the logger.  If the name is that of the root name,
     /// this creates an instance of the root logger; otherwise it creates a
     /// child of the root logger.
-    Logger(const std::string& name) : loggerptr_(NULL), name_(name)
-    {}
+    ///
+    /// \note The name of the logger may be no longer than MAX_LOGGER_NAME_SIZE
+    /// else the program will halt with an assertion failure.  This restriction
+    /// allows loggers to be declared statically: the name is stored in a
+    /// fixed-size array to avoid the need to allocate heap storage during
+    /// program initialization (which causes problems on some operating
+    /// systems).
+    ///
+    /// \note Note also that there is no constructor taking a std::string. This
+    /// minimises the possibility of initializing a static logger with a
+    /// string, so leading to problems mentioned above.
+    Logger(const char* name) : loggerptr_(NULL) {
+        assert(std::strlen(name) < sizeof(name_));
+        // Do the copy.  Note that the assertion above has checked that the
+        // contents of "name" and a trailing null will fit within the space
+        // allocated for name_, so we could use strcpy here and be safe.
+        // However, a bit of extra paranoia doesn't hurt.
+        std::strncpy(name_, name, sizeof(name_));
+        assert(name_[sizeof(name_) - 1] == '\0');
+    }
 
     /// \brief Destructor
     virtual ~Logger();
@@ -256,8 +289,8 @@ private:
     /// \brief Initialize Underlying Implementation and Set loggerptr_
     void initLoggerImpl();
 
-    LoggerImpl*     loggerptr_;     ///< Pointer to the underlying logger
-    std::string     name_;          ///< Copy of the logger name
+    LoggerImpl* loggerptr_;                  ///< Pointer to underlying logger
+    char        name_[MAX_LOGGER_NAME_SIZE + 1]; ///< Copy of the logger name
 };
 
 } // namespace log
diff --git a/src/lib/log/logger_manager.cc b/src/lib/log/logger_manager.cc
index a04fffb..8a8a36b 100644
--- a/src/lib/log/logger_manager.cc
+++ b/src/lib/log/logger_manager.cc
@@ -95,6 +95,10 @@ void
 LoggerManager::init(const std::string& root, isc::log::Severity severity,
                     int dbglevel, const char* file)
 {
+    // Load in the messages declared in the program and registered by
+    // statically-declared MessageInitializer objects.
+    MessageInitializer::loadDictionary();
+
     // Save name, severity and debug level for later.  No need to save the
     // file name as once the local message file is read the messages will
     // not be lost.
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
index d69cec8..68f2bb8 100644
--- a/src/lib/log/logger_manager_impl.cc
+++ b/src/lib/log/logger_manager_impl.cc
@@ -115,8 +115,8 @@ void
 LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
                                          const OutputOption& opt)
 {
-    LOG4CPLUS_OPEN_MODE_TYPE mode = 
-        LOG4CPLUS_FSTREAM_NAMESPACE::ios::app;  // Append to existing file
+    // Append to existing file
+    std::ios::openmode mode = std::ios::app;
 
     log4cplus::SharedAppenderPtr fileapp;
     if (opt.maxsize == 0) {
diff --git a/src/lib/log/message_exception.h b/src/lib/log/message_exception.h
index eebee89..cd6caf2 100644
--- a/src/lib/log/message_exception.h
+++ b/src/lib/log/message_exception.h
@@ -15,12 +15,14 @@
 #ifndef __MESSAGE_EXCEPTION_H
 #define __MESSAGE_EXCEPTION_H
 
+#include <exceptions/exceptions.h>
+#include <log/message_types.h>
+
 #include <stdexcept>
 #include <string>
 #include <vector>
 
 #include <boost/lexical_cast.hpp>
-#include <log/message_types.h>
 
 namespace isc {
 namespace log {
@@ -31,16 +33,18 @@ namespace log {
 /// code and its arguments to be encapsulated in an exception and thrown
 /// up the stack.
 
-class MessageException : public std::exception {
+class MessageException : public isc::Exception {
 public:
 
     /// \brief Constructor
     ///
     /// \param id Message identification.
     /// \param lineno Line number on which error occurred (if > 0).
-    MessageException(MessageID id, int lineno = 0) : id_(id)
+    MessageException(const char* file, size_t line, const char* what,
+                     MessageID id, int lineno)
+        : isc::Exception(file, line, what), id_(id), lineno_(lineno)
     {
-        if (lineno > 0) {
+        if (lineno_ > 0) {
             args_.push_back(boost::lexical_cast<std::string>(lineno));
         }
     }
@@ -50,8 +54,9 @@ public:
     /// \param id Message identification.
     /// \param arg1 First message argument.
     /// \param lineno Line number on which error occurred (if > 0).
-    MessageException(MessageID id, const std::string& arg1, int lineno = 0)
-        : id_(id)
+    MessageException(const char* file, size_t line, const char* what,
+                     MessageID id, const std::string& arg1, int lineno)
+        : isc::Exception(file, line, what), id_(id)
     {
         if (lineno > 0) {
             args_.push_back(boost::lexical_cast<std::string>(lineno));
@@ -65,8 +70,10 @@ public:
     /// \param arg1 First message argument.
     /// \param arg2 Second message argument.
     /// \param lineno Line number on which error occurred (if > 0).
-    MessageException(MessageID id, const std::string& arg1,
-        const std::string& arg2, int lineno = 0) : id_(id)
+    MessageException(const char* file, size_t line, const char *what,
+                     MessageID id, const std::string& arg1,
+                     const std::string& arg2, int lineno)
+        : isc::Exception(file, line, what), id_(id)
     {
         if (lineno > 0) {
             args_.push_back(boost::lexical_cast<std::string>(lineno));
@@ -95,6 +102,7 @@ public:
 private:
     MessageID                   id_;        // Exception ID
     std::vector<std::string>    args_;      // Exception arguments
+    int lineno_;
 };
 
 } // namespace log
diff --git a/src/lib/log/message_initializer.cc b/src/lib/log/message_initializer.cc
index 0113497..3dd5da7 100644
--- a/src/lib/log/message_initializer.cc
+++ b/src/lib/log/message_initializer.cc
@@ -12,25 +12,80 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <cassert>
+#include <cstdlib>
 #include <log/message_dictionary.h>
 #include <log/message_initializer.h>
 
+
+// As explained in the header file, initialization of the message dictionary
+// is a two-stage process:
+// 1) In the MessageInitializer constructor, a pointer to the array of
+//    messages is stored in a pre-defined array.  Since the MessageInitializers
+//    are declared statically outside a program unit, this takes place before
+//    main() is called.  As no heap storage is allocated in this process, we
+//    should avoid the static initialization fiasco in cases where
+//    initialization of system libraries is also carried out at the same time.
+// 2) After main() starts executing, loadDictionary() is called.
+//
+//
+
+namespace {
+
+// Declare the array of pointers to value arrays.
+const char** logger_values[isc::log::MessageInitializer::MAX_MESSAGE_ARRAYS];
+
+// Declare the index used to access the array.  As this needs to be initialized
+// at first used, it is accessed it via a function.
+size_t& getIndex() {
+    static size_t index = 0;
+    return (index);
+}
+
+}
+
+
 namespace isc {
 namespace log {
 
-// Constructor.  Just retrieve the global dictionary and load the IDs and
-// associated text into it.
+// Constructor.  Add the pointer to the message array to the global array.
+// This method will trigger an assertion failure if the array overflows.
 
 MessageInitializer::MessageInitializer(const char* values[]) {
+    assert(getIndex() < MAX_MESSAGE_ARRAYS);
+    logger_values[getIndex()++] = values;
+}
+
+// Return the number of arrays registered but not yet loaded.
+
+size_t
+MessageInitializer::getPendingCount() {
+    return (getIndex());
+}
+
+// Load the messages in the arrays registered in the logger_values array
+// into the global dictionary.
+
+void
+MessageInitializer::loadDictionary() {
     MessageDictionary& global = MessageDictionary::globalDictionary();
-    std::vector<std::string> repeats = global.load(values);
 
-    // Append the IDs in the list just loaded (the "repeats") to the global list
-    // of duplicate IDs.
-    if (!repeats.empty()) {
-        std::vector<std::string>& duplicates = getDuplicates();
-        duplicates.insert(duplicates.end(), repeats.begin(), repeats.end());
+    for (size_t i = 0; i < getIndex(); ++i) {
+        std::vector<std::string> repeats = global.load(logger_values[i]);
+
+        // Append the IDs in the list just loaded (the "repeats") to the
+        // global list of duplicate IDs.
+        if (!repeats.empty()) {
+            std::vector<std::string>& duplicates = getDuplicates();
+            duplicates.insert(duplicates.end(), repeats.begin(),
+                              repeats.end());
+        }
     }
+
+    // ... and mark that the messages have been loaded.  (This avoids a lot
+    // of "duplicate message ID" messages in some of the unit tests where the
+    // logging initialization code may be called multiple times.)
+    getIndex() = 0;
 }
 
 // Return reference to duplicate array
diff --git a/src/lib/log/message_initializer.h b/src/lib/log/message_initializer.h
index 7516823..28b0e61 100644
--- a/src/lib/log/message_initializer.h
+++ b/src/lib/log/message_initializer.h
@@ -15,6 +15,7 @@
 #ifndef __MESSAGEINITIALIZER_H
 #define __MESSAGEINITIALIZER_H
 
+#include <cstdlib>
 #include <string>
 #include <vector>
 #include <log/message_dictionary.h>
@@ -37,28 +38,63 @@ namespace log {
 ///             :
 ///         NULL
 ///     };
-///     MessageDictionaryHelper xyz(values);
+///     MessageInitializer xyz(values);
 ///
-/// This will automatically add the message ID/text pairs to the global
-/// dictionary during initialization - all that is required is that the module
-/// containing the definition is included into the final executable.
+/// All that needed is for the module containing the definitions to be
+/// included in the execution unit.
 ///
-/// Messages are added via the MessageDictionary::add() method, so any
-/// duplicates are stored in the the global dictionary's overflow vector whence
-/// they can be retrieved at run-time.
+/// To avoid static initialization fiasco problems, the initialization is
+/// carried out in two stages:
+/// - The constructor adds a pointer to the values array to a pre-defined array
+///   of pointers.
+/// - During the run-time initialization of the logging system, the static
+///   method loadDictionary() is called to load the message dictionary.
+/// This way, no heap storage is allocated during the static initialization,
+/// something that may give problems on some operating systems.
+///
+/// \note The maximum number of message arrays that can be added to the
+/// dictionary in this way is defined by the constant
+/// MessageInitializer::MAX_MESSAGE_ARRAYS.  This is set to 256 as a compromise
+/// between wasted space and allowing for future expansion, but can be
+/// changed (by editing the source file) to any value desired.
+///
+/// When messages are added to the dictionary, the are added via the
+/// MessageDictionary::add() method, so any duplicates are stored in the
+/// global dictionary's overflow vector whence they can be retrieved at
+/// run-time.
 
 class MessageInitializer {
 public:
+    /// Maximum number of message arrays that can be initialized in this way
+    static const size_t MAX_MESSAGE_ARRAYS = 256;
 
     /// \brief Constructor
     ///
-    /// Adds the array of values to the global dictionary, and notes any
-    /// duplicates.
+    /// Adds a pointer to the array of messages to the global array of
+    /// pointers to message arrays.
     ///
     /// \param values NULL-terminated array of alternating identifier strings
-    /// and associated message text.
+    /// and associated message text. N.B. This object stores a pointer to the
+    /// passed array; the array MUST remain valid at least until
+    /// loadDictionary() has been called.
     MessageInitializer(const char* values[]);
 
+    /// \brief Obtain pending load count
+    ///
+    /// Returns the number of message arrays that will be loaded by the next
+    /// call to loadDictionary().
+    ///
+    /// \return Number of registered message arrays.  This is reset to zero
+    ///         when loadDictionary() is called.
+    static size_t getPendingCount();
+
+    /// \brief Run-Time Initialization
+    ///
+    /// Loops through the internal array of pointers to message arrays
+    /// and adds the messages to the internal dictionary.  This is called
+    /// during run-time initialization.
+    static void loadDictionary();
+
     /// \brief Return Duplicates
     ///
     /// When messages are added to the global dictionary, any duplicates are
diff --git a/src/lib/log/message_reader.cc b/src/lib/log/message_reader.cc
index 2710ab8..b5a4d35 100644
--- a/src/lib/log/message_reader.cc
+++ b/src/lib/log/message_reader.cc
@@ -48,7 +48,8 @@ MessageReader::readFile(const string& file, MessageReader::Mode mode) {
     // Open the file.
     ifstream infile(file.c_str());
     if (infile.fail()) {
-        throw MessageException(LOG_INPUT_OPEN_FAIL, file, strerror(errno));
+        isc_throw_4(MessageException, "Failed to open message file",
+                    LOG_INPUT_OPEN_FAIL, file, strerror(errno), 0);
     }
 
     // Loop round reading it.  As we process the file one line at a time,
@@ -65,7 +66,8 @@ MessageReader::readFile(const string& file, MessageReader::Mode mode) {
 
     // Why did the loop terminate?
     if (!infile.eof()) {
-        throw MessageException(LOG_READ_ERROR, file, strerror(errno));
+        isc_throw_4(MessageException, "Error reading message file",
+                    LOG_READ_ERROR, file, strerror(errno), 0);
     }
     infile.close();
 }
@@ -114,7 +116,9 @@ MessageReader::parseDirective(const std::string& text) {
     } else {
 
         // Unrecognised directive
-        throw MessageException(LOG_UNRECOGNISED_DIRECTIVE, tokens[0], lineno_);
+        isc_throw_3(MessageException, "Unrecognized directive",
+                    LOG_UNRECOGNISED_DIRECTIVE, tokens[0],
+                    lineno_);
     }
 }
 
@@ -138,13 +142,15 @@ MessageReader::parsePrefix(const vector<string>& tokens) {
         // and numeric characters (and underscores) and does not start with a
         // digit.
         if (invalidSymbol(prefix_)) {
-            throw MessageException(LOG_PREFIX_INVALID_ARG, prefix_, lineno_);
+            isc_throw_3(MessageException, "Invalid prefix",
+                        LOG_PREFIX_INVALID_ARG, prefix_, lineno_);
         }
 
     } else {
 
         // Too many arguments
-        throw MessageException(LOG_PREFIX_EXTRA_ARGS, lineno_);
+        isc_throw_2(MessageException, "Too many arguments",
+                    LOG_PREFIX_EXTRA_ARGS, lineno_);
     }
 }
 
@@ -172,10 +178,12 @@ MessageReader::parseNamespace(const vector<string>& tokens) {
 
     // Check argument count
     if (tokens.size() < 2) {
-        throw MessageException(LOG_NAMESPACE_NO_ARGS, lineno_);
+        isc_throw_2(MessageException, "No arguments", LOG_NAMESPACE_NO_ARGS,
+                    lineno_);
 
     } else if (tokens.size() > 2) {
-        throw MessageException(LOG_NAMESPACE_EXTRA_ARGS, lineno_);
+        isc_throw_2(MessageException, "Too many arguments",
+                    LOG_NAMESPACE_EXTRA_ARGS, lineno_);
 
     }
 
@@ -187,12 +195,14 @@ MessageReader::parseNamespace(const vector<string>& tokens) {
                                       "abcdefghijklmnopqrstuvwxyz"
                                       "0123456789_:";
     if (tokens[1].find_first_not_of(valid_chars) != string::npos) {
-        throw MessageException(LOG_NAMESPACE_INVALID_ARG, tokens[1], lineno_);
+        isc_throw_3(MessageException, "Invalid argument",
+                    LOG_NAMESPACE_INVALID_ARG, tokens[1], lineno_);
     }
 
     // All OK - unless the namespace has already been set.
     if (ns_.size() != 0) {
-        throw MessageException(LOG_DUPLICATE_NAMESPACE, lineno_);
+        isc_throw_2(MessageException, "Duplicate namespace",
+                    LOG_DUPLICATE_NAMESPACE, lineno_);
     }
 
     // Prefix has not been set, so set it and return success.
@@ -219,7 +229,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
 
     // A line comprising just the message introducer is not valid.
     if (text.size() == 1) {
-        throw MessageException(LOG_NO_MESSAGE_ID, text, lineno_);
+        isc_throw_3(MessageException, "No message ID", LOG_NO_MESSAGE_ID,
+                    text, lineno_);
     }
 
     // Strip off the introducer and any leading space after that.
@@ -230,7 +241,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
     if (first_delim == string::npos) {
 
         // Just a single token in the line - this is not valid
-        throw MessageException(LOG_NO_MESSAGE_TEXT, message_line, lineno_);
+        isc_throw_3(MessageException, "No message text", LOG_NO_MESSAGE_TEXT,
+                    message_line, lineno_);
     }
 
     // Extract the first token into the message ID, preceding it with the
@@ -240,7 +252,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
     string ident = prefix_ + message_line.substr(0, first_delim);
     if (prefix_.empty()) {
         if (invalidSymbol(ident)) {
-            throw MessageException(LOG_INVALID_MESSAGE_ID, ident, lineno_);
+            isc_throw_3(MessageException, "Invalid message ID",
+                        LOG_INVALID_MESSAGE_ID, ident, lineno_);
         }
     }
     isc::util::str::uppercase(ident);
@@ -252,7 +265,8 @@ MessageReader::parseMessage(const std::string& text, MessageReader::Mode mode) {
         // ?? This happens if there are trailing delimiters, which should not
         // occur as we have stripped trailing spaces off the line.  Just treat
         // this as a single-token error for simplicity's sake.
-        throw MessageException(LOG_NO_MESSAGE_TEXT, message_line, lineno_);
+        isc_throw_3(MessageException, "No message text", LOG_NO_MESSAGE_TEXT,
+                    message_line, lineno_);
     }
 
     // Add the result to the dictionary and to the non-added list if the add to
diff --git a/src/lib/log/message_reader.h b/src/lib/log/message_reader.h
index eded9c6..a468d43 100644
--- a/src/lib/log/message_reader.h
+++ b/src/lib/log/message_reader.h
@@ -61,7 +61,7 @@ public:
     /// The ownership of the dictionary object is not transferred - the caller
     /// is responsible for managing the lifetime of the dictionary.
     MessageReader(MessageDictionary* dictionary = NULL) :
-        dictionary_(dictionary)
+        dictionary_(dictionary), lineno_(0)
     {}
 
     /// \brief Virtual Destructor
diff --git a/src/lib/log/tests/.gitignore b/src/lib/log/tests/.gitignore
new file mode 100644
index 0000000..41b863b
--- /dev/null
+++ b/src/lib/log/tests/.gitignore
@@ -0,0 +1,11 @@
+/console_test.sh
+/destination_test.sh
+/init_logger_test
+/init_logger_test.sh
+/initializer_unittests_1
+/initializer_unittests_2
+/local_file_test.sh
+/logger_example
+/run_unittests
+/severity_test.sh
+/tempdir.h
diff --git a/src/lib/log/tests/Makefile.am b/src/lib/log/tests/Makefile.am
index 53e97a1..6f3d768 100644
--- a/src/lib/log/tests/Makefile.am
+++ b/src/lib/log/tests/Makefile.am
@@ -3,15 +3,52 @@ SUBDIRS = .
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS += $(BOOST_INCLUDES)
 AM_CXXFLAGS = $(B10_CXXFLAGS)
+AM_LDADD    =
+AM_LDFLAGS  =
 
 if USE_STATIC_LINK
-AM_LDFLAGS = -static
+AM_LDFLAGS += -static
 endif
 
 CLEANFILES = *.gcno *.gcda
 
-TESTS =
+noinst_PROGRAMS = logger_example
+logger_example_SOURCES = logger_example.cc
+logger_example_CPPFLAGS = $(AM_CPPFLAGS)
+logger_example_LDFLAGS = $(AM_LDFLAGS)
+logger_example_LDADD  = $(top_builddir)/src/lib/log/liblog.la
+logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
+logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+logger_example_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
+
+noinst_PROGRAMS += init_logger_test
+init_logger_test_SOURCES = init_logger_test.cc
+init_logger_test_CPPFLAGS = $(AM_CPPFLAGS)
+init_logger_test_LDFLAGS = $(AM_LDFLAGS)
+init_logger_test_LDADD  = $(top_builddir)/src/lib/log/liblog.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
+init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+init_logger_test_LDADD += $(AM_LDADD) $(LOG4CPLUS_LIBS)
+
 if HAVE_GTEST
+TESTS =
+
+# Define the flags used in each set of tests
+if USE_CLANGPP
+# Workaround unused variables tcout and tcerr in log4cplus's streams.h.
+AM_CXXFLAGS += -Wno-unused-variable
+endif
+AM_CPPFLAGS += $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+AM_LDFLAGS  += $(GTEST_LDFLAGS)
+
+AM_LDADD += $(top_builddir)/src/lib/log/liblog.la
+AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+AM_LDADD += $(top_builddir)/src/lib/util/libutil.la
+AM_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+AM_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+AM_LDADD += $(GTEST_LDADD)
+
+# Set of unit tests for the general logging classes
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += log_formatter_unittest.cc
@@ -23,47 +60,39 @@ run_unittests_SOURCES += logger_support_unittest.cc
 run_unittests_SOURCES += logger_unittest.cc
 run_unittests_SOURCES += logger_specification_unittest.cc
 run_unittests_SOURCES += message_dictionary_unittest.cc
-run_unittests_SOURCES += message_initializer_unittest_2.cc
-run_unittests_SOURCES += message_initializer_unittest.cc
 run_unittests_SOURCES += message_reader_unittest.cc
 run_unittests_SOURCES += output_option_unittest.cc
 
-run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
-run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS)
 run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
-if USE_CLANGPP
-# This is to workaround unused variables tcout and tcerr in
-# log4cplus's streams.h.
-run_unittests_CXXFLAGS += -Wno-unused-variable
-endif
-run_unittests_LDADD  = $(GTEST_LDADD)
-run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
-run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
-run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
-endif
+run_unittests_LDADD    = $(AM_LDADD)
+run_unittests_LDFLAGS  = $(AM_LDFLAGS)
 
-noinst_PROGRAMS = logger_example
-logger_example_SOURCES = logger_example.cc
-logger_example_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-logger_example_LDFLAGS = $(AM_LDFLAGS)
-logger_example_LDADD  = $(LOG4CPLUS_LIBS)
-logger_example_LDADD += $(top_builddir)/src/lib/log/liblog.la
-logger_example_LDADD += $(top_builddir)/src/lib/util/libutil.la
-logger_example_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+# logging initialization tests.  These are put in separate programs to
+# ensure that the initialization status at the start of each test is known,
+# and to prevent circumstances where the execution of one test affects the
+# starting conditions of the next.
+TESTS += initializer_unittests_1
+initializer_unittests_1_SOURCES  = run_initializer_unittests.cc
+initializer_unittests_1_SOURCES += message_initializer_1_unittest.cc
+initializer_unittests_1_SOURCES += message_initializer_1a_unittest.cc
 
-noinst_PROGRAMS += init_logger_test
-init_logger_test_SOURCES = init_logger_test.cc
-init_logger_test_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
-init_logger_test_LDFLAGS = $(AM_LDFLAGS)
-init_logger_test_LDADD  = $(LOG4CPLUS_LIBS)
-init_logger_test_LDADD += $(top_builddir)/src/lib/log/liblog.la
-init_logger_test_LDADD += $(top_builddir)/src/lib/util/libutil.la
-init_logger_test_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+initializer_unittests_1_CPPFLAGS = $(AM_CPPFLAGS)
+initializer_unittests_1_CXXFLAGS = $(AM_CXXFLAGS)
+initializer_unittests_1_LDADD    = $(AM_LDADD)
+initializer_unittests_1_LDFLAGS  = $(AM_LDFLAGS)
+
+TESTS += initializer_unittests_2
+initializer_unittests_2_SOURCES  = run_initializer_unittests.cc
+initializer_unittests_2_SOURCES += message_initializer_2_unittest.cc
+
+initializer_unittests_2_CPPFLAGS = $(AM_CPPFLAGS)
+initializer_unittests_2_CXXFLAGS = $(AM_CXXFLAGS)
+initializer_unittests_2_LDADD    = $(AM_LDADD)
+initializer_unittests_2_LDFLAGS  = $(AM_LDFLAGS)
 
 noinst_PROGRAMS += $(TESTS)
+endif
 
 # Additional test using the shell.  These are principally tests
 # where the global logging environment is affected, and where the
diff --git a/src/lib/log/tests/log_formatter_unittest.cc b/src/lib/log/tests/log_formatter_unittest.cc
index b91665d..83fc062 100644
--- a/src/lib/log/tests/log_formatter_unittest.cc
+++ b/src/lib/log/tests/log_formatter_unittest.cc
@@ -12,7 +12,11 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include "config.h"
 #include <gtest/gtest.h>
+
+#include <util/unittests/resource.h>
+
 #include <log/log_formatter.h>
 #include <log/logger_level.h>
 
@@ -94,16 +98,66 @@ TEST_F(FormatterTest, multiArg) {
     EXPECT_EQ("The arguments are switched", outputs[0].second);
 }
 
-// Can survive and complains if placeholder is missing
-TEST_F(FormatterTest, missingPlace) {
-    EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
-                    arg("missing").arg("argument"));
+#ifdef ENABLE_LOGGER_CHECKS
+
+TEST_F(FormatterTest, mismatchedPlaceholders) {
+    // Throws MismatchedPlaceholders exception if the placeholder is missing
+    // for a supplied argument.
+    EXPECT_THROW(Formatter(isc::log::INFO, s("Missing the second %1"), this).
+                 arg("argument").arg("missing"),
+                 isc::log::MismatchedPlaceholders);
+
+#ifdef EXPECT_DEATH
+    // Likewise, if there's a redundant placeholder (or missing argument), the
+    // check detects it and aborts the program.  Due to the restriction of the
+    // current implementation, it doesn't throw.
+    EXPECT_DEATH({
+        isc::util::unittests::dontCreateCoreDumps();
+        Formatter(isc::log::INFO, s("Too many arguments in %1 %2"), this).
+            arg("only one");
+    }, ".*");
+
+    // Mixed case of above two: the exception will be thrown due to the missing
+    // placeholder, but before even it's caught the program will be aborted
+    // due to the unused placeholder as a result of the exception.
+    EXPECT_DEATH({
+        isc::util::unittests::dontCreateCoreDumps();
+        Formatter(isc::log::INFO, s("Missing the first %2"), this).
+            arg("missing").arg("argument");
+    }, ".*");
+#endif /* EXPECT_DEATH */
+}
+
+#else
+
+// If logger checks are not enabled, nothing is thrown
+TEST_F(FormatterTest, mismatchedPlaceholders) {
+    Formatter(isc::log::INFO, s("Missing the second %1"), this).
+        arg("argument").arg("missing");
     ASSERT_EQ(1, outputs.size());
     EXPECT_EQ(isc::log::INFO, outputs[0].first);
+    EXPECT_EQ("Missing the second argument "
+              "@@Missing placeholder %2 for 'missing'@@", outputs[0].second);
+
+    EXPECT_NO_THROW(Formatter(isc::log::INFO,
+                              s("Too many arguments in %1 %2"), this).
+                    arg("only one"));
+    ASSERT_EQ(2, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[1].first);
+    EXPECT_EQ("Too many arguments in only one %2 "
+              "@@Excess logger placeholders still exist@@",
+              outputs[1].second);
+
+    EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this).
+                    arg("missing").arg("argument"));
+    ASSERT_EQ(3, outputs.size());
+    EXPECT_EQ(isc::log::INFO, outputs[2].first);
     EXPECT_EQ("Missing the first argument "
-              "@@Missing placeholder %1 for 'missing'@@", outputs[0].second);
+              "@@Missing placeholder %1 for 'missing'@@", outputs[2].second);
 }
 
+#endif /* ENABLE_LOGGER_CHECKS */
+
 // Can replace multiple placeholders
 TEST_F(FormatterTest, multiPlaceholder) {
     Formatter(isc::log::INFO, s("The %1 is the %1"), this).
diff --git a/src/lib/log/tests/logger_example.cc b/src/lib/log/tests/logger_example.cc
index 2170066..d3f08f3 100644
--- a/src/lib/log/tests/logger_example.cc
+++ b/src/lib/log/tests/logger_example.cc
@@ -105,7 +105,7 @@ void usage() {
 // messages.  Looking at the output determines whether the program worked.
 
 int main(int argc, char** argv) {
-    const string ROOT_NAME = "example";
+    const char* ROOT_NAME = "example";
 
     bool                sw_found = false;   // Set true if switch found
     bool                c_found = false;    // Set true if "-c" found
diff --git a/src/lib/log/tests/logger_manager_unittest.cc b/src/lib/log/tests/logger_manager_unittest.cc
index 9eb85ad..584d0f5 100644
--- a/src/lib/log/tests/logger_manager_unittest.cc
+++ b/src/lib/log/tests/logger_manager_unittest.cc
@@ -201,7 +201,7 @@ TEST_F(LoggerManagerTest, FileLogger) {
         // Scope-limit the logger to ensure it is destroyed after the brief
         // check.  This adds weight to the idea that the logger will not
         // keep the file open.
-        Logger logger(file_spec.getLoggerName());
+        Logger logger(file_spec.getLoggerName().c_str());
 
         LOG_FATAL(logger, LOG_DUPLICATE_MESSAGE_ID).arg("test");
         ids.push_back(LOG_DUPLICATE_MESSAGE_ID);
@@ -223,7 +223,7 @@ TEST_F(LoggerManagerTest, FileLogger) {
     manager.process(spec.begin(), spec.end());
 
     // Create a new instance of the logger and log three more messages.
-    Logger logger(file_spec.getLoggerName());
+    Logger logger(file_spec.getLoggerName().c_str());
 
     LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg("test");
     ids.push_back(LOG_NO_SUCH_MESSAGE);
@@ -275,7 +275,7 @@ TEST_F(LoggerManagerTest, FileSizeRollover) {
     // three files as for the log4cplus implementation, the files appear to
     // be rolled after the message is logged.
     {
-        Logger logger(file_spec.getLoggerName());
+        Logger logger(file_spec.getLoggerName().c_str());
         LOG_FATAL(logger, LOG_NO_SUCH_MESSAGE).arg(big_arg);
         LOG_FATAL(logger, LOG_DUPLICATE_NAMESPACE).arg(big_arg);
     }
@@ -295,8 +295,8 @@ TEST_F(LoggerManagerTest, FileSizeRollover) {
     // a .3 version does not exist.
     manager.process(spec);
     {
-        Logger logger(file_spec.getLoggerName());
-        LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg(big_arg);
+        Logger logger(file_spec.getLoggerName().c_str());
+        LOG_FATAL(logger, LOG_NO_MESSAGE_TEXT).arg("42").arg(big_arg);
     }
 
     LoggerManager::reset();     // Ensure files are closed
diff --git a/src/lib/log/tests/logger_support_unittest.cc b/src/lib/log/tests/logger_support_unittest.cc
index b418906..c626c6d 100644
--- a/src/lib/log/tests/logger_support_unittest.cc
+++ b/src/lib/log/tests/logger_support_unittest.cc
@@ -79,5 +79,5 @@ TEST_F(LoggerSupportTest, LoggingInitializationCheck) {
     // ... and check that they work when logging is initialized.
     setLoggingInitialized(true);
     EXPECT_NO_THROW(test_logger.isDebugEnabled());
-    EXPECT_NO_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL));
+    EXPECT_NO_THROW(test_logger.info(LOG_INPUT_OPEN_FAIL).arg("foo").arg("bar"));
 }
diff --git a/src/lib/log/tests/logger_unittest.cc b/src/lib/log/tests/logger_unittest.cc
index edca9ce..069205e 100644
--- a/src/lib/log/tests/logger_unittest.cc
+++ b/src/lib/log/tests/logger_unittest.cc
@@ -17,6 +17,8 @@
 
 #include <gtest/gtest.h>
 
+#include <util/unittests/resource.h>
+
 #include <log/logger.h>
 #include <log/logger_manager.h>
 #include <log/logger_name.h>
@@ -58,8 +60,8 @@ TEST_F(LoggerTest, Name) {
 
 TEST_F(LoggerTest, GetLogger) {
 
-    const string name1 = "alpha";
-    const string name2 = "beta";
+    const char* name1 = "alpha";
+    const char* name2 = "beta";
 
     // Instantiate two loggers that should be the same
     Logger logger1(name1);
@@ -348,3 +350,32 @@ TEST_F(LoggerTest, IsDebugEnabledLevel) {
     EXPECT_TRUE(logger.isDebugEnabled(MID_LEVEL));
     EXPECT_TRUE(logger.isDebugEnabled(MAX_DEBUG_LEVEL));
 }
+
+// Check that if a logger name is too long, it triggers the appropriate
+// assertion.
+
+TEST_F(LoggerTest, LoggerNameLength) {
+    // The following statements should just declare a logger and nothing
+    // should happen.
+    string ok1(Logger::MAX_LOGGER_NAME_SIZE - 1, 'x');
+    Logger l1(ok1.c_str());
+    EXPECT_EQ(getRootLoggerName() + "." + ok1, l1.getName());
+
+    string ok2(Logger::MAX_LOGGER_NAME_SIZE, 'x');
+    Logger l2(ok2.c_str());
+    EXPECT_EQ(getRootLoggerName() + "." + ok2, l2.getName());
+
+    // Note: Not all systems have EXPECT_DEATH.  As it is a macro we can just
+    // test for its presence and bypass the test if not available.
+#ifdef EXPECT_DEATH
+    // Too long a logger name should trigger an assertion failure.
+    // Note that we just check that it dies - we don't check what message is
+    // output.
+    EXPECT_DEATH({
+        isc::util::unittests::dontCreateCoreDumps();
+
+        string ok3(Logger::MAX_LOGGER_NAME_SIZE + 1, 'x');
+        Logger l3(ok3.c_str());
+    }, ".*");
+#endif
+}
diff --git a/src/lib/log/tests/message_initializer_1_unittest.cc b/src/lib/log/tests/message_initializer_1_unittest.cc
new file mode 100644
index 0000000..994174c
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_1_unittest.cc
@@ -0,0 +1,79 @@
+// 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 <log/message_dictionary.h>
+#include <log/message_initializer.h>
+#include <boost/lexical_cast.hpp>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::log;
+using namespace std;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+const char* values1[] = {
+    "GLOBAL1", "global message one",
+    "GLOBAL2", "global message two",
+    NULL
+};
+
+const char* values2[] = {
+    "GLOBAL3", "global message three",
+    "GLOBAL4", "global message four",
+    NULL
+};
+
+}
+
+// Statically initialize the global dictionary with those messages.  Three sets
+// are used to check that the declaration of separate initializer objects
+// really does combine the messages. (The third set - declaring message IDs
+// GLOBAL5 and GLOBAL6) is declared in the separately-compiled file,
+// message_identifier_initializer_1a_unittest.cc.)
+
+const MessageInitializer init_message_initializer_unittest_1(values1);
+const MessageInitializer init_message_initializer_unittest_2(values2);
+
+// Check that the global dictionary is initialized with the specified
+// messages.
+
+TEST(MessageInitializerTest1, MessageTest) {
+    MessageDictionary& global = MessageDictionary::globalDictionary();
+
+    // Pointers to the message arrays should have been stored, but none of the
+    // messages should yet be in the dictionary.
+    for (int i = 1; i <= 6; ++i) {
+        string symbol = string("GLOBAL") + boost::lexical_cast<std::string>(i);
+        EXPECT_EQ(string(""), global.getText(symbol));
+    }
+
+    // Load the dictionary - this should clear the message array pending count.
+    // (N.B. We do not check for a known value before the call, only that the
+    // value is not zero.  This is because libraries against which the test
+    // is linked may have registered their own message arrays.)
+    EXPECT_NE(0, MessageInitializer::getPendingCount());
+    MessageInitializer::loadDictionary();
+    EXPECT_EQ(0, MessageInitializer::getPendingCount());
+
+    // ... and check the messages loaded.
+    EXPECT_EQ(string("global message one"), global.getText("GLOBAL1"));
+    EXPECT_EQ(string("global message two"), global.getText("GLOBAL2"));
+    EXPECT_EQ(string("global message three"), global.getText("GLOBAL3"));
+    EXPECT_EQ(string("global message four"), global.getText("GLOBAL4"));
+    EXPECT_EQ(string("global message five"), global.getText("GLOBAL5"));
+    EXPECT_EQ(string("global message six"), global.getText("GLOBAL6"));
+}
diff --git a/src/lib/log/tests/message_initializer_1a_unittest.cc b/src/lib/log/tests/message_initializer_1a_unittest.cc
new file mode 100644
index 0000000..3360167
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_1a_unittest.cc
@@ -0,0 +1,37 @@
+// 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.
+
+// The sole purpose of this file is to provide a set of message definitions
+// in a separate compilation unit from the one in which their presence is
+// checked.  This tests that merely declaring the MessageInitializer object
+// is enough to include the definitions in the global dictionary.
+
+#include <log/message_initializer.h>
+
+using namespace isc::log;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+
+const char* values3[] = {
+    "GLOBAL5", "global message five",
+    "GLOBAL6", "global message six",
+    NULL
+};
+
+}
+
+// Register the messages for loading into the global dictionary
+const MessageInitializer init_message_initializer_unittest_3(values3);
diff --git a/src/lib/log/tests/message_initializer_2_unittest.cc b/src/lib/log/tests/message_initializer_2_unittest.cc
new file mode 100644
index 0000000..b479eee
--- /dev/null
+++ b/src/lib/log/tests/message_initializer_2_unittest.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/message_initializer.h>
+#include <gtest/gtest.h>
+
+#include <util/unittests/resource.h>
+
+using namespace isc::log;
+
+// Declare a set of messages to go into the global dictionary.
+
+namespace {
+const char* values[] = {
+    "GLOBAL1", "global message one",
+    "GLOBAL2", "global message two",
+    NULL
+};
+}
+
+TEST(MessageInitializerTest2, MessageLoadTest) {
+    // Load the maximum number of message arrays allowed.  Some arrays may
+    // already have been loaded because of static initialization from modules
+    // in libraries linked against the test program, hence the reason for the
+    // loop starting from the value returned by getPendingCount() instead of 0.
+    for (size_t i = MessageInitializer::getPendingCount();
+         i < MessageInitializer::MAX_MESSAGE_ARRAYS; ++i) {
+        MessageInitializer initializer1(values);
+    }
+
+    // Note: Not all systems have EXPECT_DEATH.  As it is a macro we can just
+    // test for its presence and bypass the test if not available.
+#ifdef EXPECT_DEATH
+    // Adding one more should take us over the limit.
+    EXPECT_DEATH({
+        isc::util::unittests::dontCreateCoreDumps();
+
+        MessageInitializer initializer2(values);
+      }, ".*");
+#endif
+}
diff --git a/src/lib/log/tests/message_initializer_unittest.cc b/src/lib/log/tests/message_initializer_unittest.cc
deleted file mode 100644
index 0cd1879..0000000
--- a/src/lib/log/tests/message_initializer_unittest.cc
+++ /dev/null
@@ -1,70 +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 <cstddef>
-#include <string>
-#include <gtest/gtest.h>
-#include <log/message_dictionary.h>
-#include <log/message_initializer.h>
-
-using namespace isc;
-using namespace isc::log;
-using namespace std;
-
-// Declare a set of messages to go into the global dictionary.
-
-namespace {
-const char* values1[] = {
-    "GLOBAL1", "global message one",
-    "GLOBAL2", "global message two",
-    NULL
-};
-
-const char* values2[] = {
-    "GLOBAL3", "global message three",
-    "GLOBAL4", "global message four",
-    NULL
-};
-
-}
-
-// Statically initialize the global dictionary with those messages.  Three sets
-// are used to check that the declaration of separate initializer objects really// does combine the messages. (The third set is declared in the separately-
-// compiled file message_identifier_initializer_unittest_2.cc.)
-
-MessageInitializer init_message_initializer_unittest_1(values1);
-MessageInitializer init_message_initializer_unittest_2(values2);
-
-
-class MessageInitializerTest : public ::testing::Test {
-protected:
-    MessageInitializerTest()
-    {
-    }
-};
-
-
-// Check that the global dictionary is initialized with the specified
-// messages.
-
-TEST_F(MessageInitializerTest, MessageTest) {
-    MessageDictionary& global = MessageDictionary::globalDictionary();
-
-    EXPECT_EQ(string("global message one"), global.getText("GLOBAL1"));
-    EXPECT_EQ(string("global message two"), global.getText("GLOBAL2"));
-    EXPECT_EQ(string("global message three"), global.getText("GLOBAL3"));
-    EXPECT_EQ(string("global message four"), global.getText("GLOBAL4"));
-    EXPECT_EQ(string("global message five"), global.getText("GLOBAL5"));
-    EXPECT_EQ(string("global message six"), global.getText("GLOBAL6"));
-}
diff --git a/src/lib/log/tests/message_initializer_unittest_2.cc b/src/lib/log/tests/message_initializer_unittest_2.cc
deleted file mode 100644
index 94abb08..0000000
--- a/src/lib/log/tests/message_initializer_unittest_2.cc
+++ /dev/null
@@ -1,39 +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.
-
-// The sole purpose of this file is to provide a set of message definitions
-// in a separate compilation unit from the one in which their presence is
-// checked.  This tests that merely declaring the MessageInitializer object
-// is enough to include the definitions in the global dictionary.
-
-#include <log/message_initializer.h>
-
-using namespace isc::log;
-
-// Declare a set of messages to go into the global dictionary.
-
-namespace {
-
-const char* values3[] = {
-    "GLOBAL5", "global message five",
-    "GLOBAL6", "global message six",
-    NULL
-};
-
-}
-
-// Statically initialize the global dictionary with those messages.
-// Three sets are used to check that the declaration of separate
-// initializer objects really does combine the messages.
-MessageInitializer init_message_initializer_unittest_3(values3);
diff --git a/src/lib/log/tests/run_initializer_unittests.cc b/src/lib/log/tests/run_initializer_unittests.cc
new file mode 100644
index 0000000..54ee120
--- /dev/null
+++ b/src/lib/log/tests/run_initializer_unittests.cc
@@ -0,0 +1,24 @@
+// 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 <gtest/gtest.h>
+#include <util/unittests/run_all.h>
+
+#include <log/logger_support.h>
+
+int
+main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/nsas/.gitignore b/src/lib/nsas/.gitignore
new file mode 100644
index 0000000..109ef04
--- /dev/null
+++ b/src/lib/nsas/.gitignore
@@ -0,0 +1,2 @@
+/nsas_messages.cc
+/nsas_messages.h
diff --git a/src/lib/nsas/glue_hints.cc b/src/lib/nsas/glue_hints.cc
index 02c27ee..3caae25 100644
--- a/src/lib/nsas/glue_hints.cc
+++ b/src/lib/nsas/glue_hints.cc
@@ -86,8 +86,8 @@ GlueHints::GlueHints(const std::string& zone_name,
 
 bool
 GlueHints::hasGlue(AddressFamily family) const {
-    return ((addresses_v4.size() > 0 && (family == ANY_OK || family == V4_ONLY)) ||
-            (addresses_v6.size() > 0 && (family == ANY_OK || family == V6_ONLY)));
+    return ((!addresses_v4.empty() && (family == ANY_OK || family == V4_ONLY)) ||
+            (!addresses_v6.empty() && (family == ANY_OK || family == V6_ONLY)));
 }
 
 NameserverAddress
diff --git a/src/lib/nsas/hash.cc b/src/lib/nsas/hash.cc
index dbd8eec..ac2af15 100644
--- a/src/lib/nsas/hash.cc
+++ b/src/lib/nsas/hash.cc
@@ -99,9 +99,8 @@ Hash::Hash(uint32_t tablesize, uint32_t maxkeylen, bool randomise) :
 
     if (randomise) {
         init_value.curtime = time(NULL);
-    }
-    else {
-        init_value.seed = 0;
+    } else {
+        init_value.seed = 1;
     }
     srandom(init_value.seed);
 
diff --git a/src/lib/nsas/hash_table.h b/src/lib/nsas/hash_table.h
index c028fa4..6028473 100644
--- a/src/lib/nsas/hash_table.h
+++ b/src/lib/nsas/hash_table.h
@@ -59,7 +59,7 @@ struct HashTableSlot {
     /// \brief Copy Constructor
     ///
     /// ... which as noted in the class description does not copy.
-    HashTableSlot(const HashTableSlot<T>&)
+    HashTableSlot(const HashTableSlot<T>&) : mutex_(), list_()
     { }
 
 public:
diff --git a/src/lib/nsas/tests/.gitignore b/src/lib/nsas/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/nsas/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/nsas/tests/Makefile.am b/src/lib/nsas/tests/Makefile.am
index 420e897..afd91f6 100644
--- a/src/lib/nsas/tests/Makefile.am
+++ b/src/lib/nsas/tests/Makefile.am
@@ -46,11 +46,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 run_unittests_LDADD    = $(GTEST_LDADD)
 
-# NOTE: we may have to clean up this hack later (see the note in configure.ac)
-if NEED_LIBBOOST_THREAD
-run_unittests_LDADD += -lboost_thread
-endif
-
 run_unittests_LDADD += $(top_builddir)/src/lib/nsas/libnsas.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/nsas/tests/nameserver_entry_unittest.cc b/src/lib/nsas/tests/nameserver_entry_unittest.cc
index 3435d26..3aca08f 100644
--- a/src/lib/nsas/tests/nameserver_entry_unittest.cc
+++ b/src/lib/nsas/tests/nameserver_entry_unittest.cc
@@ -139,7 +139,7 @@ TEST_F(NameserverEntryTest, SetRTT) {
     NameserverEntry::AddressVector vec;
     alpha->getAddresses(vec);
 
-    ASSERT_TRUE(vec.size() > 0);
+    ASSERT_FALSE(vec.empty());
 
     // Take the first address and change the RTT.
     IOAddress first_address = vec[0].getAddress();
@@ -174,7 +174,7 @@ TEST_F(NameserverEntryTest, Unreachable) {
     NameserverEntry::AddressVector vec;
     alpha->getAddresses(vec);
 
-    ASSERT_TRUE(vec.size() > 0);
+    ASSERT_FALSE(vec.empty());
 
     // Take the first address and mark as unreachable.
     IOAddress first_address = vec[0].getAddress();
diff --git a/src/lib/nsas/zone_entry.cc b/src/lib/nsas/zone_entry.cc
index 1c5df03..8a72e5f 100644
--- a/src/lib/nsas/zone_entry.cc
+++ b/src/lib/nsas/zone_entry.cc
@@ -329,7 +329,7 @@ updateAddressSelector(std::vector<NameserverAddress>& addresses,
                 it != probabilities.end(); ++it){
             (*it) /= sum;
         }
-    } else if(probabilities.size() > 0){
+    } else if(!probabilities.empty()){
         // If all the nameservers are unreachable, the sum will be 0
         // So give each server equal opportunity to be selected.
         for(vector<double>::iterator it = probabilities.begin();
diff --git a/src/lib/python/.gitignore b/src/lib/python/.gitignore
new file mode 100644
index 0000000..9252d05
--- /dev/null
+++ b/src/lib/python/.gitignore
@@ -0,0 +1 @@
+/bind10_config.py
diff --git a/src/lib/python/Makefile.am b/src/lib/python/Makefile.am
index 893bb8c..e3ae4b5 100644
--- a/src/lib/python/Makefile.am
+++ b/src/lib/python/Makefile.am
@@ -3,7 +3,7 @@ SUBDIRS = isc
 nodist_python_PYTHON =	bind10_config.py
 pythondir = $(pyexecdir)
 
-CLEANFILES = bind10_config.pyc
+CLEANFILES = bind10_config.pyc bind10_config.pyo
 CLEANDIRS = __pycache__
 
 clean-local:
diff --git a/src/lib/python/isc/Makefile.am b/src/lib/python/isc/Makefile.am
index a3e74c5..aef5dc3 100644
--- a/src/lib/python/isc/Makefile.am
+++ b/src/lib/python/isc/Makefile.am
@@ -1,5 +1,5 @@
 SUBDIRS = datasrc cc config dns log net notify util testutils acl bind10
-SUBDIRS += xfrin log_messages
+SUBDIRS += xfrin log_messages server_common
 
 python_PYTHON = __init__.py
 
diff --git a/src/lib/python/isc/acl/Makefile.am b/src/lib/python/isc/acl/Makefile.am
index b1afa15..b9a0c81 100644
--- a/src/lib/python/isc/acl/Makefile.am
+++ b/src/lib/python/isc/acl/Makefile.am
@@ -26,11 +26,11 @@ _dns_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-acl_la_LDFLAGS += -module
+acl_la_LDFLAGS += -module -avoid-version
 acl_la_LIBADD = $(top_builddir)/src/lib/acl/libacl.la
 acl_la_LIBADD += $(PYTHON_LIB)
 
-_dns_la_LDFLAGS += -module
+_dns_la_LDFLAGS += -module -avoid-version
 _dns_la_LIBADD = $(top_builddir)/src/lib/acl/libdnsacl.la
 _dns_la_LIBADD += $(PYTHON_LIB)
 
diff --git a/src/lib/python/isc/acl/tests/dns_test.py b/src/lib/python/isc/acl/tests/dns_test.py
index 7ee3023..d225bee 100644
--- a/src/lib/python/isc/acl/tests/dns_test.py
+++ b/src/lib/python/isc/acl/tests/dns_test.py
@@ -321,7 +321,7 @@ class RequestACLTest(unittest.TestCase):
                                   '     "from": "192.0.2.0/24"},' +
                                   '    {"action": "DROP",' +
                                   '     "from": "2001:db8::1"},' +
-                                  '] }')
+                                  ']')
         self.assertEqual(ACCEPT, acl.execute(CONTEXT4))
         self.assertEqual(REJECT, acl.execute(get_context('192.0.2.2')))
         self.assertEqual(DROP, acl.execute(get_context('2001:db8::1')))
diff --git a/src/lib/python/isc/bind10/component.py b/src/lib/python/isc/bind10/component.py
index 091bfee..da2730c 100644
--- a/src/lib/python/isc/bind10/component.py
+++ b/src/lib/python/isc/bind10/component.py
@@ -30,6 +30,8 @@ configuration). This is yet to be designed.
 import isc.log
 from isc.log_messages.bind10_messages import *
 import time
+import os
+import signal
 
 logger = isc.log.Logger("boss")
 DBG_TRACE_DATA = 20
@@ -45,6 +47,14 @@ STATE_DEAD = 'dead'
 STATE_STOPPED = 'stopped'
 STATE_RUNNING = 'running'
 
+def get_signame(signal_number):
+    """Return the symbolic name for a signal."""
+    for sig in dir(signal):
+        if sig.startswith("SIG") and sig[3].isalnum():
+            if getattr(signal, sig) == signal_number:
+                return sig
+    return "unknown signal"
+
 class BaseComponent:
     """
     This represents a single component. This one is an abstract base class.
@@ -206,8 +216,24 @@ class BaseComponent:
                 it is considered a core or needed component, or because
                 the component is to be restarted later.
         """
+
+        if exit_code is not None:
+            if os.WIFEXITED(exit_code):
+                exit_str = "process exited normally with exit status %d" % (exit_code)
+            elif os.WIFSIGNALED(exit_code):
+                sig = os.WTERMSIG(exit_code)
+                signame = get_signame(sig)
+                if os.WCOREDUMP(exit_code):
+                    exit_str = "process dumped core with exit status %d (killed by signal %d: %s)" % (exit_code, sig, signame)
+                else:
+                    exit_str = "process terminated with exit status %d (killed by signal %d: %s)" % (exit_code, sig, signame)
+            else:
+                exit_str = "unknown condition with exit status %d" % (exit_code)
+        else:
+            exit_str = "unknown condition"
+
         logger.error(BIND10_COMPONENT_FAILED, self.name(), self.pid(),
-                     exit_code if exit_code is not None else "unknown")
+                     exit_str)
         if not self.running():
             raise ValueError("Can't fail component that isn't running")
         self.__state = STATE_STOPPED
diff --git a/src/lib/python/isc/cc/session.py b/src/lib/python/isc/cc/session.py
index f6b6265..33a47bd 100644
--- a/src/lib/python/isc/cc/session.py
+++ b/src/lib/python/isc/cc/session.py
@@ -72,7 +72,7 @@ class Session:
         self._lname = None
         self._closed = True
 
-    def sendmsg(self, env, msg = None):
+    def sendmsg(self, env, msg=None):
         with self._lock:
             if self._closed:
                 raise SessionError("Session has been closed.")
@@ -82,15 +82,24 @@ class Session:
                 raise ProtocolError("Envelope too large")
             if type(msg) == dict:
                 msg = isc.cc.message.to_wire(msg)
-            self._socket.setblocking(1)
             length = 2 + len(env);
-            if msg:
+            if msg is not None:
                 length += len(msg)
-            self._socket.send(struct.pack("!I", length))
-            self._socket.send(struct.pack("!H", len(env)))
-            self._socket.send(env)
-            if msg:
-                self._socket.send(msg)
+
+            # Build entire message.
+            data = struct.pack("!I", length)
+            data += struct.pack("!H", len(env))
+            data += env
+            if msg is not None:
+                data += msg
+
+            # Send it in the blocking mode.  On some systems send() may
+            # actually send only part of the data, so we need to repeat it
+            # until all data have been sent out.
+            self._socket.setblocking(1)
+            while len(data) > 0:
+                cc = self._socket.send(data)
+                data = data[cc:]
 
     def recvmsg(self, nonblock = True, seq = None):
         """Reads a message. If nonblock is true, and there is no
diff --git a/src/lib/python/isc/cc/tests/.gitignore b/src/lib/python/isc/cc/tests/.gitignore
new file mode 100644
index 0000000..b7ac2ae
--- /dev/null
+++ b/src/lib/python/isc/cc/tests/.gitignore
@@ -0,0 +1 @@
+/cc_test
diff --git a/src/lib/python/isc/cc/tests/session_test.py b/src/lib/python/isc/cc/tests/session_test.py
index 772ed0c..e589085 100644
--- a/src/lib/python/isc/cc/tests/session_test.py
+++ b/src/lib/python/isc/cc/tests/session_test.py
@@ -29,6 +29,7 @@ class MySocket():
         self.recvqueue = bytearray()
         self.sendqueue = bytearray()
         self._blocking = True
+        self.send_limit = None
 
     def connect(self, to):
         pass
@@ -40,7 +41,14 @@ class MySocket():
         self._blocking = val
 
     def send(self, data):
-        self.sendqueue.extend(data);
+        # If the upper limit is specified, only "send" up to the specified
+        # limit
+        if self.send_limit is not None and len(data) > self.send_limit:
+            self.sendqueue.extend(data[0:self.send_limit])
+            return self.send_limit
+        else:
+            self.sendqueue.extend(data)
+            return len(data)
 
     def readsent(self, length):
         if length > len(self.sendqueue):
@@ -101,6 +109,17 @@ class MySocket():
     def gettimeout(self):
         return 0
 
+    def set_send_limit(self, limit):
+        '''Specify the upper limit of the transmittable data at once.
+
+        By default, the send() method of this class "sends" all given data.
+        If this method is called and the its parameter is not None,
+        subsequent calls to send() will only transmit the specified amount
+        of data.  This can be used to emulate the situation where send()
+        on a real socket object results in partial write.
+        '''
+        self.send_limit = limit
+
 #
 # We subclass the Session class we're testing here, only
 # to override the __init__() method, which wants a socket,
@@ -157,6 +176,16 @@ class testSession(unittest.TestCase):
         #print(sent)
         #self.assertRaises(SessionError, sess.sendmsg, {}, {"hello": "a"})
 
+    def test_session_sendmsg_shortwrite(self):
+        sess = MySession()
+        # Specify the upper limit of the size that can be transmitted at
+        # a single send() call on the faked socket (10 is an arbitrary choice,
+        # just reasonably small).
+        sess._socket.set_send_limit(10)
+        sess.sendmsg({'to': 'someone', 'reply': 1}, {"hello": "a"})
+        # The complete message should still have been transmitted in the end.
+        sent = sess._socket.readsentmsg();
+
     def recv_and_compare(self, session, bytes, env, msg):
         """Adds bytes to the recvqueue (which will be read by the
            session object, and compare the resultinv env and msg to
diff --git a/src/lib/python/isc/config/Makefile.am b/src/lib/python/isc/config/Makefile.am
index ef696fb..cda8b57 100644
--- a/src/lib/python/isc/config/Makefile.am
+++ b/src/lib/python/isc/config/Makefile.am
@@ -13,6 +13,7 @@ CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.py
 CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/cfgmgr_messages.pyc
 CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.py
 CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/config_messages.pyo
 
 CLEANDIRS = __pycache__
 
diff --git a/src/lib/python/isc/config/ccsession.py b/src/lib/python/isc/config/ccsession.py
index b505399..703d196 100644
--- a/src/lib/python/isc/config/ccsession.py
+++ b/src/lib/python/isc/config/ccsession.py
@@ -38,6 +38,7 @@
 
 from isc.cc import Session
 from isc.config.config_data import ConfigData, MultiConfigData, BIND10_CONFIG_DATA_VERSION
+import isc.config.module_spec
 import isc
 from isc.util.file import path_search
 import bind10_config
@@ -327,43 +328,97 @@ class ModuleCCSession(ConfigData):
            and return an answer created with create_answer()"""
         self._command_handler = command_handler
 
-    def add_remote_config(self, spec_file_name, config_update_callback = None):
-        """Gives access to the configuration of a different module.
-           These remote module options can at this moment only be
-           accessed through get_remote_config_value(). This function
-           also subscribes to the channel of the remote module name
-           to receive the relevant updates. It is not possible to
-           specify your own handler for this right now.
-           start() must have been called on this CCSession
-           prior to the call to this method.
-           Returns the name of the module."""
-        module_spec = isc.config.module_spec_from_file(spec_file_name)
+    def _add_remote_config_internal(self, module_spec,
+                                    config_update_callback=None):
+        """The guts of add_remote_config and add_remote_config_by_name"""
         module_cfg = ConfigData(module_spec)
         module_name = module_spec.get_module_name()
+
         self._session.group_subscribe(module_name)
 
         # Get the current config for that module now
         seq = self._session.group_sendmsg(create_command(COMMAND_GET_CONFIG, { "module_name": module_name }), "ConfigManager")
 
         try:
-            answer, env = self._session.group_recvmsg(False, seq)
+            answer, _ = self._session.group_recvmsg(False, seq)
         except isc.cc.SessionTimeout:
             raise ModuleCCSessionError("No answer from ConfigManager when "
                                        "asking about Remote module " +
                                        module_name)
+        call_callback = False
         if answer:
             rcode, value = parse_answer(answer)
             if rcode == 0:
-                if value != None and module_spec.validate_config(False, value):
-                    module_cfg.set_local_config(value)
-                    if config_update_callback is not None:
-                        config_update_callback(value, module_cfg)
+                if value != None:
+                    if module_spec.validate_config(False, value):
+                        module_cfg.set_local_config(value)
+                        call_callback = True
+                    else:
+                        raise ModuleCCSessionError("Bad config data for " +
+                                                   module_name + ": " +
+                                                   str(value))
+            else:
+                raise ModuleCCSessionError("Failure requesting remote " +
+                                           "configuration data for " +
+                                           module_name)
 
         # all done, add it
         self._remote_module_configs[module_name] = module_cfg
         self._remote_module_callbacks[module_name] = config_update_callback
+        if call_callback and config_update_callback is not None:
+            config_update_callback(value, module_cfg)
+
+    def add_remote_config_by_name(self, module_name,
+                                  config_update_callback=None):
+        """
+        This does the same as add_remote_config, but you provide the module name
+        instead of the name of the spec file.
+        """
+        seq = self._session.group_sendmsg(create_command(COMMAND_GET_MODULE_SPEC,
+                                                         { "module_name":
+                                                         module_name }),
+                                          "ConfigManager")
+        try:
+            answer, env = self._session.group_recvmsg(False, seq)
+        except isc.cc.SessionTimeout:
+            raise ModuleCCSessionError("No answer from ConfigManager when " +
+                                       "asking about for spec of Remote " +
+                                       "module " + module_name)
+        if answer:
+            rcode, value = parse_answer(answer)
+            if rcode == 0:
+                module_spec = isc.config.module_spec.ModuleSpec(value)
+                if module_spec.get_module_name() != module_name:
+                    raise ModuleCCSessionError("Module name mismatch: " +
+                                               module_name + " and " +
+                                               module_spec.get_module_name())
+                self._add_remote_config_internal(module_spec,
+                                                 config_update_callback)
+            else:
+                raise ModuleCCSessionError("Error code " + str(rcode) +
+                                           "when asking for module spec of " +
+                                           module_name)
+        else:
+            raise ModuleCCSessionError("No answer when asking for module " +
+                                       "spec of " + module_name)
+        # Just to be consistent with the add_remote_config
         return module_name
-        
+
+    def add_remote_config(self, spec_file_name, config_update_callback=None):
+        """Gives access to the configuration of a different module.
+           These remote module options can at this moment only be
+           accessed through get_remote_config_value(). This function
+           also subscribes to the channel of the remote module name
+           to receive the relevant updates. It is not possible to
+           specify your own handler for this right now, but you can
+           specify a callback that is called after the change happened.
+           start() must have been called on this CCSession
+           prior to the call to this method.
+           Returns the name of the module."""
+        module_spec = isc.config.module_spec_from_file(spec_file_name)
+        self._add_remote_config_internal(module_spec, config_update_callback)
+        return module_spec.get_module_name()
+
     def remove_remote_config(self, module_name):
         """Removes the remote configuration access for this module"""
         if module_name in self._remote_module_configs:
@@ -501,8 +556,8 @@ class UIModuleCCSession(MultiConfigData):
                 self.set_value(identifier, cur_map)
             else:
                 raise isc.cc.data.DataAlreadyPresentError(value +
-                                                          " already in "
-                                                          + identifier)
+                                                          " already in " +
+                                                          identifier)
 
     def add_value(self, identifier, value_str = None, set_value_str = None):
         """Add a value to a configuration list. Raises a DataTypeError
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index dd97827..9f9ce68 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -148,6 +148,27 @@ class ConfigManagerData:
             # Ok if we really can't delete it anymore, leave it
             pass
 
+    def rename_config_file(self, old_file_name=None, new_file_name=None):
+        """Renames the given configuration file to the given new file name,
+           if it exists. If it does not exist, nothing happens.
+           If old_file_name is None (default), the file used in
+           read_from_file is used. If new_file_name is None (default), the
+           file old_file_name appended with .bak is used. If that file exists
+           already, .1 is appended. If that file exists, .2 is appended, etc.
+        """
+        if old_file_name is None:
+            old_file_name = self.db_filename
+        if new_file_name is None:
+            new_file_name = old_file_name + ".bak"
+        if os.path.exists(new_file_name):
+            i = 1
+            while os.path.exists(new_file_name + "." + str(i)):
+                i += 1
+            new_file_name = new_file_name + "." + str(i)
+        if os.path.exists(old_file_name):
+            logger.info(CFGMGR_RENAMED_CONFIG_FILE, old_file_name, new_file_name)
+            os.rename(old_file_name, new_file_name)
+
     def __eq__(self, other):
         """Returns True if the data contained is equal. data_path and
            db_filename may be different."""
@@ -163,14 +184,16 @@ class ConfigManager:
        channel session. If not, a new session will be created.
        The ability to specify a custom session is for testing purposes
        and should not be needed for normal usage."""
-    def __init__(self, data_path, database_filename, session=None):
+    def __init__(self, data_path, database_filename, session=None,
+                 clear_config=False):
         """Initialize the configuration manager. The data_path string
            is the path to the directory where the configuration is
            stored (in <data_path>/<database_filename> or in
-           <database_filename>, if it is absolute). The dabase_filename
+           <database_filename>, if it is absolute). The database_filename
            is the config file to load. Session is an optional
            cc-channel session. If this is not given, a new one is
-           created."""
+           created. If clear_config is True, the configuration file is
+           renamed and a new one is created."""
         self.data_path = data_path
         self.database_filename = database_filename
         self.module_specs = {}
@@ -179,6 +202,8 @@ class ConfigManager:
         # of some other process
         self.virtual_modules = {}
         self.config = ConfigManagerData(data_path, database_filename)
+        if clear_config:
+            self.config.rename_config_file()
         if session:
             self.cc = session
         else:
diff --git a/src/lib/python/isc/config/cfgmgr_messages.mes b/src/lib/python/isc/config/cfgmgr_messages.mes
index 61a63ed..ad78be0 100644
--- a/src/lib/python/isc/config/cfgmgr_messages.mes
+++ b/src/lib/python/isc/config/cfgmgr_messages.mes
@@ -51,7 +51,11 @@ error is given. The most likely cause is that the system does not have
 write access to the configuration database file. The updated
 configuration is not stored.
 
+% CFGMGR_RENAMED_CONFIG_FILE renamed configuration file %1 to %2, will create new %1
+BIND 10 has been started with the command to clear the configuration file.
+The existing file is backed up to the given file name, so that data is not
+immediately lost if this was done by accident.
+
 % CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the cfgmgr daemon. The
 daemon will now shut down.
-
diff --git a/src/lib/python/isc/config/config_data.py b/src/lib/python/isc/config/config_data.py
index f8da086..2bec4ab 100644
--- a/src/lib/python/isc/config/config_data.py
+++ b/src/lib/python/isc/config/config_data.py
@@ -23,6 +23,7 @@ two through the classes in ccsession)
 import isc.cc.data
 import isc.config.module_spec
 import ast
+import copy
 
 class ConfigDataError(Exception): pass
 
@@ -42,7 +43,7 @@ def spec_part_is_map(spec_part):
 def spec_part_is_named_set(spec_part):
     """Returns True if the given spec_part is a dict that contains a
        named_set specification, and False otherwise."""
-    return (type(spec_part) == dict and 'named_map_item_spec' in spec_part)
+    return (type(spec_part) == dict and 'named_set_item_spec' in spec_part)
 
 def check_type(spec_part, value):
     """Does nothing if the value is of the correct type given the
@@ -109,14 +110,17 @@ def convert_type(spec_part, value):
 
             return ret
         elif data_type == "map":
-            map = ast.literal_eval(value)
-            if type(map) == dict:
-                # todo: check types of map contents too
-                return map
-            else:
-                raise isc.cc.data.DataTypeError(
-                           "Value in convert_type not a string "
-                           "specifying a dict")
+            try:
+                map = ast.literal_eval(value)
+                if type(map) == dict:
+                    # todo: check types of map contents too
+                    return map
+                else:
+                    raise isc.cc.data.DataTypeError(
+                               "Value in convert_type not a string "
+                               "specifying a dict")
+            except SyntaxError as se:
+                raise isc.cc.data.DataTypeError("Error parsing map: " + str(se))
         else:
             return value
     except ValueError as err:
@@ -207,7 +211,8 @@ def find_spec_part(element, identifier, strict_identifier = True):
         cur_el = _get_map_or_list(cur_el)
 
     cur_el = _find_spec_part_single(cur_el, id_parts[-1])
-    return cur_el
+    # Due to the raw datatypes we use, it is safer to return a deep copy here
+    return copy.deepcopy(cur_el)
 
 def spec_name_list(spec, prefix="", recurse=False):
     """Returns a full list of all possible item identifiers in the
@@ -415,6 +420,14 @@ class MultiConfigData:
            manager or the modules."""
         return self._local_changes
 
+    def set_local_changes(self, new_local_changes):
+        """Sets the entire set of local changes, used when reverting
+           changes done automatically in case there was a problem (e.g.
+           when executing commands from a script that fails halfway
+           through).
+        """
+        self._local_changes = new_local_changes
+
     def clear_local_changes(self):
         """Reverts all local changes"""
         self._local_changes = {}
@@ -576,8 +589,10 @@ class MultiConfigData:
             if item_type == "list" and (all or first):
                 spec_part_list = spec_part['list_item_spec']
                 list_value, status = self.get_value(identifier)
+                # If not set, and no default, lists will show up as 'None',
+                # but it's better to treat it as an empty list then
                 if list_value is None:
-                    raise isc.cc.data.DataNotFoundError(identifier + " not found")
+                    list_value = []
 
                 if type(list_value) != list:
                     # the identifier specified a single element
@@ -669,6 +684,16 @@ class MultiConfigData:
                 self._append_value_item(result, spec_part, identifier, all, True)
         return result
 
+    def unset(self, identifier):
+        """
+        Reset the value to default.
+        """
+        spec_part = self.find_spec_part(identifier)
+        if spec_part is not None:
+            isc.cc.data.unset(self._local_changes, identifier)
+        else:
+            raise isc.cc.data.DataNotFoundError(identifier + "not found")
+
     def set_value(self, identifier, value):
         """Set the local value at the given identifier to value. If
            there is a specification for the given identifier, the type
@@ -717,6 +742,15 @@ class MultiConfigData:
                                     cur_id_part + id,
                                     cur_value)
             cur_id_part = cur_id_part + id_part + "/"
+
+            # We also need to copy to local if we are changing a named set,
+            # so that the other items in the set do not disappear
+            if spec_part_is_named_set(self.find_spec_part(cur_id_part)):
+                ns_value, ns_status = self.get_value(cur_id_part)
+                if ns_status != MultiConfigData.LOCAL:
+                    isc.cc.data.set(self._local_changes,
+                                    cur_id_part,
+                                    ns_value)
         isc.cc.data.set(self._local_changes, identifier, value)
 
     def _get_list_items(self, item_name):
diff --git a/src/lib/python/isc/config/tests/.gitignore b/src/lib/python/isc/config/tests/.gitignore
new file mode 100644
index 0000000..52a9c5e
--- /dev/null
+++ b/src/lib/python/isc/config/tests/.gitignore
@@ -0,0 +1 @@
+/config_test
diff --git a/src/lib/python/isc/config/tests/ccsession_test.py b/src/lib/python/isc/config/tests/ccsession_test.py
index df39550..d1060bf 100644
--- a/src/lib/python/isc/config/tests/ccsession_test.py
+++ b/src/lib/python/isc/config/tests/ccsession_test.py
@@ -488,45 +488,6 @@ class TestModuleCCSession(unittest.TestCase):
         self.assertEqual({'result': [0]},
                          fake_session.get_message('Spec2', None))
  
-    def test_check_command_without_recvmsg_remote_module(self):
-        "copied from test_check_command3"
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
-        print(fake_session.message_queue)
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
-        env = { 'group':'Spec2', 'from':None }
-        self.assertEqual(len(fake_session.message_queue), 0)
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual(len(fake_session.message_queue), 0)
- 
-    def test_check_command_without_recvmsg_remote_module2(self):
-        "copied from test_check_command3"
-        fake_session = FakeModuleCCSession()
-        mccs = self.create_session("spec1.spec", None, None, fake_session)
-        mccs.set_config_handler(self.my_config_handler_ok)
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
-        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
-                         fake_session.get_message('ConfigManager', None))
-        self.assertEqual(len(fake_session.message_queue), 0)
-
-        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec3': { 'item1': 2 }})
-        env = { 'group':'Spec3', 'from':None }
-        self.assertEqual(len(fake_session.message_queue), 0)
-        mccs.check_command_without_recvmsg(cmd, env)
-        self.assertEqual(len(fake_session.message_queue), 0)
- 
     def test_check_command_block_timeout(self):
         """Check it works if session has timeout and it sets it back."""
         def cmd_check(mccs, session):
@@ -554,16 +515,65 @@ class TestModuleCCSession(unittest.TestCase):
         mccs.set_command_handler(self.my_command_handler_ok)
         self.assertRaises(WouldBlockForever, lambda: mccs.check_command(False))
 
-    def test_remote_module(self):
+    # Now there's a group of tests testing both add_remote_config and
+    # add_remote_config_by_name. Since they are almost the same (they differ
+    # just in the parameter and that the second one asks one more question over
+    # the bus), the actual test code is shared.
+    #
+    # These three functions are helper functions to easy up the writing of them.
+    # To write a test, there need to be 3 functions. First, the function that
+    # does the actual test. It looks like:
+    # def _internal_test(self, function_lambda, param, fill_other_messages):
+    #
+    # The function_lambda provides the tested function if called on the
+    # ccsession. The param is the parameter to pass to the function (either
+    # the module name or the spec file name. The fill_other_messages fills
+    # needed messages (the answer containing the module spec in case of add by
+    # name, no messages in the case of adding by spec file) into the fake bus.
+    # So, the code would look like:
+    #
+    # * Create the fake session and tested ccsession object
+    # * function = function_lambda(ccsession object)
+    # * fill_other_messages(fake session)
+    # * Fill in answer to the get_module_config command
+    # * Test by calling function(param)
+    #
+    # Then you need two wrappers that do launch the tests. There are helpers
+    # for that, so you can just call:
+    # def test_by_spec(self)
+    #     self._common_remote_module_test(self._internal_test)
+    # def test_by_name(self)
+    #     self._common_remote_module_by_name_test(self._internal_test)
+    def _common_remote_module_test(self, internal_test):
+        internal_test(lambda ccs: ccs.add_remote_config,
+                      self.spec_file("spec2.spec"),
+                      lambda session: None)
+
+    def _prepare_spec_message(self, session, spec_name):
+        # It could have been one command, but the line would be way too long
+        # to even split it
+        spec_file = self.spec_file(spec_name)
+        spec = isc.config.module_spec_from_file(spec_file)
+        session.group_sendmsg({'result': [0, spec.get_full_spec()]}, "Spec1")
+
+    def _common_remote_module_by_name_test(self, internal_test):
+        internal_test(lambda ccs: ccs.add_remote_config_by_name, "Spec2",
+                      lambda session: self._prepare_spec_message(session,
+                                                                 "spec2.spec"))
+
+    def _internal_remote_module(self, function_lambda, parameter,
+                                fill_other_messages):
         fake_session = FakeModuleCCSession()
         mccs = self.create_session("spec1.spec", None, None, fake_session)
         mccs.remove_remote_config("Spec2")
+        function = function_lambda(mccs)
 
         self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
 
         self.assertFalse("Spec2" in fake_session.subscriptions)
+        fill_other_messages(fake_session)
         fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        rmodname = function(parameter)
         self.assertTrue("Spec2" in fake_session.subscriptions)
         self.assertEqual("Spec2", rmodname)
         self.assertRaises(isc.cc.data.DataNotFoundError, mccs.get_remote_config_value, rmodname, "asdf")
@@ -575,36 +585,77 @@ class TestModuleCCSession(unittest.TestCase):
         self.assertFalse("Spec2" in fake_session.subscriptions)
         self.assertRaises(ModuleCCSessionError, mccs.get_remote_config_value, "Spec2", "item1")
 
-        # test if unsubscription is alse sent when object is deleted
+        # test if unsubscription is also sent when object is deleted
+        fill_other_messages(fake_session)
         fake_session.group_sendmsg({'result' : [0]}, 'Spec2')
-        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        rmodname = function(parameter)
         self.assertTrue("Spec2" in fake_session.subscriptions)
         mccs = None
+        function = None
         self.assertFalse("Spec2" in fake_session.subscriptions)
 
-    def test_remote_module_with_custom_config(self):
+    def test_remote_module(self):
+        """
+        Test we can add a remote config and get the configuration.
+        Remote module specified by the spec file name.
+        """
+        self._common_remote_module_test(self._internal_remote_module)
+
+    def test_remote_module_by_name(self):
+        """
+        Test we can add a remote config and get the configuration.
+        Remote module specified its name.
+        """
+        self._common_remote_module_by_name_test(self._internal_remote_module)
+
+    def _internal_remote_module_with_custom_config(self, function_lambda,
+                                                   parameter,
+                                                   fill_other_messages):
         fake_session = FakeModuleCCSession()
         mccs = self.create_session("spec1.spec", None, None, fake_session)
-        # override the default config value for "item1".  add_remote_config()
-        # should incorporate the overridden value, and we should be abel to
+        function = function_lambda(mccs)
+        # override the default config value for "item1". add_remote_config[_by_name]()
+        # should incorporate the overridden value, and we should be able to
         # get it via get_remote_config_value().
+        fill_other_messages(fake_session)
         fake_session.group_sendmsg({'result': [0, {"item1": 10}]}, 'Spec2')
-        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        rmodname = function(parameter)
         value, default = mccs.get_remote_config_value(rmodname, "item1")
         self.assertEqual(10, value)
         self.assertEqual(False, default)
 
-    def test_ignore_command_remote_module(self):
+    def test_remote_module_with_custom_config(self):
+        """
+        Test the config of module will load non-default values on
+        initialization.
+        Remote module specified by the spec file name.
+        """
+        self._common_remote_module_test(
+            self._internal_remote_module_with_custom_config)
+
+    def test_remote_module_by_name_with_custom_config(self):
+        """
+        Test the config of module will load non-default values on
+        initialization.
+        Remote module its name.
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_remote_module_with_custom_config)
+
+    def _internal_ignore_command_remote_module(self, function_lambda, param,
+                                               fill_other_messages):
         # Create a Spec1 module and subscribe to remote config for Spec2
         fake_session = FakeModuleCCSession()
         mccs = self.create_session("spec1.spec", None, None, fake_session)
         mccs.set_command_handler(self.my_command_handler_ok)
+        function = function_lambda(mccs)
+        fill_other_messages(fake_session)
         fake_session.group_sendmsg(None, 'Spec2')
-        rmodname = mccs.add_remote_config(self.spec_file("spec2.spec"))
+        rmodname = function(param)
 
-        # remove the 'get config' from the queue
-        self.assertEqual(len(fake_session.message_queue), 1)
-        fake_session.get_message("ConfigManager")
+        # remove the commands from queue
+        while len(fake_session.message_queue) > 0:
+            fake_session.get_message("ConfigManager")
 
         # check if the command for the module itself is received
         cmd = isc.config.ccsession.create_command("just_some_command", { 'foo': 'a' })
@@ -622,6 +673,174 @@ class TestModuleCCSession(unittest.TestCase):
         mccs.check_command()
         self.assertEqual(len(fake_session.message_queue), 0)
 
+    def test_ignore_commant_remote_module(self):
+        """
+        Test that commands for remote modules aren't handled.
+        Remote module specified by the spec file name.
+        """
+        self._common_remote_module_test(
+            self._internal_ignore_command_remote_module)
+
+    def test_ignore_commant_remote_module_by_name(self):
+        """
+        Test that commands for remote modules aren't handled.
+        Remote module specified by its name.
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_ignore_command_remote_module)
+
+    def _internal_check_command_without_recvmsg_remote_module(self,
+                                                              function_lambda,
+                                                              param,
+                                                              fill_other_messages):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        function = function_lambda(mccs)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        fill_other_messages(fake_session)
+        fake_session.group_sendmsg(None, 'Spec2')
+        rmodname = function(param)
+        if (len(fake_session.message_queue) == 2):
+            self.assertEqual({'command': ['get_module_spec',
+                                          {'module_name': 'Spec2'}]},
+                             fake_session.get_message('ConfigManager', None))
+        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+                         fake_session.get_message('ConfigManager', None))
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec2': { 'item1': 2 }})
+        env = { 'group':'Spec2', 'from':None }
+        self.assertEqual(len(fake_session.message_queue), 0)
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+    def test_check_command_without_recvmsg_remote_module(self):
+        """
+        Test updates on remote module.
+        The remote module is specified by the spec file name.
+        """
+        self._common_remote_module_test(
+            self._internal_check_command_without_recvmsg_remote_module)
+
+    def test_check_command_without_recvmsg_remote_module_by_name(self):
+        """
+        Test updates on remote module.
+        The remote module is specified by its name.
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_check_command_without_recvmsg_remote_module)
+
+    def _internal_check_command_without_recvmsg_remote_module2(self,
+                                                               function_lambda,
+                                                               param,
+                                                               fill_other_messages):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        function = function_lambda(mccs)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        fill_other_messages(fake_session)
+        fake_session.group_sendmsg(None, 'Spec2')
+        rmodname = function(param)
+        if (len(fake_session.message_queue) == 2):
+            self.assertEqual({'command': ['get_module_spec',
+                                          {'module_name': 'Spec2'}]},
+                             fake_session.get_message('ConfigManager', None))
+        self.assertEqual({'command': ['get_config', {'module_name': 'Spec2'}]},
+                         fake_session.get_message('ConfigManager', None))
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+        cmd = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_CONFIG_UPDATE, { 'Spec3': { 'item1': 2 }})
+        env = { 'group':'Spec3', 'from':None }
+        self.assertEqual(len(fake_session.message_queue), 0)
+        mccs.check_command_without_recvmsg(cmd, env)
+        self.assertEqual(len(fake_session.message_queue), 0)
+
+    def test_check_command_without_recvmsg_remote_module2(self):
+        """
+        Test updates on remote module.
+        The remote module is specified by the spec file name.
+        """
+        self._common_remote_module_test(
+            self._internal_check_command_without_recvmsg_remote_module2)
+
+    def test_check_command_without_recvmsg_remote_module_by_name2(self):
+        """
+        Test updates on remote module.
+        The remote module is specified by its name.
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_check_command_without_recvmsg_remote_module2)
+
+    def _internal_remote_module_bad_config(self, function_lambda, parameter,
+                                           fill_other_messages):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        function = function_lambda(mccs)
+        # Provide wrong config data. It should be rejected.
+        fill_other_messages(fake_session)
+        fake_session.group_sendmsg({'result': [0, {"bad_item": -1}]}, 'Spec2')
+        self.assertRaises(isc.config.ModuleCCSessionError,
+                          function, parameter)
+
+    def test_remote_module_bad_config(self):
+        """
+        Test the remote module rejects bad config data.
+        """
+        self._common_remote_module_test(
+            self._internal_remote_module_bad_config)
+
+    def test_remote_module_by_name_bad_config(self):
+        """
+        Test the remote module rejects bad config data.
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_remote_module_bad_config)
+
+    def _internal_remote_module_error_response(self, function_lambda,
+                                               parameter, fill_other_messages):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        function = function_lambda(mccs)
+        # Provide wrong config data. It should be rejected.
+        fill_other_messages(fake_session)
+        fake_session.group_sendmsg({'result': [1, "An error, and I mean it!"]},
+                                   'Spec2')
+        self.assertRaises(isc.config.ModuleCCSessionError,
+                          function, parameter)
+
+    def test_remote_module_bad_config(self):
+        """
+        Test the remote module complains if there's an error response."
+        """
+        self._common_remote_module_test(
+            self._internal_remote_module_error_response)
+
+    def test_remote_module_by_name_bad_config(self):
+        """
+        Test the remote module complains if there's an error response."
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_remote_module_error_response)
+
+    def test_remote_module_bad_config(self):
+        """
+        Test the remote module rejects bad config data.
+        """
+        self._common_remote_module_by_name_test(
+            self._internal_remote_module_bad_config)
+
+    def test_module_name_mismatch(self):
+        fake_session = FakeModuleCCSession()
+        mccs = self.create_session("spec1.spec", None, None, fake_session)
+        mccs.set_config_handler(self.my_config_handler_ok)
+        self._prepare_spec_message(fake_session, 'spec1.spec')
+        self.assertRaises(isc.config.ModuleCCSessionError,
+                          mccs.add_remote_config_by_name, "Spec2")
+
     def test_logconfig_handler(self):
         # test whether default_logconfig_handler reacts nicely to
         # bad data. We assume the actual logger output is tested
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index 7fe8212..891a7d7 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -74,6 +74,60 @@ class TestConfigManagerData(unittest.TestCase):
         self.assertEqual(self.config_manager_data, new_config)
         os.remove(output_file_name)
 
+    def check_existence(self, files, should_exist=[], should_not_exist=[]):
+        """Helper function for test_rename_config_file.
+           Arguments:
+           files: array of file names to check.
+           should_exist: array of indices, the files in 'files' with these
+                         indices should exist.
+           should_not_exist: array of indices, the files in 'files' with
+                             these indices should not exist."""
+        for n in should_exist:
+            self.assertTrue(os.path.exists(files[n]))
+        for n in should_not_exist:
+            self.assertFalse(os.path.exists(files[n]))
+
+    def test_rename_config_file(self):
+        # test file names, put in array for easy cleanup
+        filenames = [ "b10-config-rename-test",
+                      "b10-config-rename-test.bak",
+                      "b10-config-rename-test.bak.1",
+                      "b10-config-rename-test.bak.2" ]
+
+        for filename in filenames:
+            if os.path.exists(filename):
+                os.remove(filename)
+
+        # The original does not exist, so the new one should not be created
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [], [0, 1, 2, 3])
+
+        # now create a file to rename, and call rename again
+        self.config_manager_data.write_to_file(filenames[0])
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [1], [0, 2, 3])
+
+        # If backup already exists, give it a new name automatically
+        self.config_manager_data.write_to_file(filenames[0])
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [1, 2], [0, 3])
+
+        # If backup already exists, give it a new name automatically with
+        # increasing postfix
+        self.config_manager_data.write_to_file(filenames[0])
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [1, 2, 3], [0])
+
+        # Test with explicit renamed file argument
+        self.config_manager_data.rename_config_file(filenames[1],
+                                                    filenames[0])
+        self.check_existence(filenames, [0, 2, 3], [1])
+
+        # clean up again to be nice
+        for filename in filenames:
+            if os.path.exists(filename):
+                os.remove(filename)
+
     def test_equality(self):
         # tests the __eq__ function. Equality is only defined
         # by equality of the .data element. If data_path or db_filename
@@ -570,5 +624,6 @@ if __name__ == '__main__':
     if not 'CONFIG_TESTDATA_PATH' in os.environ or not 'CONFIG_WR_TESTDATA_PATH' in os.environ:
         print("You need to set the environment variable CONFIG_TESTDATA_PATH and CONFIG_WR_TESTDATA_PATH to point to the directory containing the test data files")
         exit(1)
+    isc.log.init("unittests")
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
-
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 3638f05..221ffa6 100644
--- a/src/lib/python/isc/config/tests/config_data_test.py
+++ b/src/lib/python/isc/config/tests/config_data_test.py
@@ -157,6 +157,7 @@ class TestConfigData(unittest.TestCase):
         self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ "a", "b" ])
         self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, [ "1", "b" ])
         self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, { "a": 1 })
+        self.assertRaises(isc.cc.data.DataTypeError, convert_type, spec_part, "\"{ \"a\": 1 }\"")
 
         spec_part = find_spec_part(config_spec, "value7")
         self.assertEqual(['1', '2'], convert_type(spec_part, '1, 2'))
@@ -185,6 +186,10 @@ class TestConfigData(unittest.TestCase):
         spec_part = find_spec_part(config_spec, "item6/value1")
         self.assertEqual({'item_name': 'value1', 'item_type': 'string', 'item_optional': True, 'item_default': 'default'}, spec_part)
 
+        # make sure the returned data is a copy
+        spec_part['item_default'] = 'foo'
+        self.assertNotEqual(spec_part, find_spec_part(config_spec, "item6/value1"))
+
     def test_find_spec_part_lists(self):
         # A few specific tests for list data
         module_spec = isc.config.module_spec_from_file(self.data_path +
@@ -419,7 +424,14 @@ class TestMultiConfigData(unittest.TestCase):
         self.mcd.set_value("Spec2/item1", 2)
         local_changes = self.mcd.get_local_changes()
         self.assertEqual({"Spec2": { "item1": 2}}, local_changes)
-        
+
+    def test_set_local_changes(self):
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
+        self.mcd.set_specification(module_spec)
+        self.assertEqual({}, self.mcd.get_local_changes())
+        new_local_changes = {"Spec2": { "item1": 2}}
+        self.mcd.set_local_changes(new_local_changes)
+        self.assertEqual(new_local_changes, self.mcd.get_local_changes())
 
     def test_clear_local_changes(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
@@ -584,8 +596,10 @@ class TestMultiConfigData(unittest.TestCase):
 
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec24.spec")
         self.mcd.set_specification(module_spec)
-        self.assertRaises(isc.cc.data.DataNotFoundError,
-                          self.mcd.get_value_maps, "/Spec24/item", 4)
+        # optional list item that is not set should return as empty list
+        maps = self.mcd.get_value_maps("/Spec24/item", 4)
+        self.assertEqual([{'default': False, 'type': 'list', 'name': 'Spec24/item', 'value': [], 'modified': False}], maps)
+
         self.mcd._set_current_config({ "Spec24": { "item": [] } })
         maps = self.mcd.get_value_maps("/Spec24/item")
         self.assertEqual([{'default': False, 'modified': False, 'name': 'Spec24/item', 'type': 'list', 'value': []}], maps)
@@ -617,6 +631,16 @@ class TestMultiConfigData(unittest.TestCase):
         maps = self.mcd.get_value_maps("/Spec22/value9/")
         self.assertEqual(expected, maps)
 
+        # A slash at the end should not produce different output with
+        # indices too
+        expected2 = [{'default': True,
+                      'type': 'integer',
+                      'name': 'Spec22/value5[1]',
+                      'value': 'b',
+                      'modified': False}]
+        maps = self.mcd.get_value_maps("/Spec22/value5[1]/")
+        self.assertEqual(expected2, maps)
+
     def test_get_value_maps_named_set(self):
         module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec")
         self.mcd.set_specification(module_spec)
@@ -656,7 +680,38 @@ class TestMultiConfigData(unittest.TestCase):
         self.assertEqual(MultiConfigData.LOCAL, status)
 
         self.assertRaises(isc.cc.data.DataTypeError, self.mcd.set_value, "Spec2/item5[a]", "asdf")
-        
+
+
+    def test_unset(self):
+        """
+        Test the unset command works.
+        """
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec2.spec")
+        self.mcd.set_specification(module_spec)
+        self.mcd.set_specification(module_spec)
+        value, status = self.mcd.get_value("Spec2/item1")
+        # This is the default first
+        self.assertEqual(1, value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+        # Unseting a default item does nothing.
+        self.mcd.unset("Spec2/item1")
+        value, status = self.mcd.get_value("Spec2/item1")
+        # This should be the default
+        self.assertEqual(1, value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+        # Set it to something else
+        self.mcd.set_value("Spec2/item1", 42)
+        value, status = self.mcd.get_value("Spec2/item1")
+        self.assertEqual(42, value)
+        self.assertEqual(MultiConfigData.LOCAL, status)
+        # Try to unset it
+        self.mcd.unset("Spec2/item1")
+        value, status = self.mcd.get_value("Spec2/item1")
+        # This should be the default
+        self.assertEqual(1, value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+        # Unset a nonexisting item. Should raise.
+        self.assertRaises(isc.cc.data.DataNotFoundError, self.mcd.unset, "Spec2/doesnotexist")
 
     def test_get_config_item_list(self):
         config_items = self.mcd.get_config_item_list()
@@ -678,6 +733,12 @@ class TestMultiConfigData(unittest.TestCase):
         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)
 
+    def test_is_named_set(self):
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + "spec32.spec")
+        self.mcd.set_specification(module_spec)
+        spec_part = self.mcd.find_spec_part("Spec32/named_set_item")
+        self.assertTrue(spec_part_is_named_set(spec_part))
+
     def test_get_config_item_list_named_set(self):
         config_items = self.mcd.get_config_item_list()
         self.assertEqual([], config_items)
@@ -696,6 +757,20 @@ class TestMultiConfigData(unittest.TestCase):
                           'Spec32/named_set_item/bbbb',
                          ], 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
+        module_spec = isc.config.module_spec_from_file(self.data_path + os.sep + 'spec32.spec')
+        self.mcd.set_specification(module_spec)
+        value, status = self.mcd.get_value('Spec32/named_set_item')
+        self.assertEqual({'a': 1, 'b': 2}, value)
+        self.assertEqual(MultiConfigData.DEFAULT, status)
+
+        self.mcd.set_value('Spec32/named_set_item/b', 3)
+        value, status = self.mcd.get_value('Spec32/named_set_item')
+        self.assertEqual({'a': 1, 'b': 3}, value)
+        self.assertEqual(MultiConfigData.LOCAL, status)
+
 if __name__ == '__main__':
     unittest.main()
 
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index 47f3dbc..1d862db 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -22,7 +22,7 @@ datasrc_la_SOURCES += journal_reader_python.cc journal_reader_python.h
 datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 datasrc_la_LDFLAGS = $(PYTHON_LDFLAGS)
-datasrc_la_LDFLAGS += -module
+datasrc_la_LDFLAGS += -module -avoid-version
 datasrc_la_LIBADD = $(top_builddir)/src/lib/datasrc/libdatasrc.la
 datasrc_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
 datasrc_la_LIBADD += $(top_builddir)/src/lib/dns/python/libpydnspp.la
diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc
index 8ee06b7..f31d10a 100644
--- a/src/lib/python/isc/datasrc/datasrc.cc
+++ b/src/lib/python/isc/datasrc/datasrc.cc
@@ -233,6 +233,7 @@ initModulePart_ZoneJournalReader(PyObject* mod) {
 }
 
 PyObject* po_DataSourceError;
+PyObject* po_OutOfZone;
 PyObject* po_NotImplemented;
 
 PyModuleDef iscDataSrc = {
@@ -287,6 +288,8 @@ PyInit_datasrc(void) {
         po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL,
                                                 NULL);
         PyObjectContainer(po_DataSourceError).installToModule(mod, "Error");
+        po_OutOfZone = PyErr_NewException("isc.datasrc.OutOfZone", NULL, NULL);
+        PyObjectContainer(po_OutOfZone).installToModule(mod, "OutOfZone");
         po_NotImplemented = PyErr_NewException("isc.datasrc.NotImplemented",
                                                NULL, NULL);
         PyObjectContainer(po_NotImplemented).installToModule(mod,
diff --git a/src/lib/python/isc/datasrc/finder_inc.cc b/src/lib/python/isc/datasrc/finder_inc.cc
index c063c44..7caa144 100644
--- a/src/lib/python/isc/datasrc/finder_inc.cc
+++ b/src/lib/python/isc/datasrc/finder_inc.cc
@@ -99,10 +99,9 @@ Their semantics is as follows (they are or bit-field):\n\
   of the non existence of any matching wildcard or non existence of an\n\
   exact match when a wildcard match is found.\n\
 \n\
-In general, name is expected to be included in the zone, that is, it\n\
-should be equal to or a subdomain of the zone origin. Otherwise this\n\
-method will return NXDOMAIN with an empty RRset. But such a case\n\
-should rather be considered a caller's bug.\n\
+Name is expected to be included in the zone, that is, it\n\
+should be equal to or a subdomain of the zone origin. Otherwise an\n\
+OutOfZoneFind exception is raised.\n\
 \n\
 Note: For this reason it's probably better to throw an exception than\n\
 returning NXDOMAIN. This point should be revisited in a near future\n\
diff --git a/src/lib/python/isc/datasrc/finder_python.cc b/src/lib/python/isc/datasrc/finder_python.cc
index f4b4ceb..ed05fdb 100644
--- a/src/lib/python/isc/datasrc/finder_python.cc
+++ b/src/lib/python/isc/datasrc/finder_python.cc
@@ -47,15 +47,15 @@ using namespace isc::datasrc::python;
 
 namespace  {
 ZoneFinder::FindResultFlags
-getFindResultFlags(const ZoneFinder::FindResult& result) {
+getFindResultFlags(const ZoneFinder::Context& context) {
     ZoneFinder::FindResultFlags result_flags = ZoneFinder::RESULT_DEFAULT;
-    if (result.isWildcard()) {
+    if (context.isWildcard()) {
         result_flags = result_flags | ZoneFinder::RESULT_WILDCARD;
     }
-    if (result.isNSECSigned()) {
+    if (context.isNSECSigned()) {
         result_flags = result_flags | ZoneFinder::RESULT_NSEC_SIGNED;
     }
-    if (result.isNSEC3Signed()) {
+    if (context.isNSEC3Signed()) {
         result_flags = result_flags | ZoneFinder::RESULT_NSEC3_SIGNED;
     }
     return (result_flags);
@@ -83,13 +83,13 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
         try {
             ZoneFinder::FindOptions options =
                 static_cast<ZoneFinder::FindOptions>(options_int);
-            const ZoneFinder::FindResult find_result(
+            ConstZoneFinderContextPtr find_ctx(
                 finder->find(PyName_ToName(name), PyRRType_ToRRType(rrtype),
                              options));
-            const ZoneFinder::Result r = find_result.code;
-            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            const ZoneFinder::Result r = find_ctx->code;
+            isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
             ZoneFinder::FindResultFlags result_flags =
-                getFindResultFlags(find_result);
+                getFindResultFlags(*find_ctx);
             if (rrsp) {
                 // Use N instead of O so the refcount isn't increased twice
                 return (Py_BuildValue("INI", r, createRRsetObject(*rrsp),
@@ -97,6 +97,9 @@ PyObject* ZoneFinder_helper(ZoneFinder* finder, PyObject* args) {
             } else {
                 return (Py_BuildValue("IOI", r, Py_None, result_flags));
             }
+        } catch (const OutOfZone& ooz) {
+            PyErr_SetString(getDataSourceException("OutOfZone"), ooz.what());
+            return (NULL);
         } catch (const DataSourceError& dse) {
             PyErr_SetString(getDataSourceException("Error"), dse.what());
             return (NULL);
@@ -127,12 +130,12 @@ PyObject* ZoneFinder_helper_all(ZoneFinder* finder, PyObject* args) {
             ZoneFinder::FindOptions options =
                 static_cast<ZoneFinder::FindOptions>(options_int);
             std::vector<isc::dns::ConstRRsetPtr> target;
-            const ZoneFinder::FindResult find_result(
+            ConstZoneFinderContextPtr find_ctx(
                 finder->findAll(PyName_ToName(name), target, options));
-            const ZoneFinder::Result r = find_result.code;
-            isc::dns::ConstRRsetPtr rrsp = find_result.rrset;
+            const ZoneFinder::Result r = find_ctx->code;
+            isc::dns::ConstRRsetPtr rrsp = find_ctx->rrset;
             ZoneFinder::FindResultFlags result_flags =
-                getFindResultFlags(find_result);
+                getFindResultFlags(*find_ctx);
             if (r == ZoneFinder::SUCCESS) {
                 // Copy all the RRsets to the result list
                 PyObjectContainer list_container(PyList_New(target.size()));
diff --git a/src/lib/python/isc/datasrc/sqlite3_ds.py b/src/lib/python/isc/datasrc/sqlite3_ds.py
index daa12fc..f9b47c0 100644
--- a/src/lib/python/isc/datasrc/sqlite3_ds.py
+++ b/src/lib/python/isc/datasrc/sqlite3_ds.py
@@ -23,6 +23,10 @@ RR_NAME_INDEX = 2
 RR_TTL_INDEX = 4
 RR_RDATA_INDEX = 7
 
+# Current major and minor versions of schema
+SCHEMA_MAJOR_VERSION = 2
+SCHEMA_MINOR_VERSION = 0
+
 class Sqlite3DSError(Exception):
     """ Define exceptions."""
     pass
@@ -47,40 +51,46 @@ def create(cur):
         cur.execute("SELECT version FROM schema_version")
         row = cur.fetchone()
     except sqlite3.OperationalError:
-        cur.execute("CREATE TABLE schema_version (version INTEGER NOT NULL)")
-        cur.execute("INSERT INTO schema_version VALUES (1)")
+        cur.execute("""CREATE TABLE schema_version (version INTEGER NOT NULL,
+                    minor INTEGER NOT NULL DEFAULT 0)""")
+        cur.execute("INSERT INTO schema_version VALUES (" +
+                    str(SCHEMA_MAJOR_VERSION) + ", " +
+                    str(SCHEMA_MINOR_VERSION) + ")")
         cur.execute("""CREATE TABLE zones (id INTEGER PRIMARY KEY,
-                    name STRING NOT NULL COLLATE NOCASE,
-                    rdclass STRING NOT NULL COLLATE NOCASE DEFAULT 'IN',
+                    name TEXT NOT NULL COLLATE NOCASE,
+                    rdclass TEXT NOT NULL COLLATE NOCASE DEFAULT 'IN',
                     dnssec BOOLEAN NOT NULL DEFAULT 0)""")
         cur.execute("CREATE INDEX zones_byname ON zones (name)")
         cur.execute("""CREATE TABLE records (id INTEGER PRIMARY KEY,
                     zone_id INTEGER NOT NULL,
-                    name STRING NOT NULL COLLATE NOCASE,
-                    rname STRING NOT NULL COLLATE NOCASE,
+                    name TEXT NOT NULL COLLATE NOCASE,
+                    rname TEXT NOT NULL COLLATE NOCASE,
                     ttl INTEGER NOT NULL,
-                    rdtype STRING NOT NULL COLLATE NOCASE,
-                    sigtype STRING COLLATE NOCASE,
-                    rdata STRING NOT NULL)""")
+                    rdtype TEXT NOT NULL COLLATE NOCASE,
+                    sigtype TEXT COLLATE NOCASE,
+                    rdata TEXT NOT NULL)""")
         cur.execute("CREATE INDEX records_byname ON records (name)")
         cur.execute("CREATE INDEX records_byrname ON records (rname)")
+        cur.execute("""CREATE INDEX records_bytype_and_rname ON records
+                       (rdtype, rname)""")
         cur.execute("""CREATE TABLE nsec3 (id INTEGER PRIMARY KEY,
                     zone_id INTEGER NOT NULL,
-                    hash STRING NOT NULL COLLATE NOCASE,
-                    owner STRING NOT NULL COLLATE NOCASE,
+                    hash TEXT NOT NULL COLLATE NOCASE,
+                    owner TEXT NOT NULL COLLATE NOCASE,
                     ttl INTEGER NOT NULL,
-                    rdtype STRING NOT NULL COLLATE NOCASE,
-                    rdata STRING NOT NULL)""")
+                    rdtype TEXT NOT NULL COLLATE NOCASE,
+                    rdata TEXT NOT NULL)""")
         cur.execute("CREATE INDEX nsec3_byhash ON nsec3 (hash)")
         cur.execute("""CREATE TABLE diffs (id INTEGER PRIMARY KEY,
                     zone_id INTEGER NOT NULL,
                     version INTEGER NOT NULL,
                     operation INTEGER NOT NULL,
-                    name STRING NOT NULL COLLATE NOCASE,
-                    rrtype STRING NOT NULL COLLATE NOCASE,
+                    name TEXT NOT NULL COLLATE NOCASE,
+                    rrtype TEXT NOT NULL COLLATE NOCASE,
                     ttl INTEGER NOT NULL,
-                    rdata STRING NOT NULL)""")
-        row = [1]
+                    rdata TEXT NOT NULL)""")
+        cur.execute("SELECT version FROM schema_version")
+        row = cur.fetchone()
     cur.execute("COMMIT TRANSACTION")
     return row
 
@@ -115,8 +125,9 @@ def open(dbfile, connect_timeout=5.0):
         row = create(cur)
         conn.isolation_level = iso_lvl
 
-    if row == None or row[0] != 1:
-        raise Sqlite3DSError("Bad database schema version")
+    if row == None or row[0] != SCHEMA_MAJOR_VERSION:
+        bad_version = "(unknown)" if row is None else str(row[0])
+        raise Sqlite3DSError("Bad database schema version: " + bad_version)
 
     return conn, cur
 
diff --git a/src/lib/python/isc/datasrc/tests/.gitignore b/src/lib/python/isc/datasrc/tests/.gitignore
new file mode 100644
index 0000000..58ea8cd
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/.gitignore
@@ -0,0 +1 @@
+/*.sqlite3.copied
diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am
index ab89b93..c996f2a 100644
--- a/src/lib/python/isc/datasrc/tests/Makefile.am
+++ b/src/lib/python/isc/datasrc/tests/Makefile.am
@@ -1,12 +1,14 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 # old tests, TODO remove or change to use new API?
-#PYTESTS = master_test.py sqlite3_ds_test.py
-PYTESTS =  datasrc_test.py
+#PYTESTS = master_test.py
+PYTESTS =  datasrc_test.py sqlite3_ds_test.py
 EXTRA_DIST = $(PYTESTS)
 
 EXTRA_DIST += testdata/brokendb.sqlite3
 EXTRA_DIST += testdata/example.com.sqlite3
-EXTRA_DIST += testdata/test.sqlite3.nodiffs
+EXTRA_DIST += testdata/newschema.sqlite3
+EXTRA_DIST += testdata/oldschema.sqlite3
+EXTRA_DIST += testdata/new_minor_schema.sqlite3
 CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
diff --git a/src/lib/python/isc/datasrc/tests/datasrc_test.py b/src/lib/python/isc/datasrc/tests/datasrc_test.py
index a6f8f16..c7bf6b4 100644
--- a/src/lib/python/isc/datasrc/tests/datasrc_test.py
+++ b/src/lib/python/isc/datasrc/tests/datasrc_test.py
@@ -262,16 +262,16 @@ class DataSrcClient(unittest.TestCase):
         rrets = dsc.get_iterator(isc.dns.Name("example.com"))
         # there are more than 80 RRs in this zone... let's just count them
         # (already did a full check of the smaller zone above)
-        self.assertEqual(55, len(list(rrets)))
+        # There are 40 non-RRSIG RRsets and 32 dinstinct RRSIGs.
+        self.assertEqual(72, len(list(rrets)))
 
         # same test, but now with explicit False argument for separate_rrs
         dsc = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
         rrets = dsc.get_iterator(isc.dns.Name("example.com"), False)
         # there are more than 80 RRs in this zone... let's just count them
         # (already did a full check of the smaller zone above)
-        self.assertEqual(55, len(list(rrets)))
+        self.assertEqual(72, len(list(rrets)))
 
-        # Count should be 71 if we request individual rrsets for differing ttls
         dsc = isc.datasrc.DataSourceClient("sqlite3", READ_ZONE_DB_CONFIG)
         rrets = dsc.get_iterator(isc.dns.Name("example.com"), True)
         # there are more than 80 RRs in this zone... let's just count them
@@ -379,11 +379,10 @@ class DataSrcClient(unittest.TestCase):
         self.assertEqual(finder.NXDOMAIN, result)
         self.assertEqual(None, rrset)
 
-        result, rrset, _ = finder.find(isc.dns.Name("www.some.other.domain"),
-                                       isc.dns.RRType.A(),
-                                       finder.FIND_DEFAULT)
-        self.assertEqual(finder.NXDOMAIN, result)
-        self.assertEqual(None, rrset)
+
+        self.assertRaises(isc.datasrc.OutOfZone, finder.find,
+                          isc.dns.Name("www.some.other.domain"),
+                          isc.dns.RRType.A())
 
         result, rrset, _ = finder.find(isc.dns.Name("www.example.com"),
                                        isc.dns.RRType.TXT(),
@@ -881,15 +880,6 @@ class JournalRead(unittest.TestCase):
         # ZoneJournalReader can only be constructed via a factory
         self.assertRaises(TypeError, ZoneJournalReader)
 
-    def test_journal_reader_old_schema(self):
-        # The database doesn't have a "diffs" table.
-        dbfile = TESTDATA_PATH + 'test.sqlite3.nodiffs'
-        client = isc.datasrc.DataSourceClient("sqlite3",
-                                              "{ \"database_file\": \"" + \
-                                                  dbfile + "\" }")
-        self.assertRaises(isc.datasrc.Error, client.get_journal_reader,
-                          self.zname, 0, 1)
-
 if __name__ == "__main__":
     isc.log.init("bind10")
     isc.log.resetUnitTestRootLogger()
diff --git a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
index 10c61cf..5604c32 100644
--- a/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
+++ b/src/lib/python/isc/datasrc/tests/sqlite3_ds_test.py
@@ -22,122 +22,18 @@ import sqlite3
 TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
 TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
 
-READ_ZONE_DB_FILE = TESTDATA_PATH + "example.com.sqlite3"
-BROKEN_DB_FILE = TESTDATA_PATH + "brokendb.sqlite3"
-WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "example.com.out.sqlite3"
-NEW_DB_FILE = TESTDATA_WRITE_PATH + "new_db.sqlite3"
-
-def example_reader():
-    my_zone = [
-        ("example.com.",    "3600",    "IN",  "SOA", "ns.example.com. admin.example.com. 1234 3600 1800 2419200 7200"),
-        ("example.com.",    "3600",    "IN",  "NS", "ns.example.com."),
-        ("ns.example.com.", "3600",    "IN",  "A", "192.0.2.1")
-    ]
-    for rr in my_zone:
-        yield rr
-
-def example_reader_nested():
-    # this iterator is used in the 'locked' test; it will cause
-    # the load() method to try and write to the same database
-    sqlite3_ds.load(WRITE_ZONE_DB_FILE,
-                    ".",
-                    example_reader)
-    return example_reader()
-
-class TestSqlite3_ds(unittest.TestCase):
-    def test_zone_exist(self):
-        # 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.
-        nodir = "/nodir/notexist"
-        self.assertRaises(sqlite3_ds.Sqlite3DSError,
-                          sqlite3_ds.zone_exist, "example.com", nodir)
-        # Open a broken database file
-        self.assertRaises(sqlite3_ds.Sqlite3DSError,
-                          sqlite3_ds.zone_exist, "example.com",
-                          BROKEN_DB_FILE)
-        self.assertTrue(sqlite3_ds.zone_exist("example.com.",
-                        READ_ZONE_DB_FILE))
-        self.assertFalse(sqlite3_ds.zone_exist("example.org.",
-                         READ_ZONE_DB_FILE))
-
-    def test_load_db(self):
-        sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
-
-    def test_locked_db(self):
-        # load it first to make sure it exists
-        sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
-
-        # and manually create a writing session as well
-        con = sqlite3.connect(WRITE_ZONE_DB_FILE);
-        cur = con.cursor()
-        cur.execute("delete from records")
-
-        self.assertRaises(sqlite3_ds.Sqlite3DSError,
-                          sqlite3_ds.load, WRITE_ZONE_DB_FILE, ".",
-                          example_reader)
-
-        con.rollback()
-
-        # and make sure lock does not stay
-        sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
-
-        # force locked db by nested loads
-        self.assertRaises(sqlite3_ds.Sqlite3DSError,
-                          sqlite3_ds.load, WRITE_ZONE_DB_FILE, ".",
-                          example_reader_nested)
-
-        # and make sure lock does not stay
-        sqlite3_ds.load(WRITE_ZONE_DB_FILE, ".", example_reader)
+DBFILE_NEWSCHEMA = TESTDATA_PATH + "/newschema.sqlite3";
+DBFILE_OLDSCHEMA = TESTDATA_PATH + "/oldschema.sqlite3";
+DBFILE_NEW_MINOR_SCHEMA = TESTDATA_PATH + "/new_minor_schema.sqlite3";
 
 class NewDBFile(unittest.TestCase):
-    def tearDown(self):
-        # remove the created database after every test
-        if (os.path.exists(NEW_DB_FILE)):
-            os.remove(NEW_DB_FILE)
-
-    def setUp(self):
-        # remove the created database before every test too, just
-        # in case a test got aborted half-way, and cleanup didn't occur
-        if (os.path.exists(NEW_DB_FILE)):
-            os.remove(NEW_DB_FILE)
-
-    def test_new_db(self):
-        self.assertFalse(os.path.exists(NEW_DB_FILE))
-        sqlite3_ds.open(NEW_DB_FILE)
-        self.assertTrue(os.path.exists(NEW_DB_FILE))
-
-    def test_new_db_locked(self):
-        self.assertFalse(os.path.exists(NEW_DB_FILE))
-        con = sqlite3.connect(NEW_DB_FILE);
-        con.isolation_level = None
-        cur = con.cursor()
-        cur.execute("BEGIN IMMEDIATE TRANSACTION")
-
-        # load should now fail, since the database is locked,
-        # and the open() call needs an exclusive lock
-        self.assertRaises(sqlite3.OperationalError,
-                          sqlite3_ds.open, NEW_DB_FILE, 0.1)
-
-        con.rollback()
-        cur.close()
-        con.close()
-        self.assertTrue(os.path.exists(NEW_DB_FILE))
-
-        # now that we closed our connection, load should work again
-        sqlite3_ds.open(NEW_DB_FILE)
-
-        # the database should now have been created, and a new load should
-        # not require an exclusive lock anymore, so we lock it again
-        con = sqlite3.connect(NEW_DB_FILE);
-        cur = con.cursor()
-        cur.execute("BEGIN IMMEDIATE TRANSACTION")
-        sqlite3_ds.open(NEW_DB_FILE, 0.1)
-        con.rollback()
-        cur.close()
-        con.close()
+    def test_different_version(self):
+        self.assertTrue(os.path.exists(DBFILE_NEWSCHEMA))
+        self.assertRaises(sqlite3_ds.Sqlite3DSError, sqlite3_ds.open,
+                          DBFILE_NEWSCHEMA)
+        self.assertRaises(sqlite3_ds.Sqlite3DSError, sqlite3_ds.open,
+                          DBFILE_OLDSCHEMA)
+        self.assertNotEqual(None, sqlite3_ds.open(DBFILE_NEW_MINOR_SCHEMA)[0])
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3
index 521cf31..9c71cb5 100644
Binary files a/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3 and b/src/lib/python/isc/datasrc/tests/testdata/example.com.sqlite3 differ
diff --git a/src/lib/python/isc/datasrc/tests/testdata/new_minor_schema.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/new_minor_schema.sqlite3
new file mode 100644
index 0000000..1542c20
Binary files /dev/null and b/src/lib/python/isc/datasrc/tests/testdata/new_minor_schema.sqlite3 differ
diff --git a/src/lib/python/isc/datasrc/tests/testdata/newschema.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/newschema.sqlite3
new file mode 100644
index 0000000..460cfa8
Binary files /dev/null and b/src/lib/python/isc/datasrc/tests/testdata/newschema.sqlite3 differ
diff --git a/src/lib/python/isc/datasrc/tests/testdata/oldschema.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/oldschema.sqlite3
new file mode 100644
index 0000000..b44c5eb
Binary files /dev/null and b/src/lib/python/isc/datasrc/tests/testdata/oldschema.sqlite3 differ
diff --git a/src/lib/python/isc/datasrc/tests/testdata/test.sqlite3.nodiffs b/src/lib/python/isc/datasrc/tests/testdata/test.sqlite3.nodiffs
deleted file mode 100644
index cc8cfc3..0000000
Binary files a/src/lib/python/isc/datasrc/tests/testdata/test.sqlite3.nodiffs and /dev/null differ
diff --git a/src/lib/python/isc/log/Makefile.am b/src/lib/python/isc/log/Makefile.am
index 5ff2c28..3658c17 100644
--- a/src/lib/python/isc/log/Makefile.am
+++ b/src/lib/python/isc/log/Makefile.am
@@ -13,7 +13,7 @@ log_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 # placed after -Wextra defined in AM_CXXFLAGS
 log_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 log_la_LDFLAGS = $(PYTHON_LDFLAGS)
-log_la_LDFLAGS += -module
+log_la_LDFLAGS += -module -avoid-version
 log_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
 log_la_LIBADD += $(top_builddir)/src/lib/cc/libcc.la
 log_la_LIBADD += $(top_builddir)/src/lib/config/libcfgclient.la
@@ -23,15 +23,6 @@ log_la_LIBADD += $(PYTHON_LIB)
 # This is not installed, it helps locate the module during tests
 EXTRA_DIST = __init__.py
 
-# We're going to abuse install-data-local for a pre-install check.
-# This is to be considered a short term hack and is expected to be removed
-# in a near future version.
-install-data-local:
-	if test -d @pyexecdir@/isc/log; then \
-		echo "@pyexecdir@/isc/log is deprecated, and will confuse newer versions.  Please (re)move it by hand."; \
-		exit 1; \
-	fi
-
 pytest:
 	$(SHELL) tests/log_test
 
diff --git a/src/lib/python/isc/log/tests/.gitignore b/src/lib/python/isc/log/tests/.gitignore
new file mode 100644
index 0000000..b9cf241
--- /dev/null
+++ b/src/lib/python/isc/log/tests/.gitignore
@@ -0,0 +1 @@
+/log_console.py
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
index 4b084cc..6b4be94 100644
--- a/src/lib/python/isc/log_messages/Makefile.am
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -13,6 +13,8 @@ EXTRA_DIST += cfgmgr_messages.py
 EXTRA_DIST += config_messages.py
 EXTRA_DIST += notify_out_messages.py
 EXTRA_DIST += libxfrin_messages.py
+EXTRA_DIST += server_common_messages.py
+EXTRA_DIST += dbutil_messages.py
 
 CLEANFILES = __init__.pyc
 CLEANFILES += bind10_messages.pyc
@@ -27,6 +29,8 @@ CLEANFILES += cfgmgr_messages.pyc
 CLEANFILES += config_messages.pyc
 CLEANFILES += notify_out_messages.pyc
 CLEANFILES += libxfrin_messages.pyc
+CLEANFILES += server_common_messages.pyc
+CLEANFILES += dbutil_messages.pyc
 
 CLEANDIRS = __pycache__
 
diff --git a/src/lib/python/isc/log_messages/dbutil_messages.py b/src/lib/python/isc/log_messages/dbutil_messages.py
new file mode 100644
index 0000000..c06dfef
--- /dev/null
+++ b/src/lib/python/isc/log_messages/dbutil_messages.py
@@ -0,0 +1 @@
+from work.dbutil_messages import *
diff --git a/src/lib/python/isc/log_messages/server_common_messages.py b/src/lib/python/isc/log_messages/server_common_messages.py
new file mode 100644
index 0000000..a491071
--- /dev/null
+++ b/src/lib/python/isc/log_messages/server_common_messages.py
@@ -0,0 +1 @@
+from work.server_common_messages import *
diff --git a/src/lib/python/isc/log_messages/work/.gitignore b/src/lib/python/isc/log_messages/work/.gitignore
new file mode 100644
index 0000000..05a7653
--- /dev/null
+++ b/src/lib/python/isc/log_messages/work/.gitignore
@@ -0,0 +1,2 @@
+/__init__.py
+/*_messages.py
diff --git a/src/lib/python/isc/log_messages/work/Makefile.am b/src/lib/python/isc/log_messages/work/Makefile.am
index 9bc5e0f..ad5ee0c 100644
--- a/src/lib/python/isc/log_messages/work/Makefile.am
+++ b/src/lib/python/isc/log_messages/work/Makefile.am
@@ -5,7 +5,7 @@ python_PYTHON = __init__.py
 
 pythondir = $(pyexecdir)/isc/log_messages/
 
-CLEANFILES = __init__.pyc
+CLEANFILES = __init__.pyc __init__.pyo
 CLEANDIRS = __pycache__
 
 clean-local:
diff --git a/src/lib/python/isc/notify/notify_out.py b/src/lib/python/isc/notify/notify_out.py
index 153a00a..bfa7167 100644
--- a/src/lib/python/isc/notify/notify_out.py
+++ b/src/lib/python/isc/notify/notify_out.py
@@ -34,7 +34,7 @@ logger = isc.log.Logger("notify_out")
 # initialized yet. see trac ticket #1103
 from isc.dns import *
 
-ZONE_NEW_DATA_READY_CMD = 'zone_new_data_ready'
+ZONE_NEW_DATA_READY_CMD = 'notify'
 _MAX_NOTIFY_NUM = 30
 _MAX_NOTIFY_TRY_NUM = 5
 _EVENT_NONE = 0
@@ -164,17 +164,19 @@ class NotifyOut:
         the only interface for class NotifyOut which can be called
         by other object.
           Internally, the function only set the zone's notify-reply
-        timeout to now, then notify message will be sent out. '''
+        timeout to now, then notify message will be sent out.
+        Returns False if the zone/class is not known, True if it is
+        (even if there are no slaves)'''
         if zone_name[len(zone_name) - 1] != '.':
             zone_name += '.'
 
         zone_id = (zone_name, zone_class)
         if zone_id not in self._notify_infos:
-            return
+            return False
 
         # Has no slave servers, skip it.
         if (len(self._notify_infos[zone_id].notify_slaves) <= 0):
-            return
+            return True
 
         with self._lock:
             if (self.notify_num >= _MAX_NOTIFY_NUM) or (zone_id in self._notifying_zones):
@@ -186,6 +188,7 @@ class NotifyOut:
                 self._notifying_zones.append(zone_id)
                 if not self._nonblock_event.isSet():
                     self._nonblock_event.set()
+        return True
 
     def _dispatcher(self, started_event):
         started_event.set() # Let the master know we are alive already
@@ -250,7 +253,9 @@ class NotifyOut:
         self._thread.join()
 
         # Clean up
+        self._write_sock.close()
         self._write_sock = None
+        self._read_sock.close()
         self._read_sock = None
         self._thread = None
 
@@ -273,12 +278,12 @@ class NotifyOut:
         # data sources.
         datasrc_config = '{ "database_file": "' + self._db_file + '"}'
         try:
-            result, finder = DataSourceClient('sqlite3',
-                                              datasrc_config).find_zone(
-                zone_name)
+            ds_client = DataSourceClient('sqlite3', datasrc_config)
         except isc.datasrc.Error as ex:
             logger.error(NOTIFY_OUT_DATASRC_ACCESS_FAILURE, ex)
             return []
+
+        result, finder = ds_client.find_zone(zone_name)
         if result is not DataSourceClient.SUCCESS:
             logger.error(NOTIFY_OUT_DATASRC_ZONE_NOT_FOUND,
                          format_zone_str(zone_name, zone_class))
@@ -302,13 +307,17 @@ class NotifyOut:
             ns_name = Name(ns_rdata.to_text())
             if soa_mname == ns_name:
                 continue
-            result, rrset, _ = finder.find(ns_name, RRType.A())
-            if result is finder.SUCCESS and rrset is not None:
-                addrs.extend([a.to_text() for a in rrset.get_rdata()])
-
-            result, rrset, _ = finder.find(ns_name, RRType.AAAA())
-            if result is finder.SUCCESS and rrset is not None:
-                addrs.extend([aaaa.to_text() for aaaa in rrset.get_rdata()])
+            ns_result, ns_finder = ds_client.find_zone(ns_name)
+            if ns_result is DataSourceClient.SUCCESS or \
+               ns_result is DataSourceClient.PARTIALMATCH:
+                result, rrset, _ = ns_finder.find(ns_name, RRType.A())
+                if result is ns_finder.SUCCESS and rrset is not None:
+                    addrs.extend([a.to_text() for a in rrset.get_rdata()])
+
+                result, rrset, _ = ns_finder.find(ns_name, RRType.AAAA())
+                if result is ns_finder.SUCCESS and rrset is not None:
+                    addrs.extend([aaaa.to_text()
+                                    for aaaa in rrset.get_rdata()])
 
         return addrs
 
diff --git a/src/lib/python/isc/notify/tests/.gitignore b/src/lib/python/isc/notify/tests/.gitignore
new file mode 100644
index 0000000..69f6d95
--- /dev/null
+++ b/src/lib/python/isc/notify/tests/.gitignore
@@ -0,0 +1 @@
+/notify_out_test
diff --git a/src/lib/python/isc/notify/tests/notify_out_test.py b/src/lib/python/isc/notify/tests/notify_out_test.py
index d64c203..1b3a4a1 100644
--- a/src/lib/python/isc/notify/tests/notify_out_test.py
+++ b/src/lib/python/isc/notify/tests/notify_out_test.py
@@ -114,38 +114,48 @@ class TestNotifyOut(unittest.TestCase):
         notify_out._MAX_NOTIFY_NUM = 2
 
         self._notify._nonblock_event.clear()
-        self._notify.send_notify('example.net')
+        self.assertTrue(self._notify.send_notify('example.net'))
         self.assertTrue(self._notify._nonblock_event.isSet())
         self.assertEqual(self._notify.notify_num, 1)
         self.assertEqual(self._notify._notifying_zones[0], ('example.net.', 'IN'))
 
-        self._notify.send_notify('example.com')
+        self.assertTrue(self._notify.send_notify('example.com'))
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(self._notify._notifying_zones[1], ('example.com.', 'IN'))
 
         # notify_num is equal to MAX_NOTIFY_NUM, append it to waiting_zones list.
         self._notify._nonblock_event.clear()
-        self._notify.send_notify('example.com', 'CH')
+        self.assertTrue(self._notify.send_notify('example.com', 'CH'))
         # add waiting zones won't set nonblock_event.
         self.assertFalse(self._notify._nonblock_event.isSet())
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(1, len(self._notify._waiting_zones))
 
         # zone_id is already in notifying_zones list, append it to waiting_zones list.
-        self._notify.send_notify('example.net')
+        self.assertTrue(self._notify.send_notify('example.net'))
         self.assertEqual(2, len(self._notify._waiting_zones))
         self.assertEqual(self._notify._waiting_zones[1], ('example.net.', 'IN'))
 
         # zone_id is already in waiting_zones list, skip it.
-        self._notify.send_notify('example.net')
+        self.assertTrue(self._notify.send_notify('example.net'))
         self.assertEqual(2, len(self._notify._waiting_zones))
 
         # has no slave masters, skip it.
-        self._notify.send_notify('example.org.', 'CH')
+        self.assertTrue(self._notify.send_notify('example.org.', 'CH'))
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(2, len(self._notify._waiting_zones))
 
-        self._notify.send_notify('example.org.')
+        self.assertTrue(self._notify.send_notify('example.org.'))
+        self.assertEqual(self._notify.notify_num, 2)
+        self.assertEqual(2, len(self._notify._waiting_zones))
+
+        # zone does not exist, should return False, and no change in other
+        # values
+        self.assertFalse(self._notify.send_notify('does.not.exist.'))
+        self.assertEqual(self._notify.notify_num, 2)
+        self.assertEqual(2, len(self._notify._waiting_zones))
+
+        self.assertFalse(self._notify.send_notify('example.net.', 'CH'))
         self.assertEqual(self._notify.notify_num, 2)
         self.assertEqual(2, len(self._notify._waiting_zones))
 
@@ -185,6 +195,11 @@ class TestNotifyOut(unittest.TestCase):
         # Now make one socket be readable
         self._notify._notify_infos[('example.net.', 'IN')].notify_timeout = time.time() + 10
         self._notify._notify_infos[('example.com.', 'IN')].notify_timeout = time.time() + 10
+
+        if self._notify._read_sock is not None:
+            self._notify._read_sock.close()
+        if self._notify._write_sock is not None:
+            self._notify._write_sock.close()
         self._notify._read_sock, self._notify._write_sock = socket.socketpair()
         self._notify._write_sock.send(SOCK_DATA)
         replied_zones, timeout_zones = self._notify._wait_for_notify_reply()
diff --git a/src/lib/python/isc/notify/tests/testdata/brokentest.sqlite3 b/src/lib/python/isc/notify/tests/testdata/brokentest.sqlite3
index 61e766c..10d64c1 100644
Binary files a/src/lib/python/isc/notify/tests/testdata/brokentest.sqlite3 and b/src/lib/python/isc/notify/tests/testdata/brokentest.sqlite3 differ
diff --git a/src/lib/python/isc/notify/tests/testdata/test.sqlite3 b/src/lib/python/isc/notify/tests/testdata/test.sqlite3
index e3cadb0..d659181 100644
Binary files a/src/lib/python/isc/notify/tests/testdata/test.sqlite3 and b/src/lib/python/isc/notify/tests/testdata/test.sqlite3 differ
diff --git a/src/lib/python/isc/server_common/Makefile.am b/src/lib/python/isc/server_common/Makefile.am
new file mode 100644
index 0000000..a9eca2e
--- /dev/null
+++ b/src/lib/python/isc/server_common/Makefile.am
@@ -0,0 +1,24 @@
+SUBDIRS = tests
+
+python_PYTHON = __init__.py tsig_keyring.py
+
+pythondir = $(pyexecdir)/isc/server_common
+
+BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
+
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.pyc
+
+CLEANDIRS = __pycache__
+
+EXTRA_DIST = server_common_messages.mes
+
+$(PYTHON_LOGMSGPKG_DIR)/work/server_common_messages.py : server_common_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/server_common_messages.mes
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/lib/python/isc/server_common/__init__.py b/src/lib/python/isc/server_common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/python/isc/server_common/server_common_messages.mes b/src/lib/python/isc/server_common/server_common_messages.mes
new file mode 100644
index 0000000..b32205c
--- /dev/null
+++ b/src/lib/python/isc/server_common/server_common_messages.mes
@@ -0,0 +1,36 @@
+# 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.
+
+# No namespace declaration - these constants go in the global namespace
+# of the config_messages python module.
+
+# since these messages are for the python server_common library, care must
+# be taken that names do not conflict with the messages from the c++
+# server_common library. A checker script should verify that, but we do not
+# have that at this moment. So when adding a message, make sure that
+# the name is not already used in src/lib/config/config_messages.mes
+
+% PYSERVER_COMMON_TSIG_KEYRING_DEINIT Deinitializing global TSIG keyring
+A debug message noting that the global TSIG keyring is being removed from
+memory. Most programs don't do that, they just exit, which is OK.
+
+% PYSERVER_COMMON_TSIG_KEYRING_INIT Initializing global TSIG keyring
+A debug message noting the TSIG keyring storage is being prepared. It should
+appear at most once in the lifetime of a program. The keyring still needs
+to be loaded from configuration.
+
+% PYSERVER_COMMON_TSIG_KEYRING_UPDATE Updating global TSIG keyring
+A debug message. The TSIG keyring is being (re)loaded from configuration.
+This happens at startup or when the configuration changes. The old keyring
+is removed and new one created with all the keys.
diff --git a/src/lib/python/isc/server_common/tests/Makefile.am b/src/lib/python/isc/server_common/tests/Makefile.am
new file mode 100644
index 0000000..4829edc
--- /dev/null
+++ b/src/lib/python/isc/server_common/tests/Makefile.am
@@ -0,0 +1,24 @@
+PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
+PYTESTS = tsig_keyring_test.py
+EXTRA_DIST = $(PYTESTS)
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/cc/.libs:$(abs_top_builddir)/src/lib/config/.libs:$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+check-local:
+if ENABLE_PYTHON_COVERAGE
+	touch $(abs_top_srcdir)/.coverage
+	rm -f .coverage
+	${LN_S} $(abs_top_srcdir)/.coverage .coverage
+endif
+	for pytest in $(PYTESTS) ; do \
+	echo Running test: $$pytest ; \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/lib/dns/python/.libs \
+	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
diff --git a/src/lib/python/isc/server_common/tests/tsig_keyring_test.py b/src/lib/python/isc/server_common/tests/tsig_keyring_test.py
new file mode 100644
index 0000000..e9a2174
--- /dev/null
+++ b/src/lib/python/isc/server_common/tests/tsig_keyring_test.py
@@ -0,0 +1,193 @@
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Tests for isc.server_common.tsig_keyring.
+"""
+
+import unittest
+import isc.log
+from isc.server_common.tsig_keyring import *
+import isc.dns
+from isc.testutils.ccsession_mock import MockModuleCCSession
+
+class Session(MockModuleCCSession):
+    """
+    A class pretending to be the config session.
+    """
+    def __init__(self):
+        MockModuleCCSession.__init__(self)
+        self._name = None
+        self._callback = None
+        self._remove_name = None
+        self._data = None
+
+    def add_remote_config_by_name(self, name, callback):
+        self._name = name
+        self._callback = callback
+
+    def remove_remote_config(self, name):
+        self._remove_name = name
+
+    def get_remote_config_value(self, module, name):
+        if module != 'tsig_keys' or name != 'keys':
+            raise Exception("Asked for bad data element")
+        return (self._data, False)
+
+class TSIGKeyRingTest(unittest.TestCase):
+    """
+    Tests for the isc.server_common.tsig_keyring module.
+    """
+    def setUp(self):
+        self.__session = Session()
+        self.__sha1name = isc.dns.Name('hmac-sha1')
+        self.__md5name = isc.dns.Name('hmac-md5.sig-alg.reg.int')
+
+    def tearDown(self):
+        deinit_keyring()
+
+    def __do_init(self):
+        init_keyring(self.__session)
+        # Some initialization happened
+        self.assertEqual('tsig_keys', self.__session._name)
+
+    def test_initialization(self):
+        """
+        Test we can initialize and deintialize the keyring. It also
+        tests the interaction with the keyring() function.
+        """
+        # The keyring function raises until initialized
+        self.assertRaises(Unexpected, get_keyring)
+        self.__do_init()
+        current_keyring = get_keyring()
+        self.assertTrue(isinstance(current_keyring, isc.dns.TSIGKeyRing))
+        # Another initialization does nothing
+        self.__do_init()
+        self.assertEqual(current_keyring, get_keyring())
+        # When we deinitialize it, it no longer provides the keyring
+        deinit_keyring()
+        self.assertEqual('tsig_keys', self.__session._remove_name)
+        self.__session._remove_name = None
+        self.assertRaises(Unexpected, get_keyring)
+        # Another deinitialization doesn't change anything
+        deinit_keyring()
+        self.assertRaises(Unexpected, get_keyring)
+        self.assertIsNone(self.__session._remove_name)
+        # Test we can init it again (not expected, but not forbidden)
+        self.__do_init()
+        self.assertTrue(isinstance(get_keyring(), isc.dns.TSIGKeyRing))
+
+    def test_load(self):
+        """
+        Test it can load the keys from the configuration and reload them
+        when the data change.
+        """
+        # Initial load
+        self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+        self.__do_init()
+        keys = get_keyring()
+        self.assertEqual(1, keys.size())
+        (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)
+        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+        self.assertEqual(isc.dns.Name('key'), key.get_key_name())
+        # There's a change in the configuration
+        # (The key has a different name)
+        self.__session._data = ['key.example:MTIzNAo=:hmac-sha1']
+        self.__session._callback()
+        orig_keys = keys
+        keys = get_keyring()
+        self.assertNotEqual(keys, orig_keys)
+        self.assertEqual(1, keys.size())
+        # The old key is not here
+        (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)
+        self.assertEqual(isc.dns.TSIGKeyRing.NOTFOUND, rcode)
+        self.assertIsNone(key)
+        # But the new one is
+        (rcode, key) = keys.find(isc.dns.Name('key.example'), self.__sha1name)
+        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+        self.assertEqual(isc.dns.Name('key.example'), key.get_key_name())
+
+    def test_empty_update(self):
+        """
+        Test an update that doesn't carry the correct element doesn't change
+        anything.
+        """
+        self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+        self.__do_init()
+        keys = get_keyring()
+        self.__session._data = None
+        self.__session._callback()
+        self.assertEqual(keys, get_keyring())
+
+    def test_no_keys_update(self):
+        """
+        Test we can update the keyring to be empty.
+        """
+        self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+        self.__do_init()
+        keys = get_keyring()
+        self.assertEqual(1, keys.size())
+        self.__session._data = []
+        self.__session._callback()
+        keys = get_keyring()
+        self.assertEqual(0, keys.size())
+
+    def test_update_multi(self):
+        """
+        Test we can handle multiple keys in startup/update.
+        """
+        # Init
+        self.__session._data = ['key:MTIzNAo=:hmac-sha1', 'key2:MTIzNAo=']
+        self.__do_init()
+        keys = get_keyring()
+        self.assertEqual(2, keys.size())
+        (rcode, key) = keys.find(isc.dns.Name('key'), self.__sha1name)
+        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+        self.assertEqual(isc.dns.Name('key'), key.get_key_name())
+        (rcode, key) = keys.find(isc.dns.Name('key2'), self.__md5name)
+        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+        self.assertEqual(isc.dns.Name('key2'), key.get_key_name())
+        # Update
+        self.__session._data = ['key1:MTIzNAo=:hmac-sha1', 'key3:MTIzNAo=']
+        self.__session._callback()
+        keys = get_keyring()
+        self.assertEqual(2, keys.size())
+        (rcode, key) = keys.find(isc.dns.Name('key1'), self.__sha1name)
+        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+        self.assertEqual(isc.dns.Name('key1'), key.get_key_name())
+        (rcode, key) = keys.find(isc.dns.Name('key3'), self.__md5name)
+        self.assertEqual(isc.dns.TSIGKeyRing.SUCCESS, rcode)
+        self.assertEqual(isc.dns.Name('key3'), key.get_key_name())
+
+    def test_update_bad(self):
+        """
+        Test it raises on bad updates and doesn't change anything.
+        """
+        self.__session._data = ['key:MTIzNAo=:hmac-sha1']
+        self.__do_init()
+        keys = get_keyring()
+        # Bad TSIG string
+        self.__session._data = ['key:this makes no sense:really']
+        self.assertRaises(isc.dns.InvalidParameter, self.__session._callback)
+        self.assertEqual(keys, get_keyring())
+        # A duplicity
+        self.__session._data = ['key:MTIzNAo=:hmac-sha1', 'key:MTIzNAo=:hmac-sha1']
+        self.assertRaises(AddError, self.__session._callback)
+        self.assertEqual(keys, get_keyring())
+
+if __name__ == "__main__":
+    isc.log.init("bind10") # FIXME Should this be needed?
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()
diff --git a/src/lib/python/isc/server_common/tsig_keyring.py b/src/lib/python/isc/server_common/tsig_keyring.py
new file mode 100644
index 0000000..308cfd4
--- /dev/null
+++ b/src/lib/python/isc/server_common/tsig_keyring.py
@@ -0,0 +1,121 @@
+# Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+"""
+This module conveniently keeps a copy of TSIG keyring loaded from the
+tsig_keys module.
+"""
+
+import isc.dns
+import isc.log
+from isc.log_messages.server_common_messages import *
+
+updater = None
+logger = isc.log.Logger("server_common")
+
+class Unexpected(Exception):
+    """
+    Raised when an unexpected operation is requested by the user of this
+    module. For example if calling keyring() before init_keyring().
+    """
+    pass
+
+class AddError(Exception):
+    """
+    Raised when a key can not be added. This usually means there's a
+    duplicate.
+    """
+    pass
+
+class Updater:
+    """
+    The updater of tsig key ring. Not to be used directly.
+    """
+    def __init__(self, session):
+        """
+        Constructor. Pass the ccsession object so the key ring can be
+        downloaded.
+        """
+        logger.debug(logger.DBGLVL_TRACE_BASIC,
+                     PYSERVER_COMMON_TSIG_KEYRING_INIT)
+        self.__session = session
+        self.__keyring = isc.dns.TSIGKeyRing()
+        session.add_remote_config_by_name('tsig_keys', self.__update)
+        self.__update()
+
+    def __update(self, value=None, module_cfg=None):
+        """
+        Update the key ring by the configuration.
+
+        Note that this function is used as a callback, but can raise
+        on bad data. The bad data is expected to be handled by the
+        configuration plugin and not be allowed as far as here.
+
+        The parameters are there just to match the signature which
+        the callback should have (i.e. they are ignored).
+        """
+        logger.debug(logger.DBGLVL_TRACE_BASIC,
+                     PYSERVER_COMMON_TSIG_KEYRING_UPDATE)
+        (data, _) = self.__session.get_remote_config_value('tsig_keys', 'keys')
+        if data is not None: # There's an update
+            keyring = isc.dns.TSIGKeyRing()
+            for key_data in data:
+                key = isc.dns.TSIGKey(key_data)
+                if keyring.add(key) != isc.dns.TSIGKeyRing.SUCCESS:
+                    raise AddError("Can't add key " + str(key))
+            self.__keyring = keyring
+
+    def get_keyring(self):
+        """
+        Return the current key ring.
+        """
+        return self.__keyring
+
+    def deinit(self):
+        """
+        Unregister from getting updates. The object will not be
+        usable any more after this.
+        """
+        logger.debug(logger.DBGLVL_TRACE_BASIC,
+                     PYSERVER_COMMON_TSIG_KEYRING_DEINIT)
+        self.__session.remove_remote_config('tsig_keys')
+
+def get_keyring():
+    """
+    Get the current key ring. You need to call init_keyring first.
+    """
+    if updater is None:
+        raise Unexpected("You need to initialize the keyring first by " +
+                         "init_keyring()")
+    return updater.get_keyring()
+
+def init_keyring(session):
+    """
+    Initialize the key ring for future use. It does nothing if already
+    initialized.
+    """
+    global updater
+    if updater is None:
+        updater = Updater(session)
+
+def deinit_keyring():
+    """
+    Deinit key ring. Yoeu can no longer access keyring() after this.
+    Does nothing if not initialized.
+    """
+    global updater
+    if updater is not None:
+        updater.deinit()
+        updater = None
diff --git a/src/lib/python/isc/util/cio/Makefile.am b/src/lib/python/isc/util/cio/Makefile.am
index 7e00768..0a2e735 100644
--- a/src/lib/python/isc/util/cio/Makefile.am
+++ b/src/lib/python/isc/util/cio/Makefile.am
@@ -23,7 +23,7 @@ socketsession_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-socketsession_la_LDFLAGS += -module
+socketsession_la_LDFLAGS += -module -avoid-version
 socketsession_la_LIBADD = $(top_builddir)/src/lib/util/io/libutil_io.la
 socketsession_la_LIBADD += $(PYTHON_LIB)
 
diff --git a/src/lib/resolve/.gitignore b/src/lib/resolve/.gitignore
new file mode 100644
index 0000000..292ed1a
--- /dev/null
+++ b/src/lib/resolve/.gitignore
@@ -0,0 +1,2 @@
+/resolve_messages.cc
+/resolve_messages.h
diff --git a/src/lib/resolve/Makefile.am b/src/lib/resolve/Makefile.am
index ceccce8..4b81862 100644
--- a/src/lib/resolve/Makefile.am
+++ b/src/lib/resolve/Makefile.am
@@ -34,6 +34,7 @@ nodist_libresolve_la_SOURCES = resolve_messages.h resolve_messages.cc
 libresolve_la_LIBADD = $(top_builddir)/src/lib/dns/libdns++.la
 libresolve_la_LIBADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 libresolve_la_LIBADD += $(top_builddir)/src/lib/log/liblog.la
+libresolve_la_LIBADD += $(top_builddir)/src/lib/asiodns/libasiodns.la
 
 # The message file should be in the distribution.
 EXTRA_DIST = resolve_messages.mes
diff --git a/src/lib/resolve/recursive_query.cc b/src/lib/resolve/recursive_query.cc
index ea7d528..8d03c1c 100644
--- a/src/lib/resolve/recursive_query.cc
+++ b/src/lib/resolve/recursive_query.cc
@@ -14,8 +14,8 @@
 
 #include <config.h>
 
-#include <netinet/in.h>
 #include <stdlib.h>
+#include <netinet/in.h>
 #include <sys/socket.h>
 #include <unistd.h>             // for some IPC/network system calls
 #include <string>
@@ -128,7 +128,7 @@ typedef std::vector<std::pair<std::string, uint16_t> > AddressVector;
 // mishandles this in its name mangling, and wouldn't compile.
 // We can probably use a typedef, but need to move it to a central
 // location and use it consistently.
-RecursiveQuery::RecursiveQuery(DNSService& dns_service,
+RecursiveQuery::RecursiveQuery(DNSServiceBase& dns_service,
     isc::nsas::NameserverAddressStore& nsas,
     isc::cache::ResolverCache& cache,
     const std::vector<std::pair<std::string, uint16_t> >& upstream,
diff --git a/src/lib/resolve/recursive_query.h b/src/lib/resolve/recursive_query.h
index 9af2d72..a819a94 100644
--- a/src/lib/resolve/recursive_query.h
+++ b/src/lib/resolve/recursive_query.h
@@ -87,7 +87,7 @@ public:
     /// \param lookup_timeout Timeout value for when we give up, in ms
     /// \param retries how many times we try again (0 means just send and
     ///     and return if it returs).
-    RecursiveQuery(DNSService& dns_service,
+    RecursiveQuery(DNSServiceBase& dns_service,
                    isc::nsas::NameserverAddressStore& nsas,
                    isc::cache::ResolverCache& cache,
                    const std::vector<std::pair<std::string, uint16_t> >&
@@ -178,7 +178,7 @@ public:
     void setTestServer(const std::string& address, uint16_t port);
 
 private:
-    DNSService& dns_service_;
+    DNSServiceBase& dns_service_;
     isc::nsas::NameserverAddressStore& nsas_;
     isc::cache::ResolverCache& cache_;
     boost::shared_ptr<std::vector<std::pair<std::string, uint16_t> > >
diff --git a/src/lib/resolve/tests/.gitignore b/src/lib/resolve/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/resolve/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/resolve/tests/recursive_query_unittest.cc b/src/lib/resolve/tests/recursive_query_unittest.cc
index 4e939fa..a8b8057 100644
--- a/src/lib/resolve/tests/recursive_query_unittest.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest.cc
@@ -14,13 +14,16 @@
 
 #include <config.h>
 
+#include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 
-#include <string.h>
+#include <cstring>
 
+#include <boost/noncopyable.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
 #include <boost/date_time/posix_time/posix_time_types.hpp>
 
 #include <gtest/gtest.h>
@@ -61,6 +64,7 @@ using namespace isc::asiodns;
 using namespace isc::asiolink;
 using namespace isc::dns;
 using namespace isc::util;
+using boost::scoped_ptr;
 
 namespace isc {
 namespace asiodns {
@@ -84,18 +88,14 @@ const char* const TEST_IPV4_ADDR = "127.0.0.1";
 // for the tests below.
 const uint8_t test_data[] = {0, 4, 1, 2, 3, 4};
 
-// This function returns an addrinfo structure for use by tests, using
-// different addresses and ports depending on whether we're testing
-// IPv4 or v6, TCP or UDP, and client or server operation.
+// This function returns an addrinfo structure for use by tests.
 struct addrinfo*
-resolveAddress(const int family, const int protocol, const bool client) {
-    const char* const addr = (family == AF_INET6) ?
-        TEST_IPV6_ADDR : TEST_IPV4_ADDR;
-    const char* const port = client ? TEST_CLIENT_PORT : TEST_SERVER_PORT;
-
+resolveAddress(const int protocol, const char* const addr,
+               const char* const port)
+{
     struct addrinfo hints;
     memset(&hints, 0, sizeof(hints));
-    hints.ai_family = family;
+    hints.ai_family = AF_UNSPEC; // let the address decide it.
     hints.ai_socktype = (protocol == IPPROTO_UDP) ? SOCK_DGRAM : SOCK_STREAM;
     hints.ai_protocol = protocol;
     hints.ai_flags = AI_NUMERICSERV;
@@ -109,6 +109,51 @@ resolveAddress(const int family, const int protocol, const bool client) {
     return (res);
 }
 
+// convenience shortcut of the other version using different addresses and
+// ports depending on whether we're testing IPv4 or v6, TCP or UDP, and
+// client or server operation.
+struct addrinfo*
+resolveAddress(const int family, const int protocol, const bool client) {
+    return (resolveAddress(protocol,
+                           (family == AF_INET6) ? TEST_IPV6_ADDR :
+                           TEST_IPV4_ADDR,
+                           client ? TEST_CLIENT_PORT : TEST_SERVER_PORT));
+}
+
+// A helper holder of addrinfo so we can safely release the resource
+// either when leaving the defined scope either normally or due to exception.
+struct ScopedAddrInfo {
+    ScopedAddrInfo(struct addrinfo* res) : res_(res) {}
+    ~ScopedAddrInfo() { freeaddrinfo(res_); }
+    struct addrinfo* res_;
+};
+
+// Similar to ScopedAddrInfo but for socket FD.  It also supports the "release"
+// operation so it can release the ownership of the FD.
+// This is made non copyable to avoid making an accidental copy, which could
+// result in duplicate close.
+struct ScopedSocket : private boost::noncopyable {
+    ScopedSocket() : s_(-1) {}
+    ScopedSocket(int s) : s_(s) {}
+    ~ScopedSocket() {
+        if (s_ >= 0) {
+            close(s_);
+        }
+    }
+    void reset(int new_s) {
+        if (s_ >= 0) {
+            close(s_);
+        }
+        s_ = new_s;
+    }
+    int release() {
+        int s = s_;
+        s_ = -1;
+        return (s);
+    }
+    int s_;
+};
+
 // This fixture is a framework for various types of network operations
 // using the ASIO interfaces.  Each test case creates an IOService object,
 // opens a local "client" socket for testing, sends data via the local socket
@@ -128,27 +173,20 @@ protected:
         // It would delete itself, but after the io_service_, which could
         // segfailt in case there were unhandled requests
         resolver_.reset();
-        if (res_ != NULL) {
-            freeaddrinfo(res_);
-        }
-        if (sock_ != -1) {
-            close(sock_);
-        }
-        delete dns_service_;
-        delete callback_;
-        delete io_service_;
     }
 
     // Send a test UDP packet to a mock server
     void sendUDP(const int family) {
-        res_ = resolveAddress(family, IPPROTO_UDP, false);
+        ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, false));
+        struct addrinfo* res = sai.res_;
 
-        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
-        if (sock_ < 0) {
+        sock_.reset(socket(res->ai_family, res->ai_socktype,
+                           res->ai_protocol));
+        if (sock_.s_ < 0) {
             isc_throw(IOError, "failed to open test socket");
         }
-        const int cc = sendto(sock_, test_data, sizeof(test_data), 0,
-                              res_->ai_addr, res_->ai_addrlen);
+        const int cc = sendto(sock_.s_, test_data, sizeof(test_data), 0,
+                              res->ai_addr, res->ai_addrlen);
         if (cc != sizeof(test_data)) {
             isc_throw(IOError, "unexpected sendto result: " << cc);
         }
@@ -157,16 +195,18 @@ protected:
 
     // Send a test TCP packet to a mock server
     void sendTCP(const int family) {
-        res_ = resolveAddress(family, IPPROTO_TCP, false);
+        ScopedAddrInfo sai(resolveAddress(family, IPPROTO_TCP, false));
+        struct addrinfo* res = sai.res_;
 
-        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
-        if (sock_ < 0) {
+        sock_.reset(socket(res->ai_family, res->ai_socktype,
+                           res->ai_protocol));
+        if (sock_.s_ < 0) {
             isc_throw(IOError, "failed to open test socket");
         }
-        if (connect(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
+        if (connect(sock_.s_, res->ai_addr, res->ai_addrlen) < 0) {
             isc_throw(IOError, "failed to connect to the test server");
         }
-        const int cc = send(sock_, test_data, sizeof(test_data), 0);
+        const int cc = send(sock_.s_, test_data, sizeof(test_data), 0);
         if (cc != sizeof(test_data)) {
             isc_throw(IOError, "unexpected send result: " << cc);
         }
@@ -177,14 +217,16 @@ protected:
     // 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) {
-        res_ = resolveAddress(family, IPPROTO_UDP, true);
+        ScopedAddrInfo sai(resolveAddress(family, IPPROTO_UDP, true));
+        struct addrinfo* res = sai.res_;
 
-        sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
-        if (sock_ < 0) {
+        sock_.reset(socket(res->ai_family, res->ai_socktype,
+                           res->ai_protocol));
+        if (sock_.s_ < 0) {
             isc_throw(IOError, "failed to open test socket");
         }
 
-        if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
+        if (bind(sock_.s_, res->ai_addr, res->ai_addrlen) < 0) {
             isc_throw(IOError, "bind failed: " << strerror(errno));
         }
 
@@ -204,7 +246,7 @@ protected:
         // we add an ad hoc timeout.
         const struct timeval timeo = { 10, 0 };
         int recv_options = 0;
-        if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
+        if (setsockopt(sock_.s_, SOL_SOCKET, SO_RCVTIMEO, &timeo,
                        sizeof(timeo))) {
             if (errno == ENOPROTOOPT) {
                 // Workaround for Solaris: it doesn't accept SO_RCVTIMEO
@@ -217,7 +259,7 @@ protected:
                 isc_throw(IOError, "set RCVTIMEO failed: " << strerror(errno));
             }
         }
-        const int ret = recv(sock_, buffer, size, recv_options);
+        const int ret = recv(sock_.s_, buffer, size, recv_options);
         if (ret < 0) {
             isc_throw(IOError, "recvfrom failed: " << strerror(errno));
         }
@@ -226,38 +268,68 @@ protected:
         size = ret;
     }
 
+    void
+    addServer(const string& address, const char* const port, int protocol) {
+        ScopedAddrInfo sai(resolveAddress(protocol, address.c_str(), port));
+        struct addrinfo* res = sai.res_;
+        const int family = res->ai_family;
+
+        ScopedSocket sock(socket(res->ai_family, res->ai_socktype,
+                                 res->ai_protocol));
+        const int s = sock.s_;
+        if (s < 0) {
+            isc_throw(isc::Unexpected, "failed to open a test socket");
+        }
+        const int on = 1;
+        if (family == AF_INET6) {
+            if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) ==
+                -1) {
+                isc_throw(isc::Unexpected,
+                          "failed to set socket option(IPV6_V6ONLY)");
+            }
+        }
+        if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
+            isc_throw(isc::Unexpected,
+                      "failed to set socket option(SO_REUSEADDR)");
+        }
+        if (bind(s, res->ai_addr, res->ai_addrlen) != 0) {
+            isc_throw(isc::Unexpected, "failed to bind a test socket");
+        }
+        if (protocol == IPPROTO_TCP) {
+            dns_service_->addServerTCPFromFD(sock.release(), family);
+        } else {
+            dns_service_->addServerUDPFromFD(sock.release(), family);
+        }
+    }
 
     // Set up an IO Service queue using the specified address
-    void setDNSService(const char& address) {
-        delete dns_service_;
-        dns_service_ = NULL;
-        delete io_service_;
-        io_service_ = new IOService();
-        callback_ = new ASIOCallBack(this);
-        dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, address, callback_, NULL, NULL);
+    void setDNSService(const string& address) {
+        setDNSService();
+        addServer(address, TEST_SERVER_PORT, IPPROTO_TCP);
+        addServer(address, TEST_SERVER_PORT, IPPROTO_UDP);
     }
 
     // Set up an IO Service queue using the "any" address, on IPv4 if
     // 'use_ipv4' is true and on IPv6 if 'use_ipv6' is true.
     void setDNSService(const bool use_ipv4, const bool use_ipv6) {
-        delete dns_service_;
-        dns_service_ = NULL;
-        delete io_service_;
-        io_service_ = new IOService();
-        callback_ = new ASIOCallBack(this);
-        dns_service_ = new DNSService(*io_service_, *TEST_SERVER_PORT, use_ipv4, use_ipv6, callback_,
-                                      NULL, NULL);
+        setDNSService();
+        if (use_ipv6) {
+            addServer("::", TEST_SERVER_PORT, IPPROTO_TCP);
+            addServer("::", TEST_SERVER_PORT, IPPROTO_UDP);
+        }
+        if (use_ipv4) {
+            addServer("0.0.0.0", TEST_SERVER_PORT, IPPROTO_TCP);
+            addServer("0.0.0.0", TEST_SERVER_PORT, IPPROTO_UDP);
+        }
     }
 
     // Set up empty DNS Service
     // Set up an IO Service queue without any addresses
     void setDNSService() {
-        delete dns_service_;
-        dns_service_ = NULL;
-        delete io_service_;
-        io_service_ = new IOService();
-        callback_ = new ASIOCallBack(this);
-        dns_service_ = new DNSService(*io_service_, callback_, NULL, NULL);
+        io_service_.reset(new IOService());
+        callback_.reset(new ASIOCallBack(this));
+        dns_service_.reset(new DNSService(*io_service_, callback_.get(), NULL,
+                                          NULL));
     }
 
     // Run a simple server test, on either IPv4 or IPv6, and over either
@@ -276,7 +348,7 @@ protected:
         // There doesn't seem to be an effective test for the validity of
         // 'native'.
         // One thing we are sure is it must be different from our local socket.
-        EXPECT_NE(sock_, callback_native_);
+        EXPECT_NE(sock_.s_, callback_native_);
         EXPECT_EQ(protocol, callback_protocol_);
         EXPECT_EQ(family == AF_INET6 ? TEST_IPV6_ADDR : TEST_IPV4_ADDR,
                   callback_address_);
@@ -424,28 +496,26 @@ private:
 protected:
     // We use a pointer for io_service_, because for some tests we
     // need to recreate a new one within one onstance of this class
-    IOService* io_service_;
-    DNSService* dns_service_;
-    isc::nsas::NameserverAddressStore* nsas_;
+    scoped_ptr<IOService> io_service_;
+    scoped_ptr<DNSService> dns_service_;
+    scoped_ptr<isc::nsas::NameserverAddressStore> nsas_;
     isc::cache::ResolverCache cache_;
-    ASIOCallBack* callback_;
+    scoped_ptr<ASIOCallBack> callback_;
     int callback_protocol_;
     int callback_native_;
     string callback_address_;
     vector<uint8_t> callback_data_;
-    int sock_;
-    struct addrinfo* res_;
+    ScopedSocket sock_;
     boost::shared_ptr<isc::util::unittests::TestResolver> resolver_;
 };
 
 RecursiveQueryTest::RecursiveQueryTest() :
     dns_service_(NULL), callback_(NULL), callback_protocol_(0),
-    callback_native_(-1), sock_(-1), res_(NULL),
-    resolver_(new isc::util::unittests::TestResolver())
+    callback_native_(-1), resolver_(new isc::util::unittests::TestResolver())
 {
-    io_service_ = new IOService();
+    io_service_.reset(new IOService());
     setDNSService(true, true);
-    nsas_ = new isc::nsas::NameserverAddressStore(resolver_);
+    nsas_.reset(new isc::nsas::NameserverAddressStore(resolver_));
 }
 
 TEST_F(RecursiveQueryTest, v6UDPSend) {
@@ -476,24 +546,24 @@ TEST_F(RecursiveQueryTest, v6UDPSendSpecific) {
     // an error on a subsequent read operation.  We could do it, but for
     // simplicity we only tests the easier cases for now.
 
-    setDNSService(*TEST_IPV6_ADDR);
+    setDNSService(TEST_IPV6_ADDR);
     doTest(AF_INET6, IPPROTO_UDP);
 }
 
 TEST_F(RecursiveQueryTest, v6TCPSendSpecific) {
-    setDNSService(*TEST_IPV6_ADDR);
+    setDNSService(TEST_IPV6_ADDR);
     doTest(AF_INET6, IPPROTO_TCP);
 
     EXPECT_THROW(sendTCP(AF_INET), IOError);
 }
 
 TEST_F(RecursiveQueryTest, v4UDPSendSpecific) {
-    setDNSService(*TEST_IPV4_ADDR);
+    setDNSService(TEST_IPV4_ADDR);
     doTest(AF_INET, IPPROTO_UDP);
 }
 
 TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
-    setDNSService(*TEST_IPV4_ADDR);
+    setDNSService(TEST_IPV4_ADDR);
     doTest(AF_INET, IPPROTO_TCP);
 
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
@@ -501,7 +571,7 @@ TEST_F(RecursiveQueryTest, v4TCPSendSpecific) {
 
 TEST_F(RecursiveQueryTest, v6AddServer) {
     setDNSService();
-    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV6_ADDR);
+    addServer(TEST_IPV6_ADDR, TEST_SERVER_PORT, IPPROTO_TCP);
     doTest(AF_INET6, IPPROTO_TCP);
 
     EXPECT_THROW(sendTCP(AF_INET), IOError);
@@ -509,7 +579,7 @@ TEST_F(RecursiveQueryTest, v6AddServer) {
 
 TEST_F(RecursiveQueryTest, v4AddServer) {
     setDNSService();
-    dns_service_->addServer(*TEST_SERVER_PORT, TEST_IPV4_ADDR);
+    addServer(TEST_IPV4_ADDR, TEST_SERVER_PORT, IPPROTO_TCP);
     doTest(AF_INET, IPPROTO_TCP);
 
     EXPECT_THROW(sendTCP(AF_INET6), IOError);
@@ -606,41 +676,43 @@ TEST_F(RecursiveQueryTest, forwarderSend) {
 }
 
 int
-createTestSocket()
-{
-    struct addrinfo* res_ = resolveAddress(AF_INET, IPPROTO_UDP, true);
-    int sock_ = socket(res_->ai_family, res_->ai_socktype, res_->ai_protocol);
-    if (sock_ < 0) {
+createTestSocket() {
+    ScopedAddrInfo sai(resolveAddress(AF_INET, IPPROTO_UDP, true));
+    struct addrinfo* res = sai.res_;
+
+    ScopedSocket sock(socket(res->ai_family, res->ai_socktype,
+                             res->ai_protocol));
+    if (sock.s_ < 0) {
         isc_throw(IOError, "failed to open test socket");
     }
-    if (bind(sock_, res_->ai_addr, res_->ai_addrlen) < 0) {
+    if (bind(sock.s_, res->ai_addr, res->ai_addrlen) < 0) {
         isc_throw(IOError, "failed to bind test socket");
     }
-    return sock_;
+    return (sock.release());
 }
 
 int
-setSocketTimeout(int sock_, size_t tv_sec, size_t tv_usec) {
+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 (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;
+    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) {
+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) {
+        if (recv(sock, inbuff, sizeof(inbuff), recv_options) < 0) {
             return false;
         } else {
             ++i;
@@ -690,7 +762,7 @@ TEST_F(RecursiveQueryTest, forwardQueryTimeout) {
     setDNSService();
 
     // Prepare the socket
-    sock_ = createTestSocket();
+    sock_.reset(createTestSocket());
 
     // Prepare the server
     bool done(true);
@@ -724,7 +796,7 @@ TEST_F(RecursiveQueryTest, forwardClientTimeout) {
     // Prepare the service (we do not use the common setup, we do not answer
     setDNSService();
 
-    sock_ = createTestSocket();
+    sock_.reset(createTestSocket());
 
     // Prepare the server
     bool done1(true);
@@ -758,7 +830,7 @@ TEST_F(RecursiveQueryTest, forwardLookupTimeout) {
     setDNSService();
 
     // Prepare the socket
-    sock_ = createTestSocket();
+    sock_.reset(createTestSocket());
 
     // Prepare the server
     bool done(true);
@@ -793,7 +865,7 @@ TEST_F(RecursiveQueryTest, lowtimeouts) {
     setDNSService();
 
     // Prepare the socket
-    sock_ = createTestSocket();
+    sock_.reset(createTestSocket());
 
     // Prepare the server
     bool done(true);
diff --git a/src/lib/resolve/tests/recursive_query_unittest_2.cc b/src/lib/resolve/tests/recursive_query_unittest_2.cc
index a222240..2b3d129 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_2.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_2.cc
@@ -343,7 +343,8 @@ public:
 
         // Convert to wire format
         udp_send_buffer_->clear();
-        MessageRenderer renderer(*udp_send_buffer_);
+        MessageRenderer renderer;
+        renderer.setBuffer(udp_send_buffer_.get());
         msg.toWire(renderer);
 
         if (mangle_response) {
@@ -477,10 +478,9 @@ public:
         setReferralExampleOrg(msg);
 
         // Convert to wire format
-        // Use a temporary buffer for the dns wire data (we copy it
+        // Use a temporary renderer for the dns wire data (we copy it
         // to the 'real' buffer below)
-        OutputBuffer msg_buf(BUFFER_SIZE);
-        MessageRenderer renderer(msg_buf);
+        MessageRenderer renderer;
         msg.toWire(renderer);
 
         // Expected next state (when checked) is the UDP query to example.org.
@@ -496,12 +496,13 @@ public:
         // followed by the actual data. We copy them to a new buffer
         // first
         tcp_send_buffer_->clear();
-        tcp_send_buffer_->writeUint16(msg_buf.getLength());
-        tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
+        tcp_send_buffer_->writeUint16(renderer.getLength());
+        tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
         tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
                                             tcp_send_buffer_->getLength()),
-                               boost::bind(&RecursiveQueryTest2::tcpSendHandler, this,
-                                           tcp_send_buffer_->getLength(), _1, _2));
+                               boost::bind(
+                                   &RecursiveQueryTest2::tcpSendHandler, this,
+                                   tcp_send_buffer_->getLength(), _1, _2));
     }
 
     /// \brief Completion Handler for Sending TCP data
diff --git a/src/lib/resolve/tests/recursive_query_unittest_3.cc b/src/lib/resolve/tests/recursive_query_unittest_3.cc
index 3602b03..168ec80 100644
--- a/src/lib/resolve/tests/recursive_query_unittest_3.cc
+++ b/src/lib/resolve/tests/recursive_query_unittest_3.cc
@@ -240,8 +240,10 @@ public:
 
         // Convert to wire format
         udp_send_buffer_->clear();
-        MessageRenderer renderer(*udp_send_buffer_);
+        MessageRenderer renderer;
+        renderer.setBuffer(udp_send_buffer_.get());
         message.toWire(renderer);
+        renderer.setBuffer(NULL);
 
         // Return a message back to the IOFetch object (after setting the
         // expected length of data for the check in the send handler).
@@ -353,8 +355,7 @@ public:
         // Convert to wire format
         // Use a temporary buffer for the dns wire data (we copy it
         // to the 'real' buffer below)
-        OutputBuffer msg_buf(BUFFER_SIZE);
-        MessageRenderer renderer(msg_buf);
+        MessageRenderer renderer;
         message.toWire(renderer);
 
         // Also, take this opportunity to clear the accumulated read count in
@@ -368,12 +369,14 @@ public:
         // followed by the actual data. We copy them to a new buffer
         // first
         tcp_send_buffer_->clear();
-        tcp_send_buffer_->writeUint16(msg_buf.getLength());
-        tcp_send_buffer_->writeData(msg_buf.getData(), msg_buf.getLength());
+        tcp_send_buffer_->writeUint16(renderer.getLength());
+        tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
         tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
                                             tcp_send_buffer_->getLength()),
-                           boost::bind(&RecursiveQueryTest3::tcpSendHandler,
-                               this, tcp_send_buffer_->getLength(), _1, _2));
+                               boost::bind(
+                                   &RecursiveQueryTest3::tcpSendHandler,
+                                   this,
+                                   tcp_send_buffer_->getLength(), _1, _2));
     }
 
     /// \brief Completion Handler for Sending TCP data
diff --git a/src/lib/server_common/.gitignore b/src/lib/server_common/.gitignore
new file mode 100644
index 0000000..e25a98f
--- /dev/null
+++ b/src/lib/server_common/.gitignore
@@ -0,0 +1,2 @@
+/server_common_messages.cc
+/server_common_messages.h
diff --git a/src/lib/server_common/portconfig.cc b/src/lib/server_common/portconfig.cc
index d1e97f3..530c919 100644
--- a/src/lib/server_common/portconfig.cc
+++ b/src/lib/server_common/portconfig.cc
@@ -31,11 +31,6 @@ namespace isc {
 namespace server_common {
 namespace portconfig {
 
-// This flags disables pushing the sockets to the DNSService. It prevents
-// the clearServers() method to close the file descriptors we made up.
-// It is not presented in any header, but we use it from the tests anyway.
-bool test_mode(false);
-
 AddressList
 parseAddresses(isc::data::ConstElementPtr addresses,
                const std::string& elemName)
@@ -84,7 +79,9 @@ namespace {
 vector<string> current_sockets;
 
 void
-setAddresses(DNSService& service, const AddressList& addresses) {
+setAddresses(DNSServiceBase& service, const AddressList& addresses,
+             DNSService::ServerFlag server_options)
+{
     service.clearServers();
     BOOST_FOREACH(const string& token, current_sockets) {
         socketRequestor().releaseSocket(token);
@@ -99,35 +96,32 @@ setAddresses(DNSService& service, const AddressList& addresses) {
                                                 address.first, address.second,
                                                 SocketRequestor::SHARE_SAME));
         current_sockets.push_back(tcp.second);
-        if (!test_mode) {
-            service.addServerTCPFromFD(tcp.first, af);
-        }
+        service.addServerTCPFromFD(tcp.first, af);
         const SocketRequestor::SocketID
             udp(socketRequestor().requestSocket(SocketRequestor::UDP,
                                                 address.first, address.second,
                                                 SocketRequestor::SHARE_SAME));
         current_sockets.push_back(udp.second);
-        if (!test_mode) {
-            service.addServerUDPFromFD(udp.first, af);
-        }
+        service.addServerUDPFromFD(udp.first, af, server_options);
     }
 }
 
 }
 
 void
-installListenAddresses(const AddressList& newAddresses,
-                       AddressList& addressStore,
-                       isc::asiodns::DNSService& service)
+installListenAddresses(const AddressList& new_addresses,
+                       AddressList& address_store,
+                       DNSServiceBase& service,
+                       DNSService::ServerFlag server_options)
 {
     try {
         LOG_DEBUG(logger, DBG_TRACE_BASIC, SRVCOMM_SET_LISTEN);
-        BOOST_FOREACH(const AddressPair& addr, newAddresses) {
+        BOOST_FOREACH(const AddressPair& addr, new_addresses) {
             LOG_DEBUG(logger, DBG_TRACE_VALUES, SRVCOMM_ADDRESS_VALUE).
                 arg(addr.first).arg(addr.second);
         }
-        setAddresses(service, newAddresses);
-        addressStore = newAddresses;
+        setAddresses(service, new_addresses, server_options);
+        address_store = new_addresses;
     } catch (const SocketRequestor::NonFatalSocketError& e) {
         /*
          * If one of the addresses isn't set successfully, we will restore
@@ -144,14 +138,14 @@ installListenAddresses(const AddressList& newAddresses,
          */
         LOG_ERROR(logger, SRVCOMM_ADDRESS_FAIL).arg(e.what());
         try {
-            setAddresses(service, addressStore);
+            setAddresses(service, address_store, server_options);
         } catch (const SocketRequestor::NonFatalSocketError& e2) {
             LOG_FATAL(logger, SRVCOMM_ADDRESS_UNRECOVERABLE).arg(e2.what());
             // If we can't set the new ones, nor the old ones, at least
             // releasing everything should work. If it doesn't, there isn't
             // anything else we could do.
-            setAddresses(service, AddressList());
-            addressStore.clear();
+            setAddresses(service, AddressList(), server_options);
+            address_store.clear();
         }
         //Anyway the new configure has problem, we need to notify configure
         //manager the new configure doesn't work
diff --git a/src/lib/server_common/portconfig.h b/src/lib/server_common/portconfig.h
index 0c0fa6a..0795728 100644
--- a/src/lib/server_common/portconfig.h
+++ b/src/lib/server_common/portconfig.h
@@ -15,22 +15,15 @@
 #ifndef ISC_SERVER_COMMON_PORTCONFIG_H
 #define ISC_SERVER_COMMON_PORTCONFIG_H
 
+#include <cc/data.h>
+
+#include <asiodns/dns_service.h>
+
 #include <utility>
 #include <string>
 #include <stdint.h>
 #include <vector>
 
-#include <cc/data.h>
-
-/*
- * Some forward declarations.
- */
-namespace isc {
-namespace asiodns {
-class DNSService;
-}
-}
-
 namespace isc {
 namespace server_common {
 /**
@@ -88,42 +81,49 @@ AddressList
 parseAddresses(isc::data::ConstElementPtr addresses,
                const std::string& elemName);
 
-/**
- * \brief Changes current listening addresses and ports.
- *
- * Removes all sockets we currently listen on and starts listening on the
- * addresses and ports requested in newAddresses.
- *
- * If it fails to set up the new addresses, it attempts to roll back to the
- * previous addresses (but it still propagates the exception). If the rollback
- * fails as well, it doesn't abort the application (to allow reconfiguration),
- * but removes all the sockets it listened on. One of the exceptions is
- * propagated.
- *
- * The ports are requested from the socket creator through boss. Therefore
- * you need to initialize the SocketRequestor before using this function.
- *
- * \param newAddresses are the addresses you want to listen on.
- * \param addressStore is the place you store your current addresses. It is
- *     used when there's a need for rollback. The newAddresses are copied here
- *     when the change is successful.
- * \param dnsService is the DNSService object we use now. The requests from
- *     the new sockets are handled using this dnsService (and all current
- *     sockets on the service are closed first).
- * \throw asiolink::IOError when initialization or closing of socket fails.
- * \throw isc::server_common::SocketRequestor::Socket error when the
- *     boss/socket creator doesn't want to give us the socket.
- * \throw std::bad_alloc when allocation fails.
- * \throw isc::InvalidOperation when the function is called and the
- *     SocketRequestor isn't initialized yet.
- */
+/// \brief Changes current listening addresses and ports.
+///
+/// Removes all sockets we currently listen on and starts listening on the
+/// addresses and ports requested in new_addresses.
+///
+/// If it fails to set up the new addresses, it attempts to roll back to the
+/// previous addresses (but it still propagates the exception). If the rollback
+/// fails as well, it doesn't abort the application (to allow reconfiguration),
+/// but removes all the sockets it listened on. One of the exceptions is
+/// propagated.
+///
+/// The ports are requested from the socket creator through boss. Therefore
+/// you need to initialize the SocketRequestor before using this function.
+///
+/// \param new_addresses are the addresses you want to listen on.
+/// \param address_store is the place you store your current addresses. It is
+///     used when there's a need for rollback. The new_addresses are copied
+///     here when the change is successful.
+/// \param dns_service is the DNSService object we use now. The requests from
+///     the new sockets are handled using this dns_service (and all current
+///     sockets on the service are closed first).
+/// \param server_options specifies optional properties for the servers
+///        created via \c dns_service.
+///
+/// \throw asiolink::IOError when initialization or closing of socket fails.
+/// \throw isc::server_common::SocketRequestor::Socket error when the
+///     boss/socket creator doesn't want to give us the socket.
+/// \throw std::bad_alloc when allocation fails.
+/// \throw isc::InvalidOperation when the function is called and the
+///     SocketRequestor isn't initialized yet.
 void
-installListenAddresses(const AddressList& newAddresses,
-                       AddressList& addressStore,
-                       asiodns::DNSService& dnsService);
+installListenAddresses(const AddressList& new_addresses,
+                       AddressList& address_store,
+                       asiodns::DNSServiceBase& dns_service,
+                       asiodns::DNSService::ServerFlag server_options =
+                       asiodns::DNSService::SERVER_DEFAULT);
 
 }
 }
 }
 
 #endif
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/server_common/tests/.gitignore b/src/lib/server_common/tests/.gitignore
new file mode 100644
index 0000000..0749d37
--- /dev/null
+++ b/src/lib/server_common/tests/.gitignore
@@ -0,0 +1,2 @@
+/data_path.h
+/run_unittests
diff --git a/src/lib/server_common/tests/client_unittest.cc b/src/lib/server_common/tests/client_unittest.cc
index 287a926..c8db846 100644
--- a/src/lib/server_common/tests/client_unittest.cc
+++ b/src/lib/server_common/tests/client_unittest.cc
@@ -12,6 +12,7 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <sys/types.h>
 #include <sys/socket.h>
 #include <string.h>
 
diff --git a/src/lib/server_common/tests/portconfig_unittest.cc b/src/lib/server_common/tests/portconfig_unittest.cc
index 2ea3808..ac880c0 100644
--- a/src/lib/server_common/tests/portconfig_unittest.cc
+++ b/src/lib/server_common/tests/portconfig_unittest.cc
@@ -12,15 +12,19 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <gtest/gtest.h>
+
 #include <server_common/portconfig.h>
 #include <testutils/socket_request.h>
+#include <testutils/mockups.h>
+
+#include <util/unittests/resource.h>
 
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
 #include <asiolink/asiolink.h>
 #include <asiodns/asiodns.h>
 
-#include <gtest/gtest.h>
 #include <string>
 
 using namespace isc::server_common::portconfig;
@@ -30,6 +34,7 @@ using namespace isc;
 using namespace std;
 using namespace isc::asiolink;
 using namespace isc::asiodns;
+using namespace isc::testutils;
 using boost::lexical_cast;
 
 namespace {
@@ -132,7 +137,6 @@ TEST_F(ParseAddresses, invalid) {
 // Test fixture for installListenAddresses
 struct InstallListenAddresses : public ::testing::Test {
     InstallListenAddresses() :
-        dnss_(ios_, NULL, NULL, NULL),
         // The empty string is expected parameter of requestSocket,
         // not app_name - the request does not fall back to this, it
         // is checked to be the same.
@@ -143,8 +147,7 @@ struct InstallListenAddresses : public ::testing::Test {
         invalid_.push_back(AddressPair("127.0.0.1", 5288));
         invalid_.push_back(AddressPair("192.0.2.2", 1));
     }
-    IOService ios_;
-    DNSService dnss_;
+    MockDNSService dnss_;
     AddressList store_;
     isc::testutils::TestSocketRequestor sock_requestor_;
     // We should be able to bind to these addresses
@@ -313,13 +316,15 @@ TEST_F(InstallListenAddressesDeathTest, inconsistent) {
     // Make sure it actually kills the application (there should be an abort
     // in this case)
     EXPECT_DEATH({
-                    try {
-                        installListenAddresses(deathAddresses, store_, dnss_);
-                    } catch (...) {
-                        // Prevent exceptions killing the application, we need
-                        // to make sure it dies the real hard way
-                    };
-                 }, "");
+        isc::util::unittests::dontCreateCoreDumps();
+
+        try {
+          installListenAddresses(deathAddresses, store_, dnss_);
+        } catch (...) {
+          // Prevent exceptions killing the application, we need
+          // to make sure it dies the real hard way
+        };
+      }, "");
 }
 
 // If we are unable to tell the boss we closed a socket, we abort, as we are
@@ -330,16 +335,18 @@ TEST_F(InstallListenAddressesDeathTest, cantClose) {
     // Instruct it to fail on close
     sock_requestor_.break_release_ = true;
     EXPECT_DEATH({
-                    try {
-                        // Setting to empty will close all current sockets.
-                        // And thanks to the break_release_, the close will
-                        // throw, which will make it crash.
-                        installListenAddresses(empty, store_, dnss_);
-                    } catch (...) {
-                        // To make sure it is killed by abort, not by some
-                        // (unhandled) exception
-                    };
-                }, "");
+        isc::util::unittests::dontCreateCoreDumps();
+
+        try {
+          // Setting to empty will close all current sockets.
+          // And thanks to the break_release_, the close will
+          // throw, which will make it crash.
+          installListenAddresses(empty, store_, dnss_);
+        } catch (...) {
+          // To make sure it is killed by abort, not by some
+          // (unhandled) exception
+        };
+      }, "");
     // And reset it back, so it can safely clean up itself.
     sock_requestor_.break_release_ = false;
 }
diff --git a/src/lib/statistics/tests/.gitignore b/src/lib/statistics/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/statistics/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/testutils/dnsmessage_test.cc b/src/lib/testutils/dnsmessage_test.cc
index af354d5..ec6914d 100644
--- a/src/lib/testutils/dnsmessage_test.cc
+++ b/src/lib/testutils/dnsmessage_test.cc
@@ -23,6 +23,12 @@
 
 #include <testutils/dnsmessage_test.h>
 
+#include <boost/bind.hpp>
+
+#include <string>
+#include <sstream>
+
+using namespace std;
 using namespace isc::dns;
 
 namespace isc {
@@ -80,12 +86,35 @@ matchRdata(const char*, const char*,
     }
     return (::testing::AssertionSuccess());
 }
+
+// A helper callback of masterLoad() used by textToRRset() below.
+void
+setRRset(RRsetPtr rrset, RRsetPtr* rrsetp) {
+    if (*rrsetp) {
+        isc_throw(isc::Unexpected,
+                  "multiple RRsets are given to textToRRset");
+    }
+    *rrsetp = rrset;
+}
+}
+
+RRsetPtr
+textToRRset(const string& text_rrset, const RRClass& rrclass,
+            const Name& origin)
+{
+    stringstream ss(text_rrset);
+    RRsetPtr rrset;
+    masterLoad(ss, origin, rrclass, boost::bind(setRRset, _1, &rrset));
+    return (rrset);
 }
 
 void
 rrsetCheck(isc::dns::ConstRRsetPtr expected_rrset,
            isc::dns::ConstRRsetPtr actual_rrset)
 {
+    SCOPED_TRACE("Comparing RRsets\n"
+                 "  Actual: " + actual_rrset->toText() +
+                 "  Expected: " + expected_rrset->toText());
     EXPECT_EQ(expected_rrset->getName(), actual_rrset->getName());
     EXPECT_EQ(expected_rrset->getClass(), actual_rrset->getClass());
     EXPECT_EQ(expected_rrset->getType(), actual_rrset->getType());
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 1aba526..57cb72c 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_DNSMESSAGETEST_H
+#define __ISC_TESTUTILS_DNSMESSAGETEST_H 1
+
 #include <algorithm>
 #include <functional>
 #include <iosfwd>
@@ -157,8 +160,43 @@ public:
 private:
     std::vector<isc::dns::ConstRRsetPtr>& rrsets_;
 };
+
+class RRsetDumper {
+public:
+    RRsetDumper(std::string& output) :
+        output_(output)
+    {}
+    void operator()(isc::dns::ConstRRsetPtr rrset) {
+        output_ += "  " + rrset->toText();
+    }
+private:
+    std::string& output_;
+};
 }
 
+/// \brief A converter from a string to RRset.
+///
+/// This is a convenient shortcut for tests that need to create an RRset
+/// from textual representation with a single call to a function.
+///
+/// An RRset consisting of multiple RRs can be constructed, but only one
+/// RRset is allowed.  If the given string contains mixed types of RRs
+/// it throws an \c isc::Unexpected exception.
+///
+/// \param text_rrset A complete textual representation of an RRset.
+///  It must meets the assumption of the \c dns::masterLoad() function.
+/// \param rrclass The RR class of the RRset.  Note that \c text_rrset should
+/// contain the RR class, but it's needed for \c dns::masterLoad().
+/// \param origin The zone origin where the RR is expected to belong.  This
+/// parameter normally doesn't have to be specified, but for an SOA RR it
+/// must be set to its owner name, due to the internal check of
+/// \c dns::masterLoad().
+isc::dns::RRsetPtr textToRRset(const std::string& text_rrset,
+                               const isc::dns::RRClass& rrclass =
+                               isc::dns::RRClass::IN(),
+                               const isc::dns::Name& origin =
+                               isc::dns::Name::ROOT_NAME());
+
 /// Set of unit tests to check if two sets of RRsets are identical.
 ///
 /// This templated function takes two sets of sequences, each defined by
@@ -195,6 +233,10 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
             ACTUAL_ITERATOR actual_begin, ACTUAL_ITERATOR actual_end)
 {
     std::vector<isc::dns::ConstRRsetPtr> checked_rrsets; // for duplicate check
+    std::string expected_text, actual_text;
+    std::for_each(expected_begin, expected_end,
+                  detail::RRsetDumper(expected_text));
+    std::for_each(actual_begin, actual_end, detail::RRsetDumper(actual_text));
     unsigned int rrset_matched = 0;
     ACTUAL_ITERATOR it;
     for (it = actual_begin; it != actual_end; ++it) {
@@ -217,11 +259,16 @@ rrsetsCheck(EXPECTED_ITERATOR expected_begin, EXPECTED_ITERATOR expected_end,
         }
     }
 
-    // make sure all expected RRsets are in actual sets
-    EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
-    // make sure rrsets only contains expected RRsets
-    EXPECT_EQ(std::distance(expected_begin, expected_end),
-              std::distance(actual_begin, actual_end));
+    {
+        SCOPED_TRACE(std::string("Comparing two RRsets:\n") +
+                     "Actual:\n" + actual_text +
+                     "Expected:\n" + expected_text);
+        // make sure all expected RRsets are in actual sets
+        EXPECT_EQ(std::distance(expected_begin, expected_end), rrset_matched);
+        // make sure rrsets only contains expected RRsets
+        EXPECT_EQ(std::distance(expected_begin, expected_end),
+                  std::distance(actual_begin, actual_end));
+    }
 }
 
 /// Set of unit tests to check if two sets of RRsets are identical using
@@ -318,6 +365,7 @@ rrsetsCheck(const std::string& expected,
 
 } // end of namespace testutils
 } // end of namespace isc
+#endif  // __ISC_TESTUTILS_DNSMESSAGETEST_H
 
 // Local Variables:
 // mode: c++
diff --git a/src/lib/testutils/mockups.h b/src/lib/testutils/mockups.h
index 2441ad7..b5def4b 100644
--- a/src/lib/testutils/mockups.h
+++ b/src/lib/testutils/mockups.h
@@ -12,8 +12,13 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_MOCKUPS_H
+#define __ISC_TESTUTILS_MOCKUPS_H 1
+
 #include <config.h>
 
+#include <exceptions/exceptions.h>
+
 #include <cc/data.h>
 #include <cc/session.h>
 
@@ -21,6 +26,12 @@
 
 #include <asiodns/asiodns.h>
 
+#include <utility>
+#include <vector>
+
+namespace isc {
+namespace testutils {
+
 // A minimal mock configuration session.  Most the methods are
 // stubbed out, except for a very basic group_sendmsg() and
 // group_recvmsg().  hasQueuedMessages() always returns false.
@@ -93,6 +104,45 @@ private:
     bool receive_ok_;
 };
 
+// This mock object does nothing except for recording passed parameters
+// to addServerXXX methods so the test code subsequently checks the parameters.
+class MockDNSService : public isc::asiodns::DNSServiceBase {
+public:
+    // A helper tuple of parameters passed to addServerUDPFromFD().
+    struct UDPFdParams {
+        int fd;
+        int af;
+        ServerFlag options;
+    };
+
+    virtual void addServerTCPFromFD(int fd, int af) {
+        tcp_fd_params_.push_back(std::pair<int, int>(fd, af));
+    }
+    virtual void addServerUDPFromFD(int fd, int af, ServerFlag options) {
+        UDPFdParams params = { fd, af, options };
+        udp_fd_params_.push_back(params);
+    }
+    virtual void clearServers() {}
+
+    virtual asiolink::IOService& getIOService() {
+        isc_throw(isc::Unexpected,
+                  "MockDNSService::getIOService() shouldn't be called");
+    }
+
+    // These two allow the tests to check how the servers have been created
+    // through this object.
+    const std::vector<std::pair<int, int> >& getTCPFdParams() const {
+        return (tcp_fd_params_);
+    }
+    const std::vector<UDPFdParams>& getUDPFdParams() const {
+        return (udp_fd_params_);
+    }
+
+private:
+    std::vector<std::pair<int, int> > tcp_fd_params_;
+    std::vector<UDPFdParams> udp_fd_params_;
+};
+
 // A nonoperative DNSServer object to be used in calls to processMessage().
 class MockServer : public isc::asiodns::DNSServer {
 public:
@@ -149,3 +199,10 @@ private:
     bool disconnect_ok_;
 };
 
+} // end of testutils
+} // end of isc
+#endif  // __ISC_TESTUTILS_MOCKUPS_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/testutils/portconfig.h b/src/lib/testutils/portconfig.h
index b538478..ce1bb39 100644
--- a/src/lib/testutils/portconfig.h
+++ b/src/lib/testutils/portconfig.h
@@ -12,8 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
-#ifndef TESTUTILS_PORTCONFIG_H
-#define TESTUTILS_PORTCONFIG_H
+#ifndef __ISC_TESTUTILS_PORTCONFIG_H
+#define __ISC_TESTUTILS_PORTCONFIG_H
 
 #include <gtest/gtest.h>
 #include <cc/data.h>
@@ -186,4 +186,4 @@ invalidListenAddressConfig(Server& server) {
 }
 }
 
-#endif
+#endif  // __ISC_TESTUTILS_PORTCONFIG_H
diff --git a/src/lib/testutils/socket_request.h b/src/lib/testutils/socket_request.h
index 2c14120..5c76d30 100644
--- a/src/lib/testutils/socket_request.h
+++ b/src/lib/testutils/socket_request.h
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_SOCKETREQUEST_H
+#define __ISC_TESTUTILS_SOCKETREQUEST_H 1
+
 #include <server_common/socket_request.h>
 #include <server_common/portconfig.h>
 
@@ -24,13 +27,6 @@
 #include <string>
 
 namespace isc {
-namespace server_common {
-namespace portconfig {
-// Access the private hidden flag
-extern bool test_mode;
-}
-}
-
 namespace testutils {
 
 /// \brief A testcase part for faking the SocketRequestor in tests
@@ -64,7 +60,7 @@ public:
     ///     not fall back to this value if its share_name is left empty, if
     ///     you want to check the code relies on the requestor to use the
     ///     app name, you set this to empty string.
-    TestSocketRequestor(asiodns::DNSService& dnss,
+    TestSocketRequestor(asiodns::DNSServiceBase& dnss,
                         server_common::portconfig::AddressList& store,
                         uint16_t expect_port,
                         const std::string& expected_app) :
@@ -74,8 +70,6 @@ public:
     {
         // Prepare the requestor (us) for the test
         server_common::initTestSocketRequestor(this);
-        // Don't manipulate the real sockets
-        server_common::portconfig::test_mode = true;
     }
 
     /// \brief Destructor
@@ -90,8 +84,6 @@ public:
         server_common::portconfig::installListenAddresses(list, store_, dnss_);
         // Don't leave invalid pointers here
         server_common::initTestSocketRequestor(NULL);
-        // And return the mode
-        server_common::portconfig::test_mode = false;
     }
 
     /// \brief Tokens released by releaseSocket
@@ -216,7 +208,7 @@ public:
     }
 
 private:
-    asiodns::DNSService& dnss_;
+    asiodns::DNSServiceBase& dnss_;
     server_common::portconfig::AddressList& store_;
     const uint16_t expect_port_;
     const std::string expected_app_;
@@ -224,3 +216,4 @@ private:
 
 }
 }
+#endif  // __ISC_TESTUTILS_SOCKETREQUEST_H
diff --git a/src/lib/testutils/srv_test.cc b/src/lib/testutils/srv_test.cc
index 8b0053c..03aec01 100644
--- a/src/lib/testutils/srv_test.cc
+++ b/src/lib/testutils/srv_test.cc
@@ -14,6 +14,7 @@
 
 #include <config.h>
 
+#include <sys/types.h>
 #include <netinet/in.h>
 
 #include <dns/message.h>
@@ -43,8 +44,6 @@ SrvTestBase::SrvTestBase() : request_message(Message::RENDER),
                              qclass(RRClass::IN()),
                              qtype(RRType::A()), io_sock(NULL),
                              io_message(NULL), endpoint(NULL),
-                             request_obuffer(0),
-                             request_renderer(request_obuffer),
                              response_obuffer(new OutputBuffer(0))
 {}
 
diff --git a/src/lib/testutils/srv_test.h b/src/lib/testutils/srv_test.h
index 630232c..b5f64b5 100644
--- a/src/lib/testutils/srv_test.h
+++ b/src/lib/testutils/srv_test.h
@@ -12,6 +12,9 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#ifndef __ISC_TESTUTILS_SRVTEST_H
+#define __ISC_TESTUTILS_SRVTEST_H 1
+
 #include <util/buffer.h>
 #include <dns/name.h>
 #include <dns/message.h>
@@ -100,13 +103,13 @@ protected:
     asiolink::IOSocket* io_sock;
     asiolink::IOMessage* io_message;
     const asiolink::IOEndpoint* endpoint;
-    isc::util::OutputBuffer request_obuffer;
     isc::dns::MessageRenderer request_renderer;
     isc::util::OutputBufferPtr response_obuffer;
     std::vector<uint8_t> data;
 };
 } // end of namespace testutils
 } // end of namespace isc
+#endif  // __ISC_TESTUTILS_SRVTEST_H
 
 // Local Variables: 
 // mode: c++
diff --git a/src/lib/testutils/testdata/.gitignore b/src/lib/testutils/testdata/.gitignore
new file mode 100644
index 0000000..4d41a44
--- /dev/null
+++ b/src/lib/testutils/testdata/.gitignore
@@ -0,0 +1,15 @@
+/auth_test.sqlite3.copied
+/badExampleQuery_fromWire.wire
+/examplequery_fromWire.wire
+/iquery_fromWire.wire
+/iquery_response_fromWire.wire
+/iqueryresponse_fromWire.wire
+/multiquestion_fromWire.wire
+/nsec3query_fromWire.wire
+/nsec3query_nodnssec_fromWire.wire
+/queryBadEDNS_fromWire.wire
+/shortanswer_fromWire.wire
+/simplequery_fromWire.wire
+/simpleresponse_fromWire.wire
+/test1.zone.copied
+/test2.zone.copied
diff --git a/src/lib/testutils/testdata/Makefile.am b/src/lib/testutils/testdata/Makefile.am
index a6b8206..b9ef53f 100644
--- a/src/lib/testutils/testdata/Makefile.am
+++ b/src/lib/testutils/testdata/Makefile.am
@@ -27,6 +27,7 @@ EXTRA_DIST += rfc5155-example.zone.signed
 
 EXTRA_DIST += example.com
 EXTRA_DIST += example.sqlite3
+EXTRA_DIST += rwtest.sqlite3	# SQLite3 DB file as a template data source
 
 EXTRA_DIST += test1.zone.in
 EXTRA_DIST += test1-new.zone.in
diff --git a/src/lib/testutils/testdata/auth_test.sqlite3 b/src/lib/testutils/testdata/auth_test.sqlite3
new file mode 100755
index 0000000..5eeb2c3
Binary files /dev/null and b/src/lib/testutils/testdata/auth_test.sqlite3 differ
diff --git a/src/lib/testutils/testdata/example.sqlite3 b/src/lib/testutils/testdata/example.sqlite3
index e8e255b..0f6ee02 100644
Binary files a/src/lib/testutils/testdata/example.sqlite3 and b/src/lib/testutils/testdata/example.sqlite3 differ
diff --git a/src/lib/testutils/testdata/rfc5155-example.zone.signed b/src/lib/testutils/testdata/rfc5155-example.zone.signed
index 4120224..595c441 100644
--- a/src/lib/testutils/testdata/rfc5155-example.zone.signed
+++ b/src/lib/testutils/testdata/rfc5155-example.zone.signed
@@ -7,8 +7,8 @@ example.				      3600 IN NS	ns2.example.
 example.				      3600 IN RRSIG	NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
 example.				      3600 IN MX	1 xx.example.
 example.				      3600 IN RRSIG	MX 7 1 3600 20150420235959 20051021000000 40430 example. GgQ1A9xs47k42VPvpL/a1BWUz/6XsnHkjotw9So8MQtZtl2wJBsnOQsa oHrRCrRbyriEl/GZn9Mto/Kx+wBo+w==
-example.				      3600 IN DNSKEY	256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=
-example.				      3600 IN DNSKEY	257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=
+example.				      3600 IN DNSKEY	256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=  ; key id = 40430
+example.				      3600 IN DNSKEY	257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=  ; key id = 12708
 example.				      3600 IN RRSIG	DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31uzMZ/U/FpsUb8aC6QZS+ sTsJXnLnz7flGOsmMGQZf3bH+QsCtg==
 example.				      3600 IN NSEC3PARAM 1 0 12 AABBCCDD
 example.				      3600 IN RRSIG	NSEC3PARAM 7 1 3600 20150420235959 20051021000000 40430 example. C1Gl8tPZNtnjlrYWDeeUV/sGLCyy/IHie2rerN05XSA3Pq0U3+4VvGWY WdUMfflOdxqnXHwJTLQsjlkynhG6Cg==
diff --git a/src/lib/testutils/testdata/rwtest.sqlite3 b/src/lib/testutils/testdata/rwtest.sqlite3
new file mode 100644
index 0000000..5eeb2c3
Binary files /dev/null and b/src/lib/testutils/testdata/rwtest.sqlite3 differ
diff --git a/src/lib/util/buffer.h b/src/lib/util/buffer.h
index eb90d64..1ceeada 100644
--- a/src/lib/util/buffer.h
+++ b/src/lib/util/buffer.h
@@ -101,6 +101,17 @@ public:
     /// \param len The length of the data in bytes.
     InputBuffer(const void* data, size_t len) :
         position_(0), data_(static_cast<const uint8_t*>(data)), len_(len) {}
+
+    /// @brief Constructor from vector<uint8_t>
+    ///
+    /// It is caller's responsibility to ensure that the data is valid as long
+    /// as the buffer exists.
+    ///
+    /// @param begin iterator to beginning of the vector
+    /// @param end iterator to end of the vector
+    InputBuffer(std::vector<uint8_t>::const_iterator begin,
+                std::vector<uint8_t>::const_iterator end) :
+        position_(0), data_(&(*begin)), len_(distance(begin, end)) {}
     //@}
 
     ///
@@ -122,10 +133,10 @@ public:
     /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown.
     /// \param position The new position (offset from the beginning of the
     /// buffer).
-    void setPosition(size_t position)
-    {
-        if (position > len_)
-            isc_throw(InvalidBufferPosition, "position is too large");
+    void setPosition(size_t position) {
+        if (position > len_) {
+            throwError("position is too large");
+        }
         position_ = position;
     }
     //@}
@@ -137,10 +148,9 @@ public:
     ///
     /// If the remaining length of the buffer is smaller than 8-bit, an
     /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
-    uint8_t readUint8()
-    {
+    uint8_t readUint8() {
         if (position_ + sizeof(uint8_t) > len_) {
-            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+            throwError("read beyond end of buffer");
         }
 
         return (data_[position_++]);
@@ -150,13 +160,12 @@ public:
     ///
     /// If the remaining length of the buffer is smaller than 16-bit, an
     /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
-    uint16_t readUint16()
-    {
+    uint16_t readUint16() {
         uint16_t data;
         const uint8_t* cp;
 
         if (position_ + sizeof(data) > len_) {
-            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+            throwError("read beyond end of buffer");
         }
 
         cp = &data_[position_];
@@ -171,13 +180,12 @@ public:
     ///
     /// If the remaining length of the buffer is smaller than 32-bit, an
     /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
-    uint32_t readUint32()
-    {
+    uint32_t readUint32() {
         uint32_t data;
         const uint8_t* cp;
 
         if (position_ + sizeof(data) > len_) {
-            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+            throwError("read beyond end of buffer");
         }
 
         cp = &data_[position_];
@@ -196,10 +204,9 @@ public:
     /// If the remaining length of the buffer is smaller than the specified
     /// length, an exception of class \c isc::dns::InvalidBufferPosition will
     /// be thrown.
-    void readData(void* data, size_t len)
-    {
+    void readData(void* data, size_t len) {
         if (position_ + len > len_) {
-            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+            throwError("read beyond end of buffer");
         }
 
         memcpy(data, &data_[position_], len);
@@ -215,10 +222,9 @@ public:
     /// @param Reference to a buffer (data will be stored there).
     /// @param Size specified number of bytes to read in a vector.
     ///
-    void readVector(std::vector<uint8_t>& data, size_t len)
-    {
+    void readVector(std::vector<uint8_t>& data, size_t len) {
         if (position_ + len > len_) {
-            isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+            throwError("read beyond end of buffer");
         }
 
         data.resize(len);
@@ -226,6 +232,15 @@ public:
     }
 
 private:
+    /// \brief A common helper to throw an exception on invalid operation.
+    ///
+    /// Experiments showed that throwing from each method makes the buffer
+    /// operation slower, so we consolidate it here, and let the methods
+    /// call this.
+    static void throwError(const char* msg) {
+        isc_throw(InvalidBufferPosition, msg);
+    }
+
     size_t position_;
 
     // XXX: The following must be private, but for a short term workaround with
@@ -376,9 +391,7 @@ public:
     /// \param pos The position in the buffer to be returned.
     uint8_t operator[](size_t pos) const
     {
-        if (pos >= size_) {
-            isc_throw(InvalidBufferPosition, "read at invalid position");
-        }
+        assert (pos < size_);
         return (buffer_[pos]);
     }
     //@}
diff --git a/src/lib/util/io/Makefile.am b/src/lib/util/io/Makefile.am
index 96b9d25..2c3ed96 100644
--- a/src/lib/util/io/Makefile.am
+++ b/src/lib/util/io/Makefile.am
@@ -13,7 +13,7 @@ CLEANFILES = *.gcno *.gcda
 pyexec_LTLIBRARIES = libutil_io_python.la
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-libutil_io_python_la_LDFLAGS = -module
+libutil_io_python_la_LDFLAGS = -module -avoid-version
 libutil_io_python_la_SOURCES = fdshare_python.cc
 libutil_io_python_la_LIBADD = libutil_io.la
 libutil_io_python_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
diff --git a/src/lib/util/io/fd_share.cc b/src/lib/util/io/fd_share.cc
index 9c02a33..7adbbbe 100644
--- a/src/lib/util/io/fd_share.cc
+++ b/src/lib/util/io/fd_share.cc
@@ -20,6 +20,7 @@
 #include <sys/uio.h>
 #include <errno.h>
 #include <stdlib.h>             // for malloc and free
+#include <unistd.h>
 #include "fd_share.h"
 
 namespace isc {
@@ -106,7 +107,21 @@ recv_fd(const int sock) {
         std::memcpy(&fd, CMSG_DATA(cmsg), sizeof(int));
     }
     free(msghdr.msg_control);
-    return (fd);
+    // It is strange, but the call can return the same file descriptor as
+    // one returned previously, even if that one is not closed yet. So,
+    // we just re-number every one we get, so they are unique.
+    int new_fd(dup(fd));
+    int close_error(close(fd));
+    if (close_error == -1 || new_fd == -1) {
+        // We need to return an error, because something failed. But in case
+        // it was the previous close, we at least try to close the duped FD.
+        if (new_fd != -1) {
+            close(new_fd); // If this fails, nothing but returning error can't
+                           // be done and we are doing that anyway.
+        }
+        return (FD_SYSTEM_ERROR);
+    }
+    return (new_fd);
 }
 
 int
diff --git a/src/lib/util/io/pktinfo_utilities.h b/src/lib/util/io/pktinfo_utilities.h
new file mode 100644
index 0000000..9883c30
--- /dev/null
+++ b/src/lib/util/io/pktinfo_utilities.h
@@ -0,0 +1,51 @@
+// 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 __PKTINFO_UTIL_H_
+#define __PKTINFO_UTIL_H_ 1
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+// These definitions in this file are for the convenience of internal
+// implementation and test code, and are not intended to be used publicly.
+// The namespace "internal" indicates the intent.
+
+namespace isc {
+namespace util {
+namespace io {
+namespace internal {
+
+// Lower level C-APIs require conversion between char* pointers
+// (like structures returned by CMSG_DATA macro) and in6_pktinfo,
+// which is not friendly with C++. The following templates
+// are a shortcut of common workaround conversion in such cases.
+inline struct in6_pktinfo*
+convertPktInfo6(char* pktinfo) {
+    return (static_cast<struct in6_pktinfo*>(static_cast<void*>(pktinfo)));
+}
+
+inline struct in6_pktinfo*
+convertPktInfo6(unsigned char* pktinfo) {
+    return (static_cast<struct in6_pktinfo*>(static_cast<void*>(pktinfo)));
+}
+
+/// @todo: Do we need const versions as well?
+
+}
+}
+}
+}
+
+#endif  // __PKTINFO_UTIL_H_
diff --git a/src/lib/util/io/sockaddr_util.h b/src/lib/util/io/sockaddr_util.h
index 3ec6cf0..9b4a0cb 100644
--- a/src/lib/util/io/sockaddr_util.h
+++ b/src/lib/util/io/sockaddr_util.h
@@ -20,7 +20,7 @@
 
 #include <cassert>
 
-// This definitions in this file are for the convenience of internal
+// These definitions in this file are for the convenience of internal
 // implementation and test code, and are not intended to be used publicly.
 // The namespace "internal" indicates the intent.
 
diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h
index 971c413..daaf216 100644
--- a/src/lib/util/locks.h
+++ b/src/lib/util/locks.h
@@ -15,13 +15,9 @@
 /// This file (right now) provides dummy locks
 /// It also contains code to use boost/threads locks:
 ///
-/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
-/// and derive from the relevant templates so our dummy locks are
-/// replaced by the boost locks (--enable-boost-threads)
 ///
-/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
-/// that don't actually do anything. At this moment, only the very
-/// minimal set of methods that we actually use is defined.
+/// All locks are dummy classes that don't actually do anything. At this moment, 
+/// only the very minimal set of methods that we actually use is defined.
 ///
 /// Note that we need to include <config.h> in our .cc files for that
 /// to be set. we might want to enfore this at compile time with a check
@@ -30,8 +26,6 @@
 #ifndef __LOCKS_
 #define __LOCKS_
 
-#ifndef USE_BOOST_THREADS
-
 namespace isc {
 namespace util {
 namespace locks {
@@ -64,52 +58,4 @@ public:
 } // namespace util
 } // namespace isc
 
-#else // USE_BOOST_THREADS
-
-// Workaround for a problem with boost and sunstudio 5.10
-// There is a version check in there that appears wrong,
-// which makes including boost/thread.hpp fail
-// This will probably be fixed in a future version of boost,
-// in which case this part can be removed then
-#ifdef NEED_SUNPRO_WORKAROUND
-#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
-#undef __SUNPRO_CC
-#define __SUNPRO_CC 0x5090
-#endif
-#endif // NEED_SUNPRO_WORKAROUND
-
-#include <boost/thread.hpp>
-#include <boost/interprocess/sync/sharable_lock.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
-#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
-#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
-
-namespace isc {
-namespace util {
-namespace locks {
-
-typedef boost::mutex mutex;
-typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
-typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
-
-template <typename T>
-struct sharable_lock : public boost::interprocess::sharable_lock<T> {
-public:
-    sharable_lock(T&  mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
-};
-
-
-template <class T>
-struct scoped_lock : public boost::interprocess::scoped_lock<T> {
-public:
-    scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
-};
-
-} // namespace locks
-} // namespace util
-} // namespace isc
-
-
-#endif // USE_BOOST_THREADS
-
 #endif // __LOCKS_
diff --git a/src/lib/util/python/.gitignore b/src/lib/util/python/.gitignore
new file mode 100644
index 0000000..c54df80
--- /dev/null
+++ b/src/lib/util/python/.gitignore
@@ -0,0 +1,2 @@
+/gen_wiredata.py
+/mkpywrapper.py
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
index 8b3eac0..f997701 100755
--- a/src/lib/util/python/gen_wiredata.py.in
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -822,6 +822,29 @@ class RP(RR):
         f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text))
         f.write('%s %s\n' % (mailbox_wire, text_wire))
 
+class SSHFP(RR):
+    '''Implements rendering SSHFP RDATA in the test data format.
+
+    Configurable parameters are as follows (see the description of the
+    same name of attribute for the default value):
+    - algorithm (int): The algorithm number.
+    - fingerprint_type (int): The fingerprint type.
+    - fingerprint (string): The fingerprint.
+    '''
+    algorithm = 2
+    fingerprint_type = 1
+    fingerprint = '123456789abcdef67890123456789abcdef67890'
+    def dump(self, f):
+        if self.rdlen is None:
+            self.rdlen = 2 + (len(self.fingerprint) / 2)
+        else:
+            self.rdlen = int(self.rdlen)
+        self.dump_header(f, self.rdlen)
+        f.write('# ALGORITHM=%d FINGERPRINT_TYPE=%d FINGERPRINT=%s\n' % (self.algorithm,
+                                                                         self.fingerprint_type,
+                                                                         self.fingerprint))
+        f.write('%02x %02x %s\n' % (self.algorithm, self.fingerprint_type, self.fingerprint))
+
 class MINFO(RR):
     '''Implements rendering MINFO RDATA in the test data format.
 
diff --git a/src/lib/util/pyunittests/Makefile.am b/src/lib/util/pyunittests/Makefile.am
index dd2d39a..93b0748 100644
--- a/src/lib/util/pyunittests/Makefile.am
+++ b/src/lib/util/pyunittests/Makefile.am
@@ -13,7 +13,7 @@ pyunittests_util_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
 
 # Python prefers .so, while some OSes (specifically MacOS) use a different
 # suffix for dynamic objects.  -module is necessary to work this around.
-pyunittests_util_la_LDFLAGS += -module
+pyunittests_util_la_LDFLAGS += -module -avoid-version
 pyunittests_util_la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
 pyunittests_util_la_LIBADD += $(PYTHON_LIB)
 
diff --git a/src/lib/util/range_utilities.h b/src/lib/util/range_utilities.h
new file mode 100644
index 0000000..3ba2cb5
--- /dev/null
+++ b/src/lib/util/range_utilities.h
@@ -0,0 +1,68 @@
+// 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 __RANGE_UTIL_H_
+#define __RANGE_UTIL_H_ 1
+
+#include <cstdlib>
+#include <algorithm>
+
+// This header contains useful methods for conduction operations on
+// a range of container elements. Currently the collection is limited,
+// but it is expected to grow.
+
+namespace isc {
+namespace util {
+
+/// @brief Checks if specified range in a container contains only zeros
+///
+/// @param begin beginning of the range
+/// @param end end of the range
+///
+/// @return true if there are only zeroes, false otherwise
+template <typename Iterator>
+bool
+isRangeZero(Iterator begin, Iterator end) {
+    return (std::find_if(begin, end,
+                         std::bind1st(std::not_equal_to<int>(), 0))
+            == end);
+}
+
+/// @brief Fill in specified range with a random data.
+///
+/// Make sure that random number generator is initialized properly. Otherwise you
+/// will get a the same pseudo-random sequence after every start of your process.
+/// Calling srand() is enough. This method uses default rand(), which is usually
+/// a LCG pseudo-random number generator, so it is not suitable for security
+/// purposes. Please get a decent PRNG implementation, like mersene twister, if
+/// you are doing anything related with security.
+///
+/// PRNG initialization is left out of this function on purpose. It may be
+/// initialized to specific value on purpose, e.g. to repeat exactly the same
+/// sequence in a test.
+///
+/// @param begin
+/// @param end
+template <typename Iterator>
+void
+fillRandom(Iterator begin, Iterator end) {
+    for (Iterator x = begin; x != end; ++x) {
+        *x = std::rand();
+    }
+}
+
+} // end of isc::util namespace
+} // end of isc namespace
+
+#endif  // __PKTINFO_UTIL_H_
diff --git a/src/lib/util/tests/.gitignore b/src/lib/util/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/util/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index dc144bd..4aea951 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -34,6 +34,7 @@ run_unittests_SOURCES += sha1_unittest.cc
 run_unittests_SOURCES += socketsession_unittest.cc
 run_unittests_SOURCES += strutil_unittest.cc
 run_unittests_SOURCES += time_utilities_unittest.cc
+run_unittests_SOURCES += range_utilities_unittest.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
diff --git a/src/lib/util/tests/buffer_unittest.cc b/src/lib/util/tests/buffer_unittest.cc
index 666924e..9d924b3 100644
--- a/src/lib/util/tests/buffer_unittest.cc
+++ b/src/lib/util/tests/buffer_unittest.cc
@@ -12,11 +12,15 @@
 // 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/buffer.h>
+#ifdef EXPECT_DEATH
+#include <util/unittests/resource.h>
+#endif /* EXPECT_DEATH */
 
-#include <gtest/gtest.h>
+#include <util/buffer.h>
 
 using namespace isc;
 
@@ -182,7 +186,19 @@ TEST_F(BufferTest, outputBufferReadat) {
     for (int i = 0; i < sizeof(testdata); i ++) {
         EXPECT_EQ(testdata[i], obuffer[i]);
     }
-    EXPECT_THROW(obuffer[sizeof(testdata)], isc::util::InvalidBufferPosition);
+#ifdef EXPECT_DEATH
+    // We use assert now, so we check it dies
+    EXPECT_DEATH({
+        isc::util::unittests::dontCreateCoreDumps();
+
+        try {
+            obuffer[sizeof(testdata)];
+        } catch (...) {
+            // Prevent exceptions killing the application, we need
+            // to make sure it dies the real hard way
+        }
+        }, "");
+#endif
 }
 
 TEST_F(BufferTest, outputBufferClear) {
@@ -239,7 +255,7 @@ TEST_F(BufferTest, outputBufferZeroSize) {
     });
 }
 
-TEST_F(BufferTest, readVectorAll) {
+TEST_F(BufferTest, inputBufferReadVectorAll) {
     std::vector<uint8_t> vec;
 
     // check that vector can read the whole buffer
@@ -255,7 +271,7 @@ TEST_F(BufferTest, readVectorAll) {
     );
 }
 
-TEST_F(BufferTest, readVectorChunks) {
+TEST_F(BufferTest, inputBufferReadVectorChunks) {
     std::vector<uint8_t> vec;
 
     // check that vector can read the whole buffer
@@ -271,4 +287,19 @@ TEST_F(BufferTest, readVectorChunks) {
     EXPECT_EQ(0, memcmp(&vec[0], testdata+3, 2));
 }
 
+TEST_F(BufferTest, inputBufferConstructorVector) {
+    std::vector<uint8_t> vec(17);
+    for (int i = 0; i < vec.size(); i++) {
+        vec[i] = i;
+    }
+
+    InputBuffer buf(vec.begin(), vec.end());
+
+    EXPECT_EQ(buf.getLength(), 17);
+
+    std::vector<uint8_t> vec2;
+    EXPECT_NO_THROW(buf.readVector(vec2, 17));
+    EXPECT_TRUE(vec == vec2);
+}
+
 }
diff --git a/src/lib/util/tests/range_utilities_unittest.cc b/src/lib/util/tests/range_utilities_unittest.cc
new file mode 100644
index 0000000..31f9f21
--- /dev/null
+++ b/src/lib/util/tests/range_utilities_unittest.cc
@@ -0,0 +1,55 @@
+// 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 <gtest/gtest.h>
+#include <vector>
+
+#include <util/range_utilities.h>
+
+using namespace std;
+using namespace isc::util;
+
+TEST(RangeUtilitiesTest, isZero) {
+
+    vector<uint8_t> vec(32,0);
+
+    EXPECT_TRUE(isRangeZero(vec.begin(), vec.end()));
+
+    EXPECT_TRUE(isRangeZero(vec.begin(), vec.begin()+1));
+
+    vec[5] = 1;
+    EXPECT_TRUE(isRangeZero(vec.begin(), vec.begin()+5));
+    EXPECT_FALSE(isRangeZero(vec.begin(), vec.begin()+6));
+}
+
+TEST(RangeUtilitiesTest, randomFill) {
+
+    vector<uint8_t> vec1(16,0);
+    vector<uint8_t> vec2(16,0);
+
+    // Testing if returned value is actually random is extraordinary difficult.
+    // Let's just generate bunch of vectors and see if we get the same
+    // value. If we manage to do that in 100 tries, pseudo-random generator
+    // really sucks.
+    fillRandom(vec1.begin(), vec1.end());
+    for (int i=0; i<100; i++) {
+        fillRandom(vec2.begin(), vec2.end());
+        if (vec1 == vec2)
+            FAIL();
+    }
+
+}
diff --git a/src/lib/util/unittests/Makefile.am b/src/lib/util/unittests/Makefile.am
index bbb0d49..2827471 100644
--- a/src/lib/util/unittests/Makefile.am
+++ b/src/lib/util/unittests/Makefile.am
@@ -6,6 +6,7 @@ libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
 libutil_unittests_la_SOURCES += newhook.h newhook.cc
 libutil_unittests_la_SOURCES += testdata.h testdata.cc
 if HAVE_GTEST
+libutil_unittests_la_SOURCES += resource.h resource.cc
 libutil_unittests_la_SOURCES += run_all.h run_all.cc
 libutil_unittests_la_SOURCES += textdata.h
 endif
diff --git a/src/lib/util/unittests/resource.cc b/src/lib/util/unittests/resource.cc
new file mode 100644
index 0000000..3e77e0d
--- /dev/null
+++ b/src/lib/util/unittests/resource.cc
@@ -0,0 +1,35 @@
+// 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 "resource.h"
+
+#include <gtest/gtest.h>
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+void
+dontCreateCoreDumps() {
+    const rlimit core_limit = {0, 0};
+
+    EXPECT_EQ(setrlimit(RLIMIT_CORE, &core_limit), 0);
+}
+
+} // end of namespace unittests
+} // end of namespace util
+} // end of namespace isc
diff --git a/src/lib/util/unittests/resource.h b/src/lib/util/unittests/resource.h
new file mode 100644
index 0000000..6430ab2
--- /dev/null
+++ b/src/lib/util/unittests/resource.h
@@ -0,0 +1,39 @@
+// 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 __UTIL_UNITTESTS_RESOURCE_H
+#define __UTIL_UNITTESTS_RESOURCE_H 1
+
+namespace isc {
+namespace util {
+namespace unittests {
+
+/// Don't create core dumps.
+///
+/// This function sets the core size to 0, inhibiting the creation of
+/// core dumps. It is meant to be used in testcases where EXPECT_DEATH
+/// is used, where processes abort (and create cores in the process).
+/// As a new process is forked to run EXPECT_DEATH tests, the rlimits of
+/// the parent process that runs the other tests should be unaffected.
+void dontCreateCoreDumps();
+
+} // end of namespace unittests
+} // end of namespace util
+} // end of namespace isc
+
+#endif /* __UTIL_UNITTESTS_RESOURCE_H */
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/xfr/tests/.gitignore b/src/lib/xfr/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/src/lib/xfr/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/tests/lettuce/.gitignore b/tests/lettuce/.gitignore
new file mode 100644
index 0000000..f41154c
--- /dev/null
+++ b/tests/lettuce/.gitignore
@@ -0,0 +1,2 @@
+/output/
+/setup_intree_bind10.sh
diff --git a/tests/lettuce/README b/tests/lettuce/README
index 21a57c7..94bf82b 100644
--- a/tests/lettuce/README
+++ b/tests/lettuce/README
@@ -6,7 +6,7 @@ these tests are specific for BIND10, but we are keeping in mind that RFC-related
 tests could be separated, so that we can test other systems as well.
 
 Prerequisites:
-- Installed version of BIND 10 (but see below how to run it from source tree)
+- BIND 10 must be compiled or installed
 - dig
 - lettuce (http://lettuce.it)
 
@@ -26,27 +26,23 @@ Port 47805 is used for cmdctl, and must also be available.
 (note, we will need to extend this to a range, or if possible, we will need to
 do some on-the-fly available port finding)
 
-The bind10 main program, bindctl, and dig must all be in the default search 
-path of your environment, and BIND 10 must not be running if you use the 
-installed version when you run the tests.
+You can run the lettuce tests with the provided run_lettuce.sh script.
 
-If you want to test an installed version of bind 10, just run 'lettuce' in
-this directory.
+By default it will use the build tree, but you can use an installed version
+of bind10 by passing -I as the first argument of run_lettuce.sh
 
-We have provided a script that sets up the shell environment to run the tests
-with the build tree version of bind. If your shell uses export to set
-environment variables, you can source the script setup_intree_bind10.sh, then
-run lettuce.
+The tool 'dig' must be in the default search path of your environment. If
+you specified -I, so must the main bind10 program, as well as bindctl.
 
 Due to the default way lettuce prints its output, it is advisable to run it
 in a terminal that is wide than the default. If you see a lot of lines twice
 in different colors, the terminal is not wide enough.
 
 If you just want to run one specific feature test, use
-lettuce features/<feature file>
+run_lettuce.sh [-I] features/<feature file>
 
 To run a specific scenario from a feature, use
-lettuce features/<feature file> -s <scenario number>
+run_lettuce.sh [-I] features/<feature file> -s <scenario number>
 
 We have set up the tests to assume that lettuce is run from this directory,
 so even if you specify a specific feature file, you should do it from this
diff --git a/tests/lettuce/README.tutorial b/tests/lettuce/README.tutorial
index c7d3cd7..7d1c801 100644
--- a/tests/lettuce/README.tutorial
+++ b/tests/lettuce/README.tutorial
@@ -54,7 +54,7 @@ The one scenario we have has no steps, so if we run it we should
 see something like:
 
 -- output
-> lettuce
+> ./run_lettuce.sh
 Feature: showing off BIND 10
   This is to show BIND 10 running and that it answer queries
 
@@ -95,7 +95,7 @@ it to be started before we continue.
 And let's run the tests again.
 
 --
-> lettuce
+> ./run_lettuce.sh
 
 Feature: showing off BIND 10
   This is to show BIND 10 running and that it answer queries
diff --git a/tests/lettuce/configurations/.gitignore b/tests/lettuce/configurations/.gitignore
new file mode 100644
index 0000000..69d136f
--- /dev/null
+++ b/tests/lettuce/configurations/.gitignore
@@ -0,0 +1,2 @@
+/bindctl_commands.config
+/example.org.config
diff --git a/tests/lettuce/configurations/bindctl/bindctl.config.orig b/tests/lettuce/configurations/bindctl/bindctl.config.orig
new file mode 100644
index 0000000..bf623c5
--- /dev/null
+++ b/tests/lettuce/configurations/bindctl/bindctl.config.orig
@@ -0,0 +1,22 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "*"
+        } ]
+    },
+    "Auth": {
+        "database_file": "data/example.org.sqlite3",
+        "listen_on": [ {
+            "port": 47806,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}
diff --git a/tests/lettuce/configurations/bindctl_commands.config.orig b/tests/lettuce/configurations/bindctl_commands.config.orig
new file mode 100644
index 0000000..d74b96e
--- /dev/null
+++ b/tests/lettuce/configurations/bindctl_commands.config.orig
@@ -0,0 +1,34 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "*"
+        } ]
+    },
+    "Auth": {
+        "database_file": "data/example.org.sqlite3",
+        "listen_on": [ {
+            "port": 47806,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "StatsHttpd": {
+        "listen_on": [ {
+            "port": 47811,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "dispensable", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
+            "b10-stats-httpd": { "address": "StatsHttpd", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}
diff --git a/tests/lettuce/configurations/default.config b/tests/lettuce/configurations/default.config
new file mode 100644
index 0000000..9e1d3d1
--- /dev/null
+++ b/tests/lettuce/configurations/default.config
@@ -0,0 +1,16 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "*"
+        } ]
+    },
+    "StatsHttpd": {
+        "listen_on": [ {
+            "port": 47811,
+            "address": "127.0.0.1"
+        } ]
+    }
+}
diff --git a/tests/lettuce/configurations/example.org.config.orig b/tests/lettuce/configurations/example.org.config.orig
index 642f2dd..fadb3e2 100644
--- a/tests/lettuce/configurations/example.org.config.orig
+++ b/tests/lettuce/configurations/example.org.config.orig
@@ -4,7 +4,7 @@
         "loggers": [ {
             "debuglevel": 99,
             "severity": "DEBUG",
-            "name": "auth"
+            "name": "*"
         } ]
     },
     "Auth": {
@@ -13,5 +13,11 @@
             "port": 47806,
             "address": "127.0.0.1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }
diff --git a/tests/lettuce/configurations/example.org.inmem.config b/tests/lettuce/configurations/example.org.inmem.config
new file mode 100644
index 0000000..6418c65
--- /dev/null
+++ b/tests/lettuce/configurations/example.org.inmem.config
@@ -0,0 +1,8 @@
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Auth": {"database_file": "", "listen_on": [{"port": 47806, "address": "127.0.0.1"}], "datasources": [{"zones": [{"origin": "example.org", "file": "data/example.org"}], "type": "memory"}]},
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}
diff --git a/tests/lettuce/configurations/example2.org.config b/tests/lettuce/configurations/example2.org.config
index 1a40d1b..25314dc 100644
--- a/tests/lettuce/configurations/example2.org.config
+++ b/tests/lettuce/configurations/example2.org.config
@@ -3,7 +3,7 @@
     "Logging": {
         "loggers": [ {
             "severity": "DEBUG",
-            "name": "auth",
+            "name": "*",
             "debuglevel": 99
         }
         ]
@@ -12,7 +12,13 @@
         "database_file": "data/example.org.sqlite3",
         "listen_on": [ {
             "port": 47807,
-            "address": "127.0.0.1"
+            "address": "::1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }
diff --git a/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf b/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
new file mode 100644
index 0000000..a104726
--- /dev/null
+++ b/tests/lettuce/configurations/inmemory_over_sqlite3/secondary.conf
@@ -0,0 +1,32 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "auth"
+        } ]
+    },
+    "Auth": {
+        "datasources": [ {
+            "type": "memory",
+            "zones": [ {
+                "origin": "example.org",
+                "file": "data/example.org.sqlite3",
+	        "filetype": "sqlite3"
+            } ]
+	} ],
+        "listen_on": [ {
+            "port": 47806,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}
diff --git a/tests/lettuce/configurations/ixfr-out/testset1-config.db b/tests/lettuce/configurations/ixfr-out/testset1-config.db
index c5fc165..1c1b990 100644
--- a/tests/lettuce/configurations/ixfr-out/testset1-config.db
+++ b/tests/lettuce/configurations/ixfr-out/testset1-config.db
@@ -1 +1,11 @@
-{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.slite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]}}
+{"Xfrin": {"zones": [{"use_ixfr": true, "class": "IN", "name": "example.com.", "master_addr": "178.18.82.80"}]}, "version": 2, "Logging": {"loggers": [{"debuglevel": 99, "severity": "DEBUG", "output_options": [{"output": "stderr", "flush": true}], "name": "*"}]}, "Auth": {"database_file": "data/ixfr-out/zones.sqlite3", "listen_on": [{"port": 47806, "address": "::"}, {"port": 47806, "address": "0.0.0.0"}]},
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
+    }
+}
diff --git a/tests/lettuce/configurations/multi_instance/.gitignore b/tests/lettuce/configurations/multi_instance/.gitignore
new file mode 100644
index 0000000..9509290
--- /dev/null
+++ b/tests/lettuce/configurations/multi_instance/.gitignore
@@ -0,0 +1 @@
+/multi_auth.config
diff --git a/tests/lettuce/configurations/multi_instance/multi_auth.config.orig b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
new file mode 100644
index 0000000..35b8e72
--- /dev/null
+++ b/tests/lettuce/configurations/multi_instance/multi_auth.config.orig
@@ -0,0 +1,24 @@
+{
+    "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "debuglevel": 99,
+            "severity": "DEBUG",
+            "name": "*"
+        } ]
+    },
+    "Auth": {
+        "database_file": "data/test_nonexistent_db.sqlite3",
+        "listen_on": [ {
+            "port": 47806,
+            "address": "127.0.0.1"
+        } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth-2": {"kind": "dispensable", "special": "auth"},
+            "b10-auth": {"kind": "dispensable", "special": "auth"},
+            "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}
+        }
+    }
+}
diff --git a/tests/lettuce/configurations/no_db_file.config b/tests/lettuce/configurations/no_db_file.config
index f865354..fc0a25d 100644
--- a/tests/lettuce/configurations/no_db_file.config
+++ b/tests/lettuce/configurations/no_db_file.config
@@ -1,10 +1,24 @@
 {
     "version": 2,
+    "Logging": {
+        "loggers": [ {
+            "severity": "DEBUG",
+            "name": "*",
+            "debuglevel": 99
+        }
+        ]
+    },
     "Auth": {
         "database_file": "data/test_nonexistent_db.sqlite3",
         "listen_on": [ {
             "port": 47806,
             "address": "127.0.0.1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }
diff --git a/tests/lettuce/configurations/nsec3/nsec3_auth.config b/tests/lettuce/configurations/nsec3/nsec3_auth.config
new file mode 100644
index 0000000..94514c0
--- /dev/null
+++ b/tests/lettuce/configurations/nsec3/nsec3_auth.config
@@ -0,0 +1 @@
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Auth": {"datasources": [{"zones": [{"origin": "example.", "file": "configurations/nsec3/rfc5155-example.zone.signed"}], "type": "memory"}], "listen_on": [{"port": 47806, "address": "0.0.0.0"}]}, "Boss": {"components": {"b10-auth": {"kind": "needed", "special": "auth"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
diff --git a/tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed b/tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed
new file mode 100644
index 0000000..4120224
--- /dev/null
+++ b/tests/lettuce/configurations/nsec3/rfc5155-example.zone.signed
@@ -0,0 +1,72 @@
+;; The example NSEC3-signed zone used in RFC5155.
+
+example.				      3600 IN SOA	ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+example.				      3600 IN RRSIG	SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+example.				      3600 IN NS	ns1.example.
+example.				      3600 IN NS	ns2.example.
+example.				      3600 IN RRSIG	NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+example.				      3600 IN MX	1 xx.example.
+example.				      3600 IN RRSIG	MX 7 1 3600 20150420235959 20051021000000 40430 example. GgQ1A9xs47k42VPvpL/a1BWUz/6XsnHkjotw9So8MQtZtl2wJBsnOQsa oHrRCrRbyriEl/GZn9Mto/Kx+wBo+w==
+example.				      3600 IN DNSKEY	256 3 7 AwEAAaetidLzsKWUt4swWR8yu0wPHPiUi8LUsAD0QPWU+wzt89epO6tH zkMBVDkC7qphQO2hTY4hHn9npWFRw5BYubE=
+example.				      3600 IN DNSKEY	257 3 7 AwEAAcUlFV1vhmqx6NSOUOq2R/dsR7Xm3upJj7IommWSpJABVfW8Q0rO vXdM6kzt+TAu92L9AbsUdblMFin8CVF3n4s=
+example.				      3600 IN RRSIG	DNSKEY 7 1 3600 20150420235959 20051021000000 12708 example. AuU4juU9RaxescSmStrQks3Gh9FblGBlVU31uzMZ/U/FpsUb8aC6QZS+ sTsJXnLnz7flGOsmMGQZf3bH+QsCtg==
+example.				      3600 IN NSEC3PARAM 1 0 12 AABBCCDD
+example.				      3600 IN RRSIG	NSEC3PARAM 7 1 3600 20150420235959 20051021000000 40430 example. C1Gl8tPZNtnjlrYWDeeUV/sGLCyy/IHie2rerN05XSA3Pq0U3+4VvGWY WdUMfflOdxqnXHwJTLQsjlkynhG6Cg==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.     3600 IN A		192.0.2.127
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.     3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. h6c++bzhRuWWt2bykN6mjaTNBcXNq5UuL5EdK+iDP4eY8I0kSiKaCjg3 tC1SQkeloMeub2GWk8p6xHMPZumXlw==
+a.example.				      3600 IN NS	ns1.a.example.
+a.example.				      3600 IN NS	ns2.a.example.
+a.example.				      3600 IN DS	58470 5 1 3079F1593EBAD6DC121E202A8B766A6A4837206C
+a.example.				      3600 IN RRSIG	DS 7 2 3600 20150420235959 20051021000000 40430 example. XacFcQVHLVzdoc45EJhN616zQ4mEXtE8FzUhM2KWjfy1VfRKD9r1MeVG wwoukOKgJxBPFsWoo722vZ4UZ2dIdA==
+ns1.a.example.				      3600 IN A		192.0.2.5
+ns2.a.example.				      3600 IN A		192.0.2.6
+ai.example.				      3600 IN A		192.0.2.9
+ai.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+ai.example.				      3600 IN HINFO	"KLH-10" "ITS"
+ai.example.				      3600 IN RRSIG	HINFO 7 2 3600 20150420235959 20051021000000 40430 example. Yi42uOq43eyO6qXHNvwwfFnIustWgV5urFcxenkLvs6pKRh00VBjODmf 3Z4nMO7IOl6nHSQ1v0wLHpEZG7Xj2w==
+ai.example.				      3600 IN AAAA	2001:db8::f00:baa9
+ai.example.				      3600 IN RRSIG	AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+c.example.				      3600 IN NS	ns1.c.example.
+c.example.				      3600 IN NS	ns2.c.example.
+ns1.c.example.				      3600 IN A		192.0.2.7
+ns2.c.example.				      3600 IN A		192.0.2.8
+ns1.example.				      3600 IN A		192.0.2.1
+ns1.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+ns2.example.				      3600 IN A		192.0.2.2
+ns2.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+*.w.example.				      3600 IN MX	1 ai.example.
+*.w.example.				      3600 IN RRSIG	MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+x.w.example.				      3600 IN MX	1 xx.example.
+x.w.example.				      3600 IN RRSIG	MX 7 3 3600 20150420235959 20051021000000 40430 example. IrK3tq/tHFIBF0scHiE/1IwMAvckS/55hAVvQyxTFbkAdDloP3NbZzu+ yoSsr3b3OX6qbBpY7WCtwwekLKRAwQ==
+x.y.w.example.				      3600 IN MX	1 xx.example.
+x.y.w.example.				      3600 IN RRSIG	MX 7 4 3600 20150420235959 20051021000000 40430 example. MqSt5HqJIN8+SLlzTOImrh5h9Xa6gDvAW/GnnbdPc6Z7nXvCpLPJj/5l Cwx3VuzVOjkbvXze8/8Ccl2Zn2hbug==
+xx.example.				      3600 IN A		192.0.2.10
+xx.example.				      3600 IN RRSIG	A 7 2 3600 20150420235959 20051021000000 40430 example. T35hBWEZ017VC5u2c4OriKyVn/pu+fVK4AlXYOxJ6iQylfV2HQIKjv6b 7DzINB3aF/wjJqgXpQvhq+Ac6+ZiFg==
+xx.example.				      3600 IN HINFO	"KLH-10" "TOPS-20"
+xx.example.				      3600 IN RRSIG	HINFO 7 2 3600 20150420235959 20051021000000 40430 example. KimG+rDd+7VA1zRsu0ITNAQUTRlpnsmqWrihFRnU+bRa93v2e5oFNFYC s3Rqgv62K93N7AhW6Jfqj/8NzWjvKg==
+xx.example.				      3600 IN AAAA	2001:db8::f00:baaa
+xx.example.				      3600 IN RRSIG	AAAA 7 2 3600 20150420235959 20051021000000 40430 example. IXBcXORITNwd8h3gNwyxtYFvAupS/CYWufVeuBUX0O25ivBCULjZjpDx FSxfohb/KA7YRdxENzYfMItpILl/Xw==
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.     3600 IN NSEC3	1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.     3600 IN NSEC3	1 1 12 AABBCCDD 2VPTU5TIMAMQTTGL4LUU9KG21E0AOR3S A RRSIG
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
+2vptu5timamqttgl4luu9kg21e0aor3s.example.     3600 IN NSEC3	1 1 12 AABBCCDD 35MTHGPGCU1QG68FAB165KLNSNK3DPVL MX RRSIG
+2vptu5timamqttgl4luu9kg21e0aor3s.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. KL1V2oFYghNV0Hm7Tf2vpJjM6l+0g1JCcVYGVfI0lKrhPmTsOA96cLEA Cgo1x8I7kApJX+obTuktZ+sdsZPY1w==
+35mthgpgcu1qg68fab165klnsnk3dpvl.example.     3600 IN NSEC3	1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
+35mthgpgcu1qg68fab165klnsnk3dpvl.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+b4um86eghhds6nea196smvmlo4ors995.example.     3600 IN NSEC3	1 1 12 AABBCCDD GJEQE526PLBF1G8MKLP59ENFD789NJGI MX RRSIG
+b4um86eghhds6nea196smvmlo4ors995.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+gjeqe526plbf1g8mklp59enfd789njgi.example.     3600 IN NSEC3	1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+gjeqe526plbf1g8mklp59enfd789njgi.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.     3600 IN NSEC3	1 1 12 AABBCCDD K8UDEMVP1J2F7EG6JEBPS17VP3N8I58H
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example.     3600 IN NSEC3	1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example.     3600 IN NSEC3	1 1 12 AABBCCDD Q04JKCEVQVMU85R014C7DKBA38O0JI5R A RRSIG
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. VrDXs2uVW21N08SyQIz88zml+y4ZCInTwgDr6zz43yAg+LFERjOrj3Oj ct51ac7Dp4eZbf9FQJazmASFKGxGXg==
+q04jkcevqvmu85r014c7dkba38o0ji5r.example.     3600 IN NSEC3	1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
+q04jkcevqvmu85r014c7dkba38o0ji5r.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.     3600 IN NSEC3	1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+t644ebqk9bibcna874givr6joj62mlhv.example.     3600 IN NSEC3	1 1 12 AABBCCDD 0P9MHAVEQVM6T7VBL5LOP2U3T2RP3TOM A HINFO AAAA RRSIG
+t644ebqk9bibcna874givr6joj62mlhv.example.     3600 IN RRSIG	NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. RAjGECB8P7O+F4Pa4Dx3tC0M+Z3KmlLKImcafb9XWwx+NWUNz7NBEDBQ HivIyKPVDkChcePIX1xPl1ATNa+8Dw==
diff --git a/tests/lettuce/configurations/resolver/.gitignore b/tests/lettuce/configurations/resolver/.gitignore
new file mode 100644
index 0000000..8d60553
--- /dev/null
+++ b/tests/lettuce/configurations/resolver/.gitignore
@@ -0,0 +1 @@
+/resolver_basic.config
diff --git a/tests/lettuce/configurations/resolver/resolver_basic.config.orig b/tests/lettuce/configurations/resolver/resolver_basic.config.orig
index 5664c20..0adca9f 100644
--- a/tests/lettuce/configurations/resolver/resolver_basic.config.orig
+++ b/tests/lettuce/configurations/resolver/resolver_basic.config.orig
@@ -1 +1 @@
-{"version": 2, "Resolver": {"query_acl": [{"action": "REJECT", "from": "127.0.0.1"}], "listen_on": [{"port": 47806, "address": "127.0.0.1"}]}, "Boss": {"components": {"b10-resolver": {"kind": "needed"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
+{"version": 2, "Logging": {"loggers": [{"severity": "DEBUG", "name": "*", "debuglevel": 99}]}, "Resolver": {"query_acl": [{"action": "REJECT", "from": "127.0.0.1"}], "listen_on": [{"port": 47806, "address": "127.0.0.1"}]}, "Boss": {"components": {"b10-resolver": {"kind": "needed"}, "b10-cmdctl": {"kind": "needed", "special": "cmdctl"}}}}
diff --git a/tests/lettuce/configurations/xfrin/retransfer_master.conf b/tests/lettuce/configurations/xfrin/retransfer_master.conf
index 95cd88e..eae47a6 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_master.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_master.conf
@@ -4,19 +4,27 @@
         "loggers": [ {
             "debuglevel": 99,
             "severity": "DEBUG",
-            "name": "auth"
+            "name": "*"
         } ]
     },
     "Auth": {
         "database_file": "data/example.org.sqlite3",
         "listen_on": [ {
             "port": 47807,
-            "address": "127.0.0.1"
+            "address": "::1"
         } ]
     },
     "Xfrout": {
         "zone_config": [ {
             "origin": "example.org"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }
diff --git a/tests/lettuce/configurations/xfrin/retransfer_slave.conf b/tests/lettuce/configurations/xfrin/retransfer_slave.conf
index 51622cd..2296b8f 100644
--- a/tests/lettuce/configurations/xfrin/retransfer_slave.conf
+++ b/tests/lettuce/configurations/xfrin/retransfer_slave.conf
@@ -4,7 +4,7 @@
         "loggers": [ {
             "debuglevel": 99,
             "severity": "DEBUG",
-            "name": "auth"
+            "name": "*"
         } ]
     },
     "Auth": {
@@ -13,5 +13,13 @@
             "port": 47806,
             "address": "127.0.0.1"
         } ]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": { "kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }
diff --git a/tests/lettuce/data/.gitignore b/tests/lettuce/data/.gitignore
new file mode 100644
index 0000000..8c54200
--- /dev/null
+++ b/tests/lettuce/data/.gitignore
@@ -0,0 +1,2 @@
+/inmem-xfrin.sqlite3
+/test_nonexistent_db.sqlite3
diff --git a/tests/lettuce/data/commands/bad_command b/tests/lettuce/data/commands/bad_command
new file mode 100644
index 0000000..95d1694
--- /dev/null
+++ b/tests/lettuce/data/commands/bad_command
@@ -0,0 +1,9 @@
+!echo shouldshow
+# just add something so the test can verify it's reverted
+config add /Boss/components b10-auth
+config set /Boss/components/b10-auth/kind needed
+config set /Boss/components/b10-auth/special auth
+bad command
+# this should not be reached
+!echo shouldnotshow
+config commit
diff --git a/tests/lettuce/data/commands/directives b/tests/lettuce/data/commands/directives
new file mode 100644
index 0000000..4fe10f5
--- /dev/null
+++ b/tests/lettuce/data/commands/directives
@@ -0,0 +1,19 @@
+# this is a comment: commentexample1
+!echo this is an echo: echoexample2
+!verbose on
+# this is a comment with verbose on: verbosecommentexample3
+!verbose off
+# this is a comment with verbose off again: commentexample4
+# empty lines and lines with only whitespace should be ignored
+
+
+
+	
+	    	
+# directives are case insensitive, and should handle whitespace
+!ECHO echoexample5
+!eChO echoexample6
+!Verbose     ON
+# verbosecommentexample7
+!verBOSE		off	
+# commentexample8
diff --git a/tests/lettuce/data/commands/empty b/tests/lettuce/data/commands/empty
new file mode 100644
index 0000000..e69de29
diff --git a/tests/lettuce/data/commands/nested b/tests/lettuce/data/commands/nested
new file mode 100644
index 0000000..c153694
--- /dev/null
+++ b/tests/lettuce/data/commands/nested
@@ -0,0 +1,2 @@
+# include a different file
+execute file data/commands/nested1
diff --git a/tests/lettuce/data/commands/nested1 b/tests/lettuce/data/commands/nested1
new file mode 100644
index 0000000..8f984d5
--- /dev/null
+++ b/tests/lettuce/data/commands/nested1
@@ -0,0 +1,2 @@
+# this is included by nested
+!echo shouldshow
diff --git a/tests/lettuce/data/empty_db.sqlite3 b/tests/lettuce/data/empty_db.sqlite3
index f27a8b8..c434e30 100644
Binary files a/tests/lettuce/data/empty_db.sqlite3 and b/tests/lettuce/data/empty_db.sqlite3 differ
diff --git a/tests/lettuce/data/example.org b/tests/lettuce/data/example.org
new file mode 100644
index 0000000..20a93be
--- /dev/null
+++ b/tests/lettuce/data/example.org
@@ -0,0 +1,12 @@
+example.org.	3600	IN	SOA	ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+example.org.	3600	IN	NS	ns1.example.org.
+example.org.	3600	IN	NS	ns2.example.org.
+example.org.	3600	IN	MX	10 mail.example.org.
+www.example.org.	3600	IN	A	192.0.2.1
+mail.example.org.	3600	IN	A	192.0.2.10
+sub.example.org.	3600	IN	NS	ns.sub.example.org.
+ns.sub.example.org.	3600	IN	A	192.0.2.101
+dname.example.org.	3600	IN	DNAME	dname.example.info.
+dname2.foo.example.org.	3600	IN	DNAME	dname2.example.info.
+ns1.example.org.	3600	IN	A	192.0.2.3
+ns2.example.org.	3600	IN	A	192.0.2.4
diff --git a/tests/lettuce/data/example.org.sqlite3 b/tests/lettuce/data/example.org.sqlite3
index 070012f..427fa24 100644
Binary files a/tests/lettuce/data/example.org.sqlite3 and b/tests/lettuce/data/example.org.sqlite3 differ
diff --git a/tests/lettuce/data/ixfr-out/.gitignore b/tests/lettuce/data/ixfr-out/.gitignore
new file mode 100644
index 0000000..f8de78e
--- /dev/null
+++ b/tests/lettuce/data/ixfr-out/.gitignore
@@ -0,0 +1 @@
+/zones.sqlite3
diff --git a/tests/lettuce/data/ixfr-out/zones.slite3 b/tests/lettuce/data/ixfr-out/zones.slite3
deleted file mode 100644
index a2b2dbd..0000000
Binary files a/tests/lettuce/data/ixfr-out/zones.slite3 and /dev/null differ
diff --git a/tests/lettuce/data/ixfr-out/zones.sqlite3 b/tests/lettuce/data/ixfr-out/zones.sqlite3
new file mode 100644
index 0000000..311d335
Binary files /dev/null and b/tests/lettuce/data/ixfr-out/zones.sqlite3 differ
diff --git a/tests/lettuce/features/bindctl_commands.feature b/tests/lettuce/features/bindctl_commands.feature
index 872064f..1ab506d 100644
--- a/tests/lettuce/features/bindctl_commands.feature
+++ b/tests/lettuce/features/bindctl_commands.feature
@@ -1,38 +1,156 @@
 Feature: control with bindctl
     Assorted tests using bindctl for the administration of BIND 10.
 
+
     Scenario: Removing modules
-    # This test runs the original example configuration, which has
-    # a number of modules. It then removes all non-essential modules,
-    # and checks whether they do disappear from the list of running
-    # modules (note that it 'misuses' the help command for this,
-    # there is a Boss command 'show_processes' but it's output is
-    # currently less standardized than 'help')
-    Given I have bind10 running with configuration example.org.config
-
-    Then remove bind10 configuration Boss/components/NOSUCHMODULE
-    last bindctl output should contain Error
-
-    bind10 module Xfrout should be running
-    bind10 module Stats should be running
-    bind10 module Zonemgr should be running
-    bind10 module Xfrin should be running
-    bind10 module Auth should be running
-    bind10 module StatsHttpd should be running
-
-    Then remove bind10 configuration Boss/components value b10-xfrout
-    last bindctl output should not contain Error
-    # assuming it won't error for further modules (if it does, the final
-    # 'should not be running' tests would fail anyway)
-    Then remove bind10 configuration Boss/components value b10-stats
-    Then remove bind10 configuration Boss/components value b10-zonemgr
-    Then remove bind10 configuration Boss/components value b10-xfrin
-    Then remove bind10 configuration Boss/components value b10-auth
-    Then remove bind10 configuration Boss/components value b10-stats-httpd
-
-    bind10 module Xfrout should not be running
-    bind10 module Stats should not be running
-    bind10 module Zonemgr should not be running
-    bind10 module Xfrin should not be running
-    bind10 module Auth should not be running
-    bind10 module StatsHttpd should not be running
+        # This test runs the original example configuration, which has
+        # a number of modules. It then removes all non-essential modules,
+        # and checks whether they do disappear from the list of running
+        # modules (note that it 'misuses' the help command for this,
+        # there is a Boss command 'show_processes' but it's output is
+        # currently less standardized than 'help')
+        Given I have bind10 running with configuration bindctl_commands.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message ZONEMGR_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message XFRIN_STARTED
+        And wait for bind10 stderr message XFROUT_STARTED
+        And wait for bind10 stderr message STATS_STARTING
+        And wait for bind10 stderr message STATHTTPD_STARTED
+
+        Then remove bind10 configuration Boss/components/NOSUCHMODULE
+        last bindctl output should contain Error
+
+        bind10 module Xfrout should be running
+        bind10 module Stats should be running
+        bind10 module Zonemgr should be running
+        bind10 module Xfrin should be running
+        bind10 module Auth should be running
+        bind10 module StatsHttpd should be running
+        bind10 module Resolver should not be running
+
+        Then remove bind10 configuration Boss/components value b10-xfrout
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        last bindctl output should not contain Error
+
+        # assuming it won't error for further modules (if it does, the final
+        # 'should not be running' tests would fail anyway)
+        Then remove bind10 configuration Boss/components value b10-stats-httpd
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        last bindctl output should not contain Error
+
+        Then remove bind10 configuration Boss/components value b10-stats
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        last bindctl output should not contain Error
+
+        Then remove bind10 configuration Boss/components value b10-zonemgr
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        last bindctl output should not contain Error
+
+        Then remove bind10 configuration Boss/components value b10-xfrin
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        last bindctl output should not contain Error
+
+        Then remove bind10 configuration Boss/components value b10-auth
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        last bindctl output should not contain Error
+
+        # After these ^^^ have been stopped...
+        bind10 module Xfrout should not be running
+        bind10 module Zonemgr should not be running
+        bind10 module Xfrin should not be running
+        bind10 module Auth should not be running
+        bind10 module StatsHttpd should not be running
+        bind10 module Stats should not be running
+        bind10 module Resolver should not be running
+
+    Scenario: Executing scripts from files
+        # This test tests the 'execute' command, which reads and executes
+        # bindctl commands from a file
+        Given I have bind10 running with configuration bindctl/bindctl.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+
+        # first a few bad commands
+        When I send bind10 the command execute
+        last bindctl output should contain Error
+        When I send bind10 the command execute file
+        last bindctl output should contain Error
+        When I send bind10 the command execute file data/commands/nosuchfile
+        last bindctl output should contain Error
+
+        # empty list should be no-op
+        When I send bind10 the command execute file data/commands/empty
+        last bindctl output should not contain Error
+
+        # some tests of directives like !echo and !verbose
+        When I send bind10 the command execute file data/commands/directives
+        last bindctl output should not contain Error
+        last bindctl output should not contain commentexample1
+        last bindctl output should contain echoexample2
+        last bindctl output should contain verbosecommentexample3
+        last bindctl output should not contain commentexample4
+        last bindctl output should contain echoexample5
+        last bindctl output should contain echoexample6
+        last bindctl output should contain verbosecommentexample7
+        last bindctl output should not contain commentexample8
+
+        # bad_command contains a bad command, at which point execution should stop
+        When I send bind10 the command execute file data/commands/bad_command
+        last bindctl output should contain shouldshow
+        last bindctl output should contain Error
+        last bindctl output should not contain shouldnotshow
+        # This would fail if the entire list was passed, or the configuration
+        # was committed
+        send bind10 the command config show Boss/components
+        last bindctl output should not contain b10-auth
+
+        # nested_command contains another execute script
+        When I send bind10 the command execute file data/commands/nested
+        last bindctl output should contain shouldshow
+        last bindctl output should not contain Error    
+
+        # show commands from a file
+        When I send bind10 the command execute file data/commands/bad_command show
+        last bindctl output should not contain Error
+        last bindctl output should contain shouldshow
+        last bindctl output should contain shouldnotshow
+
+    Scenario: Executing builting script init_authoritative_server
+        Given I have bind10 running with configuration bindctl/bindctl.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+
+        When I send bind10 the command execute init_authoritative_server show
+        # just test some parts of the output
+        last bindctl output should contain /Boss/components/b10-auth/special
+        last bindctl output should contain /Boss/components/b10-zonemgr/kind
+        last bindctl output should contain Please
+
+        # nothing should have been changed
+        When I send bind10 the command config diff
+        last bindctl output should contain {}
+
+        # ok now make sure modules aren't running, execute it, and make
+        # sure modules are running
+        bind10 module Auth should not be running
+        bind10 module Xfrout should not be running
+        bind10 module Xfrin should not be running
+        bind10 module Zonemgr should not be running
+
+        When I send bind10 the following commands:
+        """
+        execute init_authoritative_server
+        config commit
+        """
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message ZONEMGR_STARTED
+        And wait for bind10 stderr message XFRIN_STARTED
+        And wait for bind10 stderr message XFROUT_STARTED
+
+        last bindctl output should not contain Error
+        bind10 module Auth should be running
+        bind10 module Xfrout should be running
+        bind10 module Xfrin should be running
+        bind10 module Zonemgr should be running
diff --git a/tests/lettuce/features/default.feature b/tests/lettuce/features/default.feature
new file mode 100644
index 0000000..ce7ee1e
--- /dev/null
+++ b/tests/lettuce/features/default.feature
@@ -0,0 +1,21 @@
+Feature: default bind10 config
+    Tests for the default configuration of bind10.
+
+    Scenario: Check that only the default components are running
+    Given I have bind10 running with configuration default.config
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message STATS_STARTING
+
+    # These should be running
+    bind10 module Boss should be running
+    And bind10 module Logging should be running
+    And bind10 module Stats should be running
+
+    # These should not be running
+    bind10 module Resolver should not be running
+    And bind10 module Xfrout should not be running
+    And bind10 module Zonemgr should not be running
+    And bind10 module Xfrin should not be running
+    And bind10 module Auth should not be running
+    And bind10 module StatsHttpd should not be running
diff --git a/tests/lettuce/features/example.feature b/tests/lettuce/features/example.feature
index d1ed6b3..ca5ffbf 100644
--- a/tests/lettuce/features/example.feature
+++ b/tests/lettuce/features/example.feature
@@ -8,6 +8,18 @@ Feature: Example feature
     
     Scenario: A simple example
         Given I have bind10 running with configuration example.org.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for www.example.org should have rcode NOERROR
         A query for www.doesnotexist.org should have rcode REFUSED
         The SOA serial for example.org should be 1234
@@ -26,8 +38,18 @@ Feature: Example feature
         # is actually a compound step consisting of the following two
         # one to start the server
         When I start bind10 with configuration no_db_file.config
-        # And one to wait until it reports that b10-auth has started
-        Then wait for bind10 auth to start
+
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
 
         # This is a general step to stop a named process. By convention,
         # the default name for any process is the same as the one we
@@ -50,6 +72,17 @@ Feature: Example feature
         # This is a compound statement that starts and waits for the
         # started message
         Given I have bind10 running with configuration example.org.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
 
         # Some simple queries that is not examined further
         A query for www.example.com should have rcode REFUSED
@@ -113,8 +146,18 @@ Feature: Example feature
         # the system
 
         When I start bind10 with configuration example.org.config
-        Then wait for bind10 auth to start
-        Wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
         A query for www.example.org should have rcode NOERROR
         Wait for new bind10 stderr message AUTH_SEND_NORMAL_RESPONSE
         Then set bind10 configuration Auth/database_file to data/empty_db.sqlite3
@@ -128,15 +171,20 @@ Feature: Example feature
     Scenario: two bind10 instances
         # This is more a test of the test system, start 2 bind10's
         When I start bind10 with configuration example.org.config as bind10_one
+        And wait for bind10_one stderr message BIND10_STARTED_CC
+        And wait for bind10_one stderr message CMDCTL_STARTED
+        And wait for bind10_one stderr message AUTH_SERVER_STARTED
+
         And I start bind10 with configuration example2.org.config with cmdctl port 47804 as bind10_two
+        And wait for bind10_two stderr message BIND10_STARTED_CC
+        And wait for bind10_two stderr message CMDCTL_STARTED
+        And wait for bind10_two stderr message AUTH_SERVER_STARTED
 
-        Then wait for bind10 auth of bind10_one to start
-        Then wait for bind10 auth of bind10_two to start
         A query for www.example.org to 127.0.0.1:47806 should have rcode NOERROR
-        A query for www.example.org to 127.0.0.1:47807 should have rcode NOERROR
+        A query for www.example.org to [::1]:47807 should have rcode NOERROR
 
         Then set bind10 configuration Auth/database_file to data/empty_db.sqlite3
         And wait for bind10_one stderr message DATASRC_SQLITE_OPEN
 
         A query for www.example.org to 127.0.0.1:47806 should have rcode REFUSED
-        A query for www.example.org to 127.0.0.1:47807 should have rcode NOERROR
+        A query for www.example.org to [::1]:47807 should have rcode NOERROR
diff --git a/tests/lettuce/features/inmemory_over_sqlite3.feature b/tests/lettuce/features/inmemory_over_sqlite3.feature
new file mode 100644
index 0000000..85737e9
--- /dev/null
+++ b/tests/lettuce/features/inmemory_over_sqlite3.feature
@@ -0,0 +1,10 @@
+Feature: In-memory zone using SQLite3 backend
+    This feature tests the authoritative server configured with an in-memory
+    data source that uses the SQLite3 data source as the backend, and tests
+    scenarios that update the zone via incoming zone transfers.
+
+    Scenario: Load and response
+        Given I have bind10 running with configuration inmemory_over_sqlite3/secondary.conf
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        A query for www.example.org should have rcode NOERROR
+        The SOA serial for example.org should be 1234
diff --git a/tests/lettuce/features/ixfr_out_bind10.feature b/tests/lettuce/features/ixfr_out_bind10.feature
index e84ad8c..24a9299 100644
--- a/tests/lettuce/features/ixfr_out_bind10.feature
+++ b/tests/lettuce/features/ixfr_out_bind10.feature
@@ -31,7 +31,14 @@ Feature: IXFR out
 
     Scenario: Test Set 1
         Given I have bind10 running with configuration ixfr-out/testset1-config.db
-        Then wait for bind10 xfrout to start
+
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message XFROUT_STARTED
+        And wait for bind10 stderr message XFRIN_STARTED
+        And wait for bind10 stderr message ZONEMGR_STARTED
+
         The SOA serial for example.com should be 22
 
         #
@@ -146,7 +153,14 @@ Feature: IXFR out
 
     Scenario: Test Set 2
         Given I have bind10 running with configuration ixfr-out/testset1-config.db
-        Then wait for bind10 xfrout to start
+
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        And wait for bind10 stderr message XFROUT_STARTED
+        And wait for bind10 stderr message XFRIN_STARTED
+        And wait for bind10 stderr message ZONEMGR_STARTED
+
         The SOA serial for example.com should be 22
 
         #
diff --git a/tests/lettuce/features/multi_instance.feature b/tests/lettuce/features/multi_instance.feature
new file mode 100644
index 0000000..4ce135a
--- /dev/null
+++ b/tests/lettuce/features/multi_instance.feature
@@ -0,0 +1,59 @@
+Feature: Multiple instances
+    This feature tests whether multiple instances can be run, and whether
+    removing them does not affect the running of other instances
+
+    Scenario: Multiple instances of Auth
+        # Standard check to test (non-)existence of a file
+        # This file is actually automatically
+        The file data/test_nonexistent_db.sqlite3 should not exist
+
+        # This config should have two running instances
+        Given I have bind10 running with configuration multi_instance/multi_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+
+        # This is a hack. We should actually check if b10-auth and
+        # b10-auth-2 are started by name. But there's currently no way
+        # for a component to find out its name and log it.
+        And wait 2 times for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        # Now we use the first step again to see if the file has been created
+        The file data/test_nonexistent_db.sqlite3 should exist
+
+        A query for example.com should have rcode REFUSED
+
+        # this also checks whether the process is running
+        If I remember the pid of process b10-auth
+        And remember the pid of process b10-auth-2
+
+        When I remove bind10 configuration Boss/components value b10-auth-2
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+
+        Then the pid of process b10-auth should not have changed
+        And a query for example.com should have rcode REFUSED
+
+        When I send bind10 the following commands
+        """
+        config add Boss/components b10-auth-2
+        config set Boss/components/b10-auth-2/special auth
+        config set Boss/components/b10-auth-2/kind needed
+        config commit
+        """
+        And wait for new bind10 stderr message AUTH_SERVER_STARTED
+        And remember the pid of process b10-auth-2
+
+        Then the pid of process b10-auth should not have changed
+        A query for example.com should have rcode REFUSED
+
+        When I remove bind10 configuration Boss/components value b10-auth
+        And wait for new bind10 stderr message BIND10_PROCESS_ENDED
+        Then the pid of process b10-auth-2 should not have changed
+        A query for example.com should have rcode REFUSED
diff --git a/tests/lettuce/features/nsec3_auth.feature b/tests/lettuce/features/nsec3_auth.feature
new file mode 100644
index 0000000..4e5ed5b
--- /dev/null
+++ b/tests/lettuce/features/nsec3_auth.feature
@@ -0,0 +1,466 @@
+Feature: NSEC3 Authoritative service
+    This feature tests NSEC3 as defined in RFC5155, using the example
+    zone from appendix A and testing the example responses from appendix B.
+    Additional tests can be added as well.
+
+    # Response section data is taken directly from RFC5155
+    # It has been modified slightly; it has been 'flattened' (i.e. converted
+    # to 1-line RRs with TTL and class data), and whitespace has been added
+    # in the places where dig adds them too.
+    # Any other changes from the specific example data are added as inline
+    # comments.
+
+    Scenario: B.1. Name Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for a.c.x.w.example. should have rcode NXDOMAIN
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 8
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      NSEC3   1 1 12 aabbccdd  gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      NSEC3   1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        """
+
+    Scenario: B.2. No Data Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for ns1.example. type MX should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG
+        2t7b4g4vsa5smi47k61mv5bv1a22bojr.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OmBvJ1Vgg1hCKMXHFiNeIYHK9XVW0iLDLwJN4TFoNxZuP03gAXEI634Y wOc4YBNITrj413iqNI6mRk/r1dOSUw==
+        """
+
+    Scenario: B2.1. No Data Error, Empty Non-Terminal
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for y.w.example. should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.       3600    IN      NSEC3   1 1 12 aabbccdd  k8udemvp1j2f7eg6jebps17vp3n8i58h
+        ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. gPkFp1s2QDQ6wQzcg1uSebZ61W33rUBDcTj72F3kQ490fEdp7k1BUIfb cZtPbX3YCpE+sIt0MpzVSKfTwx4uYA==
+        """
+
+    Scenario: B.3. Referral to an Opt-Out Unsigned Zone
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for mc.c.example. type MX should have rcode NOERROR
+        The last query response should have flags qr rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 6
+        The last query response should have adcount 3
+        The authority section of the last query response should be
+        """
+        c.example.      3600    IN      NS      ns1.c.example.
+        c.example.      3600    IN      NS      ns2.c.example.
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      NSEC3   1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        """
+        The additional section of the last query response should be
+        """
+        ns1.c.example. 3600 IN A       192.0.2.7
+        ns2.c.example. 3600 IN A       192.0.2.8
+        """
+
+    Scenario: B.4. Wildcard Expansion
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for a.z.w.example. type MX should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 2
+        The last query response should have nscount 5
+        The last query response should have adcount 9
+        The answer section of the last query response should be
+        """
+        a.z.w.example.  3600    IN      MX      1 ai.example.
+        a.z.w.example.  3600    IN      RRSIG   MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+        """
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      NS      ns1.example.
+        example.        3600    IN      NS      ns2.example.
+        example.        3600    IN      RRSIG   NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      NSEC3   1 1 12 aabbccdd  r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+        """
+        # This is slightly different from the example in RFC5155; there are
+        # more RRs in the additional section.
+        The additional section of the last query response should be
+        """
+        ai.example.             3600    IN      A       192.0.2.9
+        ai.example.             3600    IN      AAAA    2001:db8::f00:baa9
+        ns1.example.            3600    IN      A       192.0.2.1
+        ns2.example.            3600    IN      A       192.0.2.2
+        ai.example.             3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+        ai.example.             3600    IN      RRSIG   AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+        ns1.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+        ns2.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+        """
+
+    Scenario: B.5. Wildcard No Data Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for a.z.w.example. type AAAA should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 8
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example.       3600    IN      NSEC3   1 1 12 aabbccdd  kohar7mbb8dc2ce8a9qvl8hon4k53uhi
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      NSEC3   1 1 12 aabbccdd  r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.       3600    IN      NSEC3   1 1 12 aabbccdd  t644ebqk9bibcna874givr6joj62mlhv MX RRSIG
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+        """
+
+    Scenario: B.6. DS Child Zone No Data Error
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for example. type DS should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      NSEC3   1 1 12 aabbccdd  2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        """
+
+    #
+    # Below are additional tests, not explicitely stated in RFC5155
+    #
+
+    Scenario: 7.2.2 other; Name Error where one NSEC3 covers multiple parts of proof (closest encloser)
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for b.x.w.example. should have rcode NXDOMAIN
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 6
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.        3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.        3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      NSEC3   1 1 12 aabbccdd  gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG
+        b4um86eghhds6nea196smvmlo4ors995.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. ZkPG3M32lmoHM6pa3D6gZFGB/rhL//Bs3Omh5u4m/CUiwtblEVOaAKKZ d7S959OeiX43aLX3pOv0TSTyiTxIZg==
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      NSEC3   1 1 12 aabbccdd  b4um86eghhds6nea196smvmlo4ors995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example.       3600    IN      RRSIG   NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        """
+
+    Scenario: 7.2.2 other; Name Error where one NSEC3 covers multiple parts of proof (wildcard)
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for a.w.example. should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 6
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN NSEC3 1 1 12 AABBCCDD KOHAR7MBB8DC2CE8A9QVL8HON4K53UHI
+        k8udemvp1j2f7eg6jebps17vp3n8i58h.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. FtXGbvF0+wf8iWkyo73enAuVx03klN+pILBKS6qCcftVtfH4yVzsEZqu J27NHR7ruxJWDNMtOtx7w9WfcIg62A==
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+        """
+
+    Scenario: Wildcard other: Wildcard name itself
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for *.w.example. type MX should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 2
+        The last query response should have nscount 3
+        The last query response should have adcount 9
+        The answer section of the last query response should be
+        """
+        *.w.example.            3600    IN      MX      1 ai.example.
+        *.w.example.            3600    IN      RRSIG   MX 7 2 3600 20150420235959 20051021000000 40430 example. CikebjQwGQPwijVcxgcZcSJKtfynugtlBiKb9FcBTrmOoyQ4InoWVudh CWsh/URX3lc4WRUMivEBP6+4KS3ldA==
+        """
+        The authority section of the last query response should be
+        """
+        example.                3600    IN      NS      ns1.example.
+        example.                3600    IN      NS      ns2.example.
+        example.                3600    IN      RRSIG   NS 7 1 3600 20150420235959 20051021000000 40430 example. PVOgtMK1HHeSTau+HwDWC8Ts+6C8qtqd4pQJqOtdEVgg+MA+ai4fWDEh u3qHJyLcQ9tbD2vvCnMXjtz6SyObxA==
+        """
+        The additional section of the last query response should be
+        """
+        ai.example.             3600    IN      A       192.0.2.9
+        ai.example.             3600    IN      AAAA    2001:db8::f00:baa9
+        ns1.example.            3600    IN      A       192.0.2.1
+        ns2.example.            3600    IN      A       192.0.2.2
+        ai.example.             3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. hVe+wKYMlObTRPhX0NL67GxeZfdxqr/QeR6FtfdAj5+FgYxyzPEjIzvK Wy00hWIl6wD3Vws+rznEn8sQ64UdqA==
+        ai.example.             3600    IN      RRSIG   AAAA 7 2 3600 20150420235959 20051021000000 40430 example. LcdxKaCB5bGZwPDg+3JJ4O02zoMBrjxqlf6WuaHQZZfTUpb9Nf2nxFGe 2XRPfR5tpJT6GdRGcHueLuXkMjBArQ==
+        ns1.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. bu6kx73n6XEunoVGuRfAgY7EF/AJqHy7hj0jkiqJjB0dOrx3wuz9SaBe GfqWIdn/uta3SavN4FRvZR9SCFHF5Q==
+        ns2.example.            3600    IN      RRSIG   A 7 2 3600 20150420235959 20051021000000 40430 example. ktQ3TqE0CfRfki0Rb/Ip5BM0VnxelbuejCC4zpLbFKA/7eD7UNAwxMgx JPtbdST+syjYSJaj4IHfeX6n8vfoGA==
+        """
+
+    Scenario: Wildcard other: Wildcard name itself nodata
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for *.w.example. type A should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN NSEC3 1 1 12 AABBCCDD T644EBQK9BIBCNA874GIVR6JOJ62MLHV MX RRSIG
+        r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. aupviViruXs4bDg9rCbezzBMf9h1ZlDvbW/CZFKulIGXXLj8B/fsDJar XVDA9bnUoRhEbKp+HF1FWKW7RIJdtQ==
+        """
+
+    Scenario: Direct query for NSEC3 record
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. type NSEC3 should have rcode NXDOMAIN
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 8
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN NSEC3 1 1 12 AABBCCDD R53BQ7CC2UVMUBFU5OCMM6PERS9TK9EN A RRSIG
+        q04jkcevqvmu85r014c7dkba38o0ji5r.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. hV5I89b+4FHJDATp09g4bbN0R1F845CaXpL3ZxlMKimoPAyqletMlEWw LfFia7sdpSzn+ZlNNlkxWcLsIlMmUg==
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+        """
+
+    Scenario: No data, type DS, in-zone
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for ai.example. type DS should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 4
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN NSEC3 1 1 12 AABBCCDD JI6NEOAEPV8B5O6K4EV33ABHA8HT9FGC A HINFO AAAA RRSIG
+        gjeqe526plbf1g8mklp59enfd789njgi.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. IVnezTJ9iqblFF97vPSmfXZ5Zozngx3KX3byLTZC4QBH2dFWhf6scrGF ZB980AfCxoD9qbbKDy+rdGIeRSVNyw==
+        """
+
+    Scenario: No data, type DS, optout delegation
+        Given I have bind10 running with configuration nsec3/nsec3_auth.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A dnssec query for c.example. type DS should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 6
+        The last query response should have adcount 1
+        The authority section of the last query response should be
+        """
+        example.                3600    IN      SOA     ns1.example. bugs.x.w.example. 1 3600 300 3600000 3600
+        example.                3600    IN      RRSIG   SOA 7 1 3600 20150420235959 20051021000000 40430 example. Hu25UIyNPmvPIVBrldN+9Mlp9Zql39qaUd8iq4ZLlYWfUUbbAS41pG+6 8z81q1xhkYAcEyHdVI2LmKusbZsT0Q==
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN NSEC3 1 1 12 AABBCCDD 2T7B4G4VSA5SMI47K61MV5BV1A22BOJR NS SOA MX RRSIG DNSKEY NSEC3PARAM
+        0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. OSgWSm26B+cS+dDL8b5QrWr/dEWhtCsKlwKLIBHYH6blRxK9rC0bMJPw Q4mLIuw85H2EY762BOCXJZMnpuwhpA==
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN NSEC3 1 1 12 AABBCCDD B4UM86EGHHDS6NEA196SMVMLO4ORS995 NS DS RRSIG
+        35mthgpgcu1qg68fab165klnsnk3dpvl.example. 3600 IN RRSIG NSEC3 7 2 3600 20150420235959 20051021000000 40430 example. g6jPUUpduAJKRljUsN8gB4UagAX0NxY9shwQAynzo8EUWH+z6hEIBlUT PGj15eZll6VhQqgZXtAIR3chwgW+SA==
+        """
diff --git a/tests/lettuce/features/queries.feature b/tests/lettuce/features/queries.feature
new file mode 100644
index 0000000..de6f0fa
--- /dev/null
+++ b/tests/lettuce/features/queries.feature
@@ -0,0 +1,124 @@
+Feature: Querying feature
+    This feature is a collection of non-specific querying tests;
+    for instance whether multiple queries in a row return consistent
+    answers.
+
+    Scenario: Repeated queries
+        Given I have bind10 running with configuration example.org.inmem.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A query for www.example.org should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 1
+        The last query response should have nscount 2
+        The last query response should have adcount 2
+
+        The answer section of the last query response should be
+        """
+        www.example.org.        3600    IN      A       192.0.2.1
+        """
+        The authority section of the last query response should be
+        """
+        example.org.            3600    IN      NS      ns1.example.org.
+        example.org.            3600    IN      NS      ns2.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns1.example.org.        3600    IN      A       192.0.2.3
+        ns2.example.org.        3600    IN      A       192.0.2.4
+        """
+
+        # Repeat of the above
+        A query for www.example.org should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 1
+        The last query response should have nscount 2
+        The last query response should have adcount 2
+
+        The answer section of the last query response should be
+        """
+        www.example.org.        3600    IN      A       192.0.2.1
+        """
+        The authority section of the last query response should be
+        """
+        example.org.            3600    IN      NS      ns1.example.org.
+        example.org.            3600    IN      NS      ns2.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns1.example.org.        3600    IN      A       192.0.2.3
+        ns2.example.org.        3600    IN      A       192.0.2.4
+        """
+
+        # And now query something completely different
+        A query for nosuchname.example.org should have rcode NXDOMAIN
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 0
+        The last query response should have nscount 1
+        The last query response should have adcount 0
+        The authority section of the last query response should be
+        """
+        example.org.            3600    IN      SOA     ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+        """
+
+    Scenario: ANY query
+        Given I have bind10 running with configuration example.org.inmem.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+
+        bind10 module Auth should be running
+        And bind10 module Resolver should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
+
+        A query for example.org type ANY should have rcode NOERROR
+        The last query response should have flags qr aa rd
+        The last query response should have ancount 4
+        The last query response should have nscount 0
+        The last query response should have adcount 3
+        The answer section of the last query response should be
+        """
+        example.org.            3600    IN      NS      ns1.example.org.
+        example.org.            3600    IN      NS      ns2.example.org.
+        example.org.            3600    IN      SOA     ns1.example.org. admin.example.org. 1234 3600 1800 2419200 7200
+        example.org.            3600    IN      MX      10 mail.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns1.example.org.        3600    IN      A       192.0.2.3
+        ns2.example.org.        3600    IN      A       192.0.2.4
+        mail.example.org.       3600    IN      A       192.0.2.10
+        """
+    Scenario: Delegation query for unsigned child zone
+        Given I have bind10 running with configuration example.org.inmem.config
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message AUTH_SERVER_STARTED
+        A dnssec query for www.sub.example.org type AAAA should have rcode NOERROR
+        The last query response should have flags qr rd
+        The last query response should have edns_flags do
+        The last query response should have ancount 0
+        The last query response should have nscount 1
+        The last query response should have adcount 2
+        The authority section of the last query response should be
+        """
+        sub.example.org.	3600	IN	NS	ns.sub.example.org.
+        """
+        The additional section of the last query response should be
+        """
+        ns.sub.example.org.	3600	IN	A	192.0.2.101
+        """
diff --git a/tests/lettuce/features/resolver_basic.feature b/tests/lettuce/features/resolver_basic.feature
index c759971..4092101 100644
--- a/tests/lettuce/features/resolver_basic.feature
+++ b/tests/lettuce/features/resolver_basic.feature
@@ -11,7 +11,17 @@ Feature: Basic Resolver
         # to be revised (as it would then leak, which is probably true
         # for any resolver system test)
         When I start bind10 with configuration resolver/resolver_basic.config
-        And wait for new bind10 stderr message RESOLVER_STARTED
+        And wait for bind10 stderr message BIND10_STARTED_CC
+        And wait for bind10 stderr message CMDCTL_STARTED
+        And wait for bind10 stderr message RESOLVER_STARTED
+
+        bind10 module Resolver should be running
+        And bind10 module Auth should not be running
+        And bind10 module Xfrout should not be running
+        And bind10 module Zonemgr should not be running
+        And bind10 module Xfrin should not be running
+        And bind10 module Stats should not be running
+        And bind10 module StatsHttpd should not be running
 
         # The ACL is set to reject any queries
         A query for l.root-servers.net. should have rcode REFUSED
diff --git a/tests/lettuce/features/terrain/.gitignore b/tests/lettuce/features/terrain/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/tests/lettuce/features/terrain/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/tests/lettuce/features/terrain/bind10_control.py b/tests/lettuce/features/terrain/bind10_control.py
index 7ccd2b3..c56afb7 100644
--- a/tests/lettuce/features/terrain/bind10_control.py
+++ b/tests/lettuce/features/terrain/bind10_control.py
@@ -14,8 +14,18 @@
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 from lettuce import *
+import time
 import subprocess
 import re
+import json
+
+ at step('sleep for (\d+) seconds')
+def wait_seconds(step, seconds):
+    """Sleep for some seconds.
+       Parameters:
+       seconds number of seconds to sleep for.
+    """
+    time.sleep(float(seconds))
 
 @step('start bind10(?: with configuration (\S+))?' +\
       '(?: with cmdctl port (\d+))?' +\
@@ -99,18 +109,15 @@ def wait_for_xfrout(step, process_name):
 def have_bind10_running(step, config_file, cmdctl_port, process_name):
     """
     Compound convenience step for running bind10, which consists of
-    start_bind10 and wait_for_auth.
+    start_bind10.
     Currently only supports the 'with configuration' option.
     """
     start_step = 'start bind10 with configuration ' + config_file
-    wait_step = 'wait for bind10 auth to start'
     if cmdctl_port is not None:
         start_step += ' with cmdctl port ' + str(cmdctl_port)
     if process_name is not None:
         start_step += ' as ' + process_name
-        wait_step = 'wait for bind10 auth of ' + process_name + ' to start'
     step.given(start_step)
-    step.given(wait_step)
 
 # function to send lines to bindctl, and store the result
 def run_bindctl(commands, cmdctl_port=None):
@@ -141,8 +148,8 @@ def run_bindctl(commands, cmdctl_port=None):
                         "stderr:\n" + str(stderr)
 
 
- at step('last bindctl( stderr)? output should( not)? contain (\S+)')
-def check_bindctl_output(step, stderr, notv, string):
+ at step('last bindctl( stderr)? output should( not)? contain (\S+)( exactly)?')
+def check_bindctl_output(step, stderr, notv, string, exactly):
     """Checks the stdout (or stderr) stream of the last run of bindctl,
        fails if the given string is not found in it (or fails if 'not' was
        set and it is found
@@ -150,14 +157,19 @@ def check_bindctl_output(step, stderr, notv, string):
        stderr ('stderr'): Check stderr instead of stdout output
        notv ('not'): reverse the check (fail if string is found)
        string ('contain <string>') string to look for
+       exactly ('exactly'): Make an exact match delimited by whitespace
     """
     if stderr is None:
         output = world.last_bindctl_stdout
     else:
         output = world.last_bindctl_stderr
     found = False
-    if string in output:
-        found = True
+    if exactly is None:
+        if string in output:
+            found = True
+    else:
+        if re.search(r'^\s+' + string + r'\s+', output, re.IGNORECASE | re.MULTILINE) is not None:
+            found = True
     if notv is None:
         assert found == True, "'" + string +\
                               "' was not found in bindctl output:\n" +\
@@ -167,6 +179,83 @@ def check_bindctl_output(step, stderr, notv, string):
                           "' was found in bindctl output:\n" +\
                           output
 
+def parse_bindctl_output_as_data_structure():
+    """Helper function for data-related command tests: evaluates the
+       last output of bindctl as a data structure that can then be
+       inspected.
+       If the bindctl output is not valid (json) data, this call will
+       fail with an assertion failure.
+       If it is valid, it is parsed and returned as whatever data
+       structure it represented.
+    """
+    # strip any extra output after a charater that commonly terminates a valid
+    # JSON expression, i.e., ']', '}' and '"'.  (The extra output would
+    # contain 'Exit from bindctl' message, and depending on environment some
+    # other control-like characters...but why is this message even there?)
+    # Note that this filter is not perfect.  For example, it cannot recognize
+    # a simple expression of true/false/null.
+    output = re.sub("(.*)([^]}\"]*$)", r"\1", world.last_bindctl_stdout)
+    try:
+        return json.loads(output)
+    except ValueError as ve:
+        assert False, "Last bindctl output does not appear to be a " +\
+                      "parseable data structure: '" + output + "': " + str(ve)
+
+def find_process_pid(step, process_name):
+    """Helper function to request the running processes from Boss, and
+       return the pid of the process with the given process_name.
+       Fails with an assert if the response from boss is not valid JSON,
+       or if the process with the given name is not found.
+    """
+    # show_processes output is a list of lists, where the inner lists
+    # are of the form [ pid, "name" ]
+    # Not checking data form; errors will show anyway (if these turn
+    # out to be too vague, we can change this)
+    step.given('send bind10 the command Boss show_processes')
+    running_processes = parse_bindctl_output_as_data_structure()
+
+    for process in running_processes:
+        if process[1] == process_name:
+            return process[0]
+    assert False, "Process named " + process_name +\
+                  " not found in output of Boss show_processes";
+
+ at step("remember the pid of process ([\S]+)")
+def remember_pid(step, process_name):
+    """Stores the PID of the process with the given name as returned by
+       Boss show_processes command.
+       Fails if the process with the given name does not appear to exist.
+       Stores the component_name->pid value in the dict world.process_pids.
+       This should only be used by the related step
+       'the pid of process <name> should (not) have changed'
+       Arguments:
+       process name ('process <name>') the name of the component to store
+                                       the pid of.
+    """
+    if world.process_pids is None:
+        world.process_pids = {}
+    world.process_pids[process_name] = find_process_pid(step, process_name)
+
+ at step('pid of process ([\S]+) should not have changed')
+def check_pid(step, process_name):
+    """Checks the PID of the process with the given name as returned by
+       Boss show_processes command.
+       Fails if the process with the given name does not appear to exist.
+       Fails if the process with the given name exists, but has a different
+       pid than it had when the step 'remember the pid of process' was
+       called.
+       Fails if that step has not been called (since world.process_pids
+       does not exist).
+    """
+    assert world.process_pids is not None, "No process pids stored"
+    assert process_name in world.process_pids, "Process named " +\
+                                               process_name +\
+                                               " was not stored"
+    pid = find_process_pid(step, process_name)
+    assert world.process_pids[process_name] == pid,\
+                   "Expected pid: " + str(world.process_pids[process_name]) +\
+                   " Got pid: " + str(pid)
+
 @step('set bind10 configuration (\S+) to (.*)(?: with cmdctl port (\d+))?')
 def config_set_command(step, name, value, cmdctl_port):
     """
@@ -183,6 +272,20 @@ def config_set_command(step, name, value, cmdctl_port):
                 "quit"]
     run_bindctl(commands, cmdctl_port)
 
+ at step('send bind10 the following commands(?: with cmdctl port (\d+))?')
+def send_multiple_commands(step, cmdctl_port):
+    """
+    Run bindctl, and send it the given multiline set of commands.
+    A quit command is always appended.
+    cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
+                the command to. Defaults to 47805.
+    Fails if cmdctl does not exit with status code 0.
+    """
+    commands = step.multiline.split("\n")
+    # Always add quit
+    commands.append("quit")
+    run_bindctl(commands, cmdctl_port)
+
 @step('remove bind10 configuration (\S+)(?: value (\S+))?(?: with cmdctl port (\d+))?')
 def config_remove_command(step, name, value, cmdctl_port):
     """
@@ -230,4 +333,4 @@ def module_is_running(step, name, not_str):
     if not_str is None:
         not_str = ""
     step.given('send bind10 the command help')
-    step.given('last bindctl output should' + not_str + ' contain ' + name)
+    step.given('last bindctl output should' + not_str + ' contain ' + name + ' exactly')
diff --git a/tests/lettuce/features/terrain/querying.py b/tests/lettuce/features/terrain/querying.py
index 51c158e..a547014 100644
--- a/tests/lettuce/features/terrain/querying.py
+++ b/tests/lettuce/features/terrain/querying.py
@@ -41,9 +41,10 @@ import re
 #
 # The following attributes are 'parsed' from the response, all as strings,
 # and end up as direct attributes of the QueryResult object:
-# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount
-# (flags is one string with all flags, in the order they appear in the
-# response packet.)
+# opcode, rcode, id, flags, qdcount, ancount, nscount, adcount,
+# edns_version, edns_flags, and edns_udp_size
+# (flags and edns_flags are both one string with all flags, in the order
+# in which they appear in the response message.)
 #
 # this will set 'rcode' as the result code, we 'define' one additional
 # rcode, "NO_ANSWER", if the dig process returned an error code itself
@@ -55,10 +56,12 @@ import re
 # See server_from_sqlite3.feature for various examples to perform queries
 class QueryResult(object):
     status_re = re.compile("opcode: ([A-Z])+, status: ([A-Z]+), id: ([0-9]+)")
+    edns_re = re.compile("; EDNS: version: ([0-9]+), flags: ([a-z ]*); udp: ([0-9]+)")
     flags_re = re.compile("flags: ([a-z ]+); QUERY: ([0-9]+), ANSWER: " +
                           "([0-9]+), AUTHORITY: ([0-9]+), ADDITIONAL: ([0-9]+)")
 
-    def __init__(self, name, qtype, qclass, address, port):
+    def __init__(self, name, qtype, qclass, address, port,
+                 additional_args=None):
         """
         Constructor. This fires of a query using dig.
         Parameters:
@@ -67,6 +70,7 @@ class QueryResult(object):
         qclass: The RR class to query. Defaults to IN if it is None.
         address: The IP adress to send the query to.
         port: The port number to send the query to.
+        additional_args: List of additional arguments (e.g. '+dnssec').
         All parameters must be either strings or have the correct string
         representation.
         Only one query attempt will be made.
@@ -78,6 +82,8 @@ class QueryResult(object):
         if qclass is not None:
             args.append('-c')
             args.append(str(qclass))
+        if additional_args is not None:
+            args.extend(additional_args)
         args.append(name)
         dig_process = subprocess.Popen(args, 1, None, None, subprocess.PIPE,
                                        None)
@@ -102,6 +108,8 @@ class QueryResult(object):
         """
         if line == ";; ANSWER SECTION:\n":
             self.line_handler = self.parse_answer
+        elif line == ";; OPT PSEUDOSECTION:\n":
+            self.line_handler = self.parse_opt
         elif line == ";; AUTHORITY SECTION:\n":
             self.line_handler = self.parse_authority
         elif line == ";; ADDITIONAL SECTION:\n":
@@ -131,6 +139,19 @@ class QueryResult(object):
                 self.nscount = flags_match.group(4)
                 self.adcount = flags_match.group(5)
 
+    def parse_opt(self, line):
+        """
+        Parse the header lines of the query response.
+        Parameters:
+        line: The current line of the response.
+        """
+        if not self._check_next_header(line):
+            edns_match = self.edns_re.search(line)
+            if edns_match is not None:
+                self.edns_version = edns_match.group(1)
+                self.edns_flags = edns_match.group(2)
+                self.edns_udp_size = edns_match.group(3)
+
     def parse_question(self, line):
         """
         Parse the question section lines of the query response.
@@ -179,9 +200,10 @@ class QueryResult(object):
         """
         pass
 
- at step('A query for ([\w.-]+) (?:type ([A-Z0-9]+) )?(?:class ([A-Z]+) )?' +
-      '(?:to ([^:]+)(?::([0-9]+))? )?should have rcode ([\w.]+)')
-def query(step, query_name, qtype, qclass, addr, port, rcode):
+ at step('A (dnssec )?query for ([\S]+) (?:type ([A-Z0-9]+) )?' +
+      '(?:class ([A-Z]+) )?(?:to ([^:]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))? )?' +
+      'should have rcode ([\w.]+)')
+def query(step, dnssec, query_name, qtype, qclass, addr, port, rcode):
     """
     Run a query, check the rcode of the response, and store the query
     result in world.last_query_result.
@@ -201,9 +223,19 @@ def query(step, query_name, qtype, qclass, addr, port, rcode):
         qclass = "IN"
     if addr is None:
         addr = "127.0.0.1"
+    addr = re.sub(r"\[(.+)\]", r"\1", addr) # convert [IPv6_addr] to IPv6_addr
     if port is None:
         port = 47806
-    query_result = QueryResult(query_name, qtype, qclass, addr, port)
+    additional_arguments = []
+    if dnssec is not None:
+        additional_arguments.append("+dnssec")
+    else:
+        # some builds of dig add edns0 by default. This could muck up
+        # additional counts, so unless we need dnssec, explicitly
+        # disable edns0
+        additional_arguments.append("+noedns")
+    query_result = QueryResult(query_name, qtype, qclass, addr, port,
+                               additional_arguments)
     assert query_result.rcode == rcode,\
         "Expected: " + rcode + ", got " + query_result.rcode
     world.last_query_result = query_result
@@ -255,9 +287,15 @@ def check_last_query_section(step, section):
     section ('<section> section'): The name of the section (QUESTION, ANSWER,
                                    AUTHORITY or ADDITIONAL).
     The expected response is taken from the multiline part of the step in the
-    scenario. Differing whitespace is ignored, but currently the order is
-    significant.
+    scenario. Differing whitespace is ignored, the order of the lines is
+    ignored, and the comparison is case insensitive.
     Fails if they do not match.
+    WARNING: Case insensitivity is not strictly correct; for instance the
+    data of TXT RRs would be case sensitive. But most other output is, so
+    currently the checks are always case insensitive. Should we decide
+    these checks do need to be case sensitive, we can either remove it
+    or make it optional (for the former, we'll need to update a number of
+    tests).
     """
     response_string = None
     if section.lower() == 'question':
@@ -265,15 +303,32 @@ def check_last_query_section(step, section):
     elif section.lower() == 'answer':
         response_string = "\n".join(world.last_query_result.answer_section)
     elif section.lower() == 'authority':
-        response_string = "\n".join(world.last_query_result.answer_section)
+        response_string = "\n".join(world.last_query_result.authority_section)
     elif section.lower() == 'additional':
-        response_string = "\n".join(world.last_query_result.answer_section)
+        response_string = "\n".join(world.last_query_result.additional_section)
     else:
         assert False, "Unknown section " + section
+
+    # Now mangle the data for 'conformance'
+    # This could be done more efficiently, but is done one
+    # by one on a copy of the original data, so it is clear
+    # what is done. Final error output is currently still the
+    # original unchanged multiline strings
+
     # replace whitespace of any length by one space
     response_string = re.sub("[ \t]+", " ", response_string)
     expect = re.sub("[ \t]+", " ", step.multiline)
+    # lowercase them
+    response_string = response_string.lower()
+    expect = expect.lower()
+    # sort them
+    response_string_parts = response_string.split("\n")
+    response_string_parts.sort()
+    response_string = "\n".join(response_string_parts)
+    expect_parts = expect.split("\n")
+    expect_parts.sort()
+    expect = "\n".join(expect_parts)
+
     assert response_string.strip() == expect.strip(),\
         "Got:\n'" + response_string + "'\nExpected:\n'" + step.multiline +"'"
-    
-    
+
diff --git a/tests/lettuce/features/terrain/steps.py b/tests/lettuce/features/terrain/steps.py
index 4b199d6..8df0bae 100644
--- a/tests/lettuce/features/terrain/steps.py
+++ b/tests/lettuce/features/terrain/steps.py
@@ -30,12 +30,13 @@ def stop_a_named_process(step, process_name):
     """
     world.processes.stop_process(process_name)
 
- at step('wait for (new )?(\w+) stderr message (\w+)(?: not (\w+))?')
-def wait_for_message(step, new, process_name, message, not_message):
+ at step('wait (?:(\d+) times )?for (new )?(\w+) stderr message (\w+)(?: not (\w+))?')
+def wait_for_stderr_message(step, times, new, process_name, message, not_message):
     """
     Block until the given message is printed to the given process's stderr
     output.
     Parameter:
+    times: Check for the string this many times.
     new: (' new', optional): Only check the output printed since last time
                              this step was used for this process.
     process_name ('<name> stderr'): Name of the process to check the output of.
@@ -46,16 +47,19 @@ def wait_for_message(step, new, process_name, message, not_message):
     strings = [message]
     if not_message is not None:
         strings.append(not_message)
-    (found, line) = world.processes.wait_for_stderr_str(process_name, strings, new)
+    if times is None:
+        times = 1
+    (found, line) = world.processes.wait_for_stderr_str(process_name, strings, new, int(times))
     if not_message is not None:
         assert found != not_message, line
 
- at step('wait for (new )?(\w+) stdout message (\w+)(?: not (\w+))?')
-def wait_for_message(step, process_name, message, not_message):
+ at step('wait (?:(\d+) times )?for (new )?(\w+) stdout message (\w+)(?: not (\w+))?')
+def wait_for_stdout_message(step, times, new, process_name, message, not_message):
     """
     Block until the given message is printed to the given process's stdout
     output.
     Parameter:
+    times: Check for the string this many times.
     new: (' new', optional): Only check the output printed since last time
                              this step was used for this process.
     process_name ('<name> stderr'): Name of the process to check the output of.
@@ -66,7 +70,9 @@ def wait_for_message(step, process_name, message, not_message):
     strings = [message]
     if not_message is not None:
         strings.append(not_message)
-    (found, line) = world.processes.wait_for_stdout_str(process_name, strings, new)
+    if times is None:
+        times = 1
+    (found, line) = world.processes.wait_for_stdout_str(process_name, strings, new, int(times))
     if not_message is not None:
         assert found != not_message, line
 
diff --git a/tests/lettuce/features/terrain/terrain.py b/tests/lettuce/features/terrain/terrain.py
index 90531a1..9a04bf5 100644
--- a/tests/lettuce/features/terrain/terrain.py
+++ b/tests/lettuce/features/terrain/terrain.py
@@ -28,6 +28,7 @@ import subprocess
 import os.path
 import shutil
 import re
+import sys
 import time
 
 # In order to make sure we start all tests with a 'clean' environment,
@@ -42,10 +43,16 @@ import time
 # The first element is the original, the second is the target that will be
 # used by the tests that need them
 copylist = [
+    ["configurations/bindctl_commands.config.orig",
+     "configurations/bindctl_commands.config"],
     ["configurations/example.org.config.orig",
      "configurations/example.org.config"],
+    ["configurations/bindctl/bindctl.config.orig",
+     "configurations/bindctl/bindctl.config"],
     ["configurations/resolver/resolver_basic.config.orig",
-     "configurations/resolver/resolver_basic.config"]
+     "configurations/resolver/resolver_basic.config"],
+    ["configurations/multi_instance/multi_auth.config.orig",
+     "configurations/multi_instance/multi_auth.config"]
 ]
 
 # This is a list of files that, if present, will be removed before a scenario
@@ -162,7 +169,7 @@ class RunningProcess:
         os.remove(self.stderr_filename)
         os.remove(self.stdout_filename)
 
-    def _wait_for_output_str(self, filename, running_file, strings, only_new):
+    def _wait_for_output_str(self, filename, running_file, strings, only_new, matches = 1):
         """
         Wait for a line of output in this process. This will (if only_new is
         False) first check all previous output from the process, and if not
@@ -176,18 +183,22 @@ class RunningProcess:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
+        match_count = 0
         if not only_new:
             full_file = open(filename, "r")
             for line in full_file:
                 for string in strings:
                     if line.find(string) != -1:
-                        full_file.close()
-                        return (string, line)
+                        match_count += 1
+                        if match_count >= matches:
+                            full_file.close()
+                            return (string, line)
         wait_count = 0
         while wait_count < OUTPUT_WAIT_MAX_INTERVALS:
             where = running_file.tell()
@@ -195,42 +206,46 @@ class RunningProcess:
             if line:
                 for string in strings:
                     if line.find(string) != -1:
-                        return (string, line)
+                        match_count += 1
+                        if match_count >= matches:
+                            return (string, line)
             else:
                 wait_count += 1
                 time.sleep(OUTPUT_WAIT_INTERVAL)
                 running_file.seek(where)
         assert False, "Timeout waiting for process output: " + str(strings)
 
-    def wait_for_stderr_str(self, strings, only_new = True):
+    def wait_for_stderr_str(self, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in this process's stderr output.
         Parameters:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
         return self._wait_for_output_str(self.stderr_filename, self.stderr,
-                                         strings, only_new)
+                                         strings, only_new, matches)
 
-    def wait_for_stdout_str(self, strings, only_new = True):
+    def wait_for_stdout_str(self, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in this process's stdout output.
         Parameters:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns a tuple containing the matched string, and the complete line
         it was found in.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
         """
         return self._wait_for_output_str(self.stdout_filename, self.stdout,
-                                         strings, only_new)
+                                         strings, only_new, matches)
 
 # Container class for a number of running processes
 # i.e. servers like bind10, etc
@@ -296,7 +311,7 @@ class RunningProcesses:
         for process in self.processes.values():
             process.remove_files_on_exit = False
 
-    def wait_for_stderr_str(self, process_name, strings, only_new = True):
+    def wait_for_stderr_str(self, process_name, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in the given process's stderr output.
         Parameters:
@@ -304,6 +319,7 @@ class RunningProcesses:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns the matched string.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
@@ -312,9 +328,10 @@ class RunningProcesses:
         assert process_name in self.processes,\
            "Process " + process_name + " unknown"
         return self.processes[process_name].wait_for_stderr_str(strings,
-                                                                only_new)
+                                                                only_new,
+                                                                matches)
 
-    def wait_for_stdout_str(self, process_name, strings, only_new = True):
+    def wait_for_stdout_str(self, process_name, strings, only_new = True, matches = 1):
         """
         Wait for one of the given strings in the given process's stdout output.
         Parameters:
@@ -322,6 +339,7 @@ class RunningProcesses:
         strings: Array of strings to look for.
         only_new: If true, only check output since last time this method was
                   called. If false, first check earlier output.
+        matches: Check for the string this many times.
         Returns the matched string.
         Fails if none of the strings was read after 10 seconds
         (OUTPUT_WAIT_INTERVAL * OUTPUT_WAIT_MAX_INTERVALS).
@@ -330,7 +348,8 @@ class RunningProcesses:
         assert process_name in self.processes,\
            "Process " + process_name + " unknown"
         return self.processes[process_name].wait_for_stdout_str(strings,
-                                                                only_new)
+                                                                only_new,
+                                                                matches)
 
 @before.each_scenario
 def initialize(scenario):
@@ -343,6 +362,10 @@ def initialize(scenario):
     # Convenience variable to access the last query result from querying.py
     world.last_query_result = None
 
+    # For slightly better errors, initialize a process_pids for the relevant
+    # steps
+    world.process_pids = None
+
     # Some tests can modify the settings. If the tests fail half-way, or
     # don't clean up, this can leave configurations or data in a bad state,
     # so we copy them from originals before each scenario
@@ -363,4 +386,13 @@ def cleanup(scenario):
         world.processes.keep_files()
     # Stop any running processes we may have had around
     world.processes.stop_all_processes()
-    
+
+# Environment check
+# Checks if LETTUCE_SETUP_COMPLETED is set in the environment
+# If not, abort with an error to use the run-script
+if 'LETTUCE_SETUP_COMPLETED' not in os.environ:
+    print("Environment check failure; LETTUCE_SETUP_COMPLETED not set")
+    print("Please use the run_lettuce.sh script. If you want to test an")
+    print("installed version of bind10 with these tests, use")
+    print("run_lettuce.sh -I [lettuce arguments]")
+    sys.exit(1)
diff --git a/tests/lettuce/features/terrain/transfer.py b/tests/lettuce/features/terrain/transfer.py
index 305e677..4ba45b8 100644
--- a/tests/lettuce/features/terrain/transfer.py
+++ b/tests/lettuce/features/terrain/transfer.py
@@ -58,7 +58,7 @@ class TransferResult(object):
             if len(line) > 0 and line[0] != ';':
                 self.records.append(line)
 
- at step('An AXFR transfer of ([\w.]+)(?: from ([^:]+)(?::([0-9]+))?)?')
+ at step('An AXFR transfer of ([\w.]+)(?: from ([^:]+|\[[0-9a-fA-F:]+\])(?::([0-9]+))?)?')
 def perform_axfr(step, zone_name, address, port):
     """
     Perform an AXFR transfer, and store the result as an instance of
@@ -72,6 +72,8 @@ def perform_axfr(step, zone_name, address, port):
     """
     if address is None:
         address = "127.0.0.1"
+    # convert [IPv6_addr] to IPv6_addr:
+    address = re.sub(r"\[(.+)\]", r"\1", address)
     if port is None:
         port = 47806
     args = [ 'dig', 'AXFR', '@' + str(address), '-p', str(port), zone_name ]
diff --git a/tests/lettuce/features/xfrin_bind10.feature b/tests/lettuce/features/xfrin_bind10.feature
index 70c3571..2a56356 100644
--- a/tests/lettuce/features/xfrin_bind10.feature
+++ b/tests/lettuce/features/xfrin_bind10.feature
@@ -3,9 +3,29 @@ Feature: Xfrin
     
     Scenario: Retransfer command
     Given I have bind10 running with configuration xfrin/retransfer_master.conf with cmdctl port 47804 as master
+    And wait for master stderr message BIND10_STARTED_CC
+    And wait for master stderr message CMDCTL_STARTED
+    And wait for master stderr message AUTH_SERVER_STARTED
+    And wait for master stderr message XFROUT_STARTED
+    And wait for master stderr message ZONEMGR_STARTED
+
     And I have bind10 running with configuration xfrin/retransfer_slave.conf
+    And wait for bind10 stderr message BIND10_STARTED_CC
+    And wait for bind10 stderr message CMDCTL_STARTED
+    And wait for bind10 stderr message AUTH_SERVER_STARTED
+    And wait for bind10 stderr message XFRIN_STARTED
+    And wait for bind10 stderr message ZONEMGR_STARTED
+
     A query for www.example.org should have rcode REFUSED
     Wait for bind10 stderr message CMDCTL_STARTED
-    When I send bind10 the command Xfrin retransfer example.org IN 127.0.0.1 47807
-    Then wait for new bind10 stderr message XFRIN_XFR_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
+    When I send bind10 the command Xfrin retransfer example.org IN ::1 47807
+    Then wait for new bind10 stderr message XFRIN_TRANSFER_SUCCESS not XFRIN_XFR_PROCESS_FAILURE
     A query for www.example.org should have rcode NOERROR
+
+    # The transferred zone should have 11 non-NSEC3 RRs and 1 NSEC3 RR.
+    # The following check will get these by AXFR, so the total # of RRs
+    # should be 13, counting the duplicated SOA.
+    # At this point we can confirm both in and out of AXFR for a zone
+    # containing an NSEC3 RR.
+    When I do an AXFR transfer of example.org from ::1 47807
+    Then transfer result should have 13 rrs
diff --git a/tests/lettuce/run_lettuce.sh b/tests/lettuce/run_lettuce.sh
new file mode 100755
index 0000000..9580dce
--- /dev/null
+++ b/tests/lettuce/run_lettuce.sh
@@ -0,0 +1,25 @@
+#! /bin/sh
+
+# Copyright (C) 2012  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+if [ "$1" = "-I" ]; then
+    shift
+    echo "$@"
+    LETTUCE_SETUP_COMPLETED=1 exec lettuce $@
+else
+    . ./setup_intree_bind10.sh
+    exec lettuce $@
+fi
diff --git a/tests/lettuce/setup_intree_bind10.sh.in b/tests/lettuce/setup_intree_bind10.sh.in
old mode 100755
new mode 100644
index b1f17bc..4ccf6ca
--- a/tests/lettuce/setup_intree_bind10.sh.in
+++ b/tests/lettuce/setup_intree_bind10.sh.in
@@ -23,7 +23,7 @@ BIND10_PATH=@abs_top_builddir@/src/bin/bind10
 PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/ddns:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
 export PATH
 
-PYTHONPATH=@abs_top_builddir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
+PYTHONPATH=@abs_top_builddir@/src/bin:@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs:$PYTHONPATH
 export PYTHONPATH
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
@@ -44,3 +44,5 @@ export B10_FROM_BUILD
 
 BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
 export BIND10_MSGQ_SOCKET_FILE
+
+export LETTUCE_SETUP_COMPLETED=1
diff --git a/tests/system/.gitignore b/tests/system/.gitignore
new file mode 100644
index 0000000..76f87fe
--- /dev/null
+++ b/tests/system/.gitignore
@@ -0,0 +1,2 @@
+/conf.sh
+/run.sh
diff --git a/tests/system/bindctl/nsx1/.gitignore b/tests/system/bindctl/nsx1/.gitignore
new file mode 100644
index 0000000..4a8ce05
--- /dev/null
+++ b/tests/system/bindctl/nsx1/.gitignore
@@ -0,0 +1,3 @@
+/b10-config.db.template
+/bind10.run
+/bindctl.out
diff --git a/tests/system/bindctl/nsx1/b10-config.db.template.in b/tests/system/bindctl/nsx1/b10-config.db.template.in
index 162329a..7a3647c 100644
--- a/tests/system/bindctl/nsx1/b10-config.db.template.in
+++ b/tests/system/bindctl/nsx1/b10-config.db.template.in
@@ -3,8 +3,5 @@
    "listen_on": [{"address": "10.53.0.1", "port": 53210}],
    "database_file": "@abs_builddir@/zone.sqlite3",
    "statistics-interval": 1
- },
- "Xfrout": {
-   "log_file": "@abs_builddir@/Xfrout.log"
  }
 }
diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh
index e933355..352642e 100755
--- a/tests/system/bindctl/tests.sh
+++ b/tests/system/bindctl/tests.sh
@@ -25,11 +25,25 @@ status=0
 n=0
 
 # TODO: consider consistency with statistics definition in auth.spec
-auth_queries_tcp="\<queries\.tcp\>"
-auth_queries_udp="\<queries\.udp\>"
-auth_opcode_queries="\<opcode\.query\>"
+cnt_name1="\<queries\.tcp\>"
+cnt_name2="\<queries\.udp\>"
+cnt_name3="\<opcode\.query\>"
+cnt_value1=0
+cnt_value2=0
+cnt_value3=0
 
-echo "I:Checking b10-auth is working by default ($n)"
+echo "I:Checking b10-auth is disabled by default ($n)"
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A > /dev/null && status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Starting b10-auth and checking that it works ($n)"
+echo 'config add Boss/components b10-auth
+config set Boss/components/b10-auth { "special": "auth", "kind": "needed" }
+config commit
+quit
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
 # perform a simple check on the output (digcomp would be too much for this)
 grep 192.0.2.1 dig.out.$n > /dev/null || status=1
@@ -43,11 +57,14 @@ sleep 2
 echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
-# the server should have received 1 UDP and 1 TCP queries (TCP query was
-# sent from the server startup script)
-grep $auth_queries_tcp".*\<1\>" bindctl.out.$n > /dev/null || status=1
-grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1
-grep $auth_opcode_queries".*\<2\>" bindctl.out.$n > /dev/null || status=1
+# the server should have received 1 UDP and 0 TCP queries (the server
+# startup script no longer sends any TCP queries)
+cnt_value1=`expr $cnt_value1 + 0`
+cnt_value2=`expr $cnt_value2 + 1`
+cnt_value3=`expr $cnt_value1 + $cnt_value2`
+grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
+grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
+grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
@@ -58,7 +75,7 @@ quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 # dig should exit with a failure code.
-$DIG +tcp +norec @10.53.0.1 -p 53210 ns.example.com. A && status=1
+$DIG +tcp +norec @10.53.0.1 -p 53210 ns.example.com. A > /dev/null && status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
@@ -70,6 +87,7 @@ quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 $DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+# perform a simple check on the output (digcomp would be too much for this)
 grep 192.0.2.1 dig.out.$n > /dev/null || status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
@@ -80,9 +98,12 @@ echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 # The statistics counters should have been reset while stop/start.
-grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1
-grep $auth_queries_udp".*\<1\>" bindctl.out.$n > /dev/null || status=1
-grep $auth_opcode_queries".*\<1\>" bindctl.out.$n > /dev/null || status=1
+cnt_value1=0
+cnt_value2=1
+cnt_value3=`expr $cnt_value1 + $cnt_value2`
+grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
+grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
+grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
@@ -105,11 +126,68 @@ echo 'Stats show
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
 # The statistics counters shouldn't be reset due to hot-swapping datasource.
-grep $auth_queries_tcp".*\<0\>" bindctl.out.$n > /dev/null || status=1
-grep $auth_queries_udp".*\<2\>" bindctl.out.$n > /dev/null || status=1
-grep $auth_opcode_queries".*\<2\>" bindctl.out.$n > /dev/null || status=1
+cnt_value1=`expr $cnt_value1 + 0`
+cnt_value2=`expr $cnt_value2 + 1`
+cnt_value3=`expr $cnt_value1 + $cnt_value2`
+grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
+grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
+grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+echo "I:Starting more b10-auths and checking that ($n)"
+for i in 2 3
+do
+    echo 'config add Boss/components b10-auth-'$i'
+config set Boss/components/b10-auth-'$i' { "special": "auth", "kind": "needed" }
+config commit
+quit
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
+done
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+grep 192.0.2.2 dig.out.$n > /dev/null || status=1
 if [ $status != 0 ]; then echo "I:failed"; fi
 n=`expr $n + 1`
 
+echo "I:Rechecking BIND 10 statistics consistency after a pause ($n)"
+sleep 2
+cnt_value1=`expr $cnt_value1 + 0`
+cnt_value2=`expr $cnt_value2 + 1`
+cnt_value3=`expr $cnt_value1 + $cnt_value2`
+# Rechecking some times
+for i in 1 2 3 4
+do
+    echo 'Stats show
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
+    # The statistics counters should keep being consistent even while
+    # multiple b10-auths are running.
+    grep $cnt_name1".*\<"$cnt_value1"\>" bindctl.out.$n > /dev/null || status=1
+    grep $cnt_name2".*\<"$cnt_value2"\>" bindctl.out.$n > /dev/null || status=1
+    grep $cnt_name3".*\<"$cnt_value3"\>" bindctl.out.$n > /dev/null || status=1
+    if [ $status != 0 ]; then echo "I:failed "; break ; fi
+done
+n=`expr $n + 1`
+
+echo "I:Stopping extra b10-auths and checking that ($n)"
+for i in 3 2
+do
+    echo 'config remove Boss/components b10-auth-'$i'
+config commit
+quit
+' | $RUN_BINDCTL \
+	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
+done
+$DIG +norec @10.53.0.1 -p 53210 ns.example.com. A >dig.out.$n || status=1
+grep 192.0.2.2 dig.out.$n > /dev/null || status=1
+if [ $status != 0 ]; then echo "I:failed"; fi
+n=`expr $n + 1`
+
+# The statistics counters can not be rechecked here because the auth
+# instance seems to hang up after one of the multiple auth instances
+# was removed via bindctl. This reason seems to be the same reason as
+# #1703.
+
 echo "I:exit status: $status"
 exit $status
diff --git a/tests/system/glue/.gitignore b/tests/system/glue/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/glue/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/glue/nsx1/.gitignore b/tests/system/glue/nsx1/.gitignore
new file mode 100644
index 0000000..c0750b3
--- /dev/null
+++ b/tests/system/glue/nsx1/.gitignore
@@ -0,0 +1,3 @@
+/b10-config.db
+/bind10.run
+/bindctl.out
diff --git a/tests/system/glue/nsx1/b10-config.db.in b/tests/system/glue/nsx1/b10-config.db.in
index acd040c..660183b 100644
--- a/tests/system/glue/nsx1/b10-config.db.in
+++ b/tests/system/glue/nsx1/b10-config.db.in
@@ -3,7 +3,14 @@
    "listen_on": [{"address": "10.53.0.1", "port": 53210}],
    "database_file": "@abs_builddir@/zone.sqlite3"
  },
- "Xfrout": {
-   "log_file": "@abs_builddir@/Xfrout.log"
+ "Boss": {
+   "components": {
+     "b10-auth": {"kind": "needed", "special": "auth" },
+     "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+     "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+     "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+     "b10-stats": { "address": "Stats", "kind": "dispensable" },
+     "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+   }
  }
 }
diff --git a/tests/system/ixfr/.gitignore b/tests/system/ixfr/.gitignore
new file mode 100644
index 0000000..027d45e
--- /dev/null
+++ b/tests/system/ixfr/.gitignore
@@ -0,0 +1,8 @@
+/b10-config.db
+/common_tests.sh
+/db.example.n0
+/db.example.n2
+/db.example.n2.refresh
+/db.example.n4
+/db.example.n6
+/ixfr_init.sh
diff --git a/tests/system/ixfr/b10-config.db.in b/tests/system/ixfr/b10-config.db.in
index 946d80f..156c959 100644
--- a/tests/system/ixfr/b10-config.db.in
+++ b/tests/system/ixfr/b10-config.db.in
@@ -19,5 +19,15 @@
             "name": "example.",
             "class": "IN"
         }]
+    },
+    "Boss": {
+        "components": {
+            "b10-auth": {"kind": "needed", "special": "auth" },
+            "b10-xfrin": { "address": "Xfrin", "kind": "dispensable" },
+            "b10-xfrout": { "address": "Xfrout", "kind": "dispensable" },
+            "b10-zonemgr": { "address": "Zonemgr", "kind": "dispensable" },
+            "b10-stats": { "address": "Stats", "kind": "dispensable" },
+            "b10-cmdctl": { "special": "cmdctl", "kind": "needed" }
+        }
     }
 }
diff --git a/tests/system/ixfr/in-1/.gitignore b/tests/system/ixfr/in-1/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-1/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-2/.gitignore b/tests/system/ixfr/in-2/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-2/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-2/ns1/.gitignore b/tests/system/ixfr/in-2/ns1/.gitignore
new file mode 100644
index 0000000..35ae1cb
--- /dev/null
+++ b/tests/system/ixfr/in-2/ns1/.gitignore
@@ -0,0 +1 @@
+/named.run
diff --git a/tests/system/ixfr/in-2/nsx2/.gitignore b/tests/system/ixfr/in-2/nsx2/.gitignore
new file mode 100644
index 0000000..d31eb18
--- /dev/null
+++ b/tests/system/ixfr/in-2/nsx2/.gitignore
@@ -0,0 +1 @@
+/bindctl.out
diff --git a/tests/system/ixfr/in-2/tests.sh b/tests/system/ixfr/in-2/tests.sh
index 7b1e2a8..3050713 100644
--- a/tests/system/ixfr/in-2/tests.sh
+++ b/tests/system/ixfr/in-2/tests.sh
@@ -54,7 +54,7 @@ then
     exit 1
 fi
 
-grep XFRIN_XFR_TRANSFER_SUCCESS nsx2/bind10.run | grep IXFR > /dev/null
+grep XFRIN_IXFR_TRANSFER_SUCCESS nsx2/bind10.run | grep IXFR > /dev/null
 if [ $? -ne 0 ];
 then
     echo "R:$CLIENT_NAME FAIL no 'IXFR successful' message in the BIND 10 log"
diff --git a/tests/system/ixfr/in-3/.gitignore b/tests/system/ixfr/in-3/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-3/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/ixfr/in-4/.gitignore b/tests/system/ixfr/in-4/.gitignore
new file mode 100644
index 0000000..87e08bf
--- /dev/null
+++ b/tests/system/ixfr/in-4/.gitignore
@@ -0,0 +1 @@
+/setup.sh
diff --git a/tests/system/start.pl b/tests/system/start.pl
index daa4577..32284de 100755
--- a/tests/system/start.pl
+++ b/tests/system/start.pl
@@ -53,6 +53,8 @@ if ($server && !-d "$test/$server") {
 my $topdir = abs_path("$test/..");
 my $testdir = abs_path("$test");
 my $RUN_BIND10 = $ENV{'RUN_BIND10'};
+my $RUN_BINDCTL = $ENV{'RUN_BINDCTL'};
+my $BINDCTL_CSV_DIR = $ENV{'BINDCTL_CSV_DIR'};
 my $NAMED = $ENV{'BIND9_NAMED'};
 my $LWRESD = $ENV{'LWRESD'};
 my $DIG = $ENV{'DIG'};
@@ -211,14 +213,15 @@ sub verify_server {
 
 	my $tries = 0;
 	while (1) {
-		my $return = system("$DIG +tcp +noadd +nosea +nostat +noquest +nocomm +nocmd -p 53210 version.bind. chaos txt \@10.53.0.$n > dig.out");
+		my $return = system("echo \"Stats show\" | $RUN_BINDCTL --csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out");
 		last if ($return == 0);
-		print `grep ";" dig.out`;
 		if (++$tries >= 30) {
 			print "I:no response from $server\n";
 			print "R:FAIL\n";
 			system("$PERL $topdir/stop.pl $testdir");
 			exit 1;
+		} else {
+			print "I:no response from $server. retrying.\n";
 		}
 		sleep 2;
 	}
diff --git a/tests/tools/badpacket/.gitignore b/tests/tools/badpacket/.gitignore
new file mode 100644
index 0000000..ad6c1e6
--- /dev/null
+++ b/tests/tools/badpacket/.gitignore
@@ -0,0 +1 @@
+/badpacket
diff --git a/tests/tools/badpacket/scan.cc b/tests/tools/badpacket/scan.cc
index a6e7229..1559a1f 100644
--- a/tests/tools/badpacket/scan.cc
+++ b/tests/tools/badpacket/scan.cc
@@ -61,7 +61,8 @@ Scan::scan(const CommandOptions& options) {
                         RRType::A()));
 
     OutputBufferPtr msgbuf(new OutputBuffer(512));
-    MessageRenderer renderer(*msgbuf);
+    MessageRenderer renderer;
+    renderer.setBuffer(msgbuf.get());
     message.toWire(renderer);
 
     iterateFlagsStart(msgbuf, options);
diff --git a/tests/tools/badpacket/tests/.gitignore b/tests/tools/badpacket/tests/.gitignore
new file mode 100644
index 0000000..d6d1ec8
--- /dev/null
+++ b/tests/tools/badpacket/tests/.gitignore
@@ -0,0 +1 @@
+/run_unittests
diff --git a/tests/tools/perfdhcp/.gitignore b/tests/tools/perfdhcp/.gitignore
new file mode 100644
index 0000000..1a8375a
--- /dev/null
+++ b/tests/tools/perfdhcp/.gitignore
@@ -0,0 +1 @@
+/perfdhcp
diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c
index 6e02b7e..3ab9a2e 100644
--- a/tests/tools/perfdhcp/perfdhcp.c
+++ b/tests/tools/perfdhcp/perfdhcp.c
@@ -66,6 +66,35 @@ main(const int argc, char* const argv[])
 #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
@@ -1207,6 +1236,13 @@ getsock6(void)
 		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;
diff --git a/tools/git-obsolete-branch.py b/tools/git-obsolete-branch.py
new file mode 100755
index 0000000..70a7788
--- /dev/null
+++ b/tools/git-obsolete-branch.py
@@ -0,0 +1,198 @@
+#!/usr/bin/python
+#
+# 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.
+
+#
+# This script lists obsolete (fully merged) branches. It is useful for periodic
+# maintenance of our GIT tree.
+#
+# It is good idea to use following command before running this script:
+#
+# git pull
+# git remote prune origin
+#
+# This script requires python 2.7 or 3.
+#
+# I have limited experience in Python. If things are done in a strange or
+# uncommon way, there are no obscure reasons to do it that way, just plain
+# lack of experience.
+#
+#                                                                        tomek
+
+import string
+import subprocess
+import sys
+from optparse import OptionParser
+
+class Branch:
+    MERGED = 1
+    NOTMERGED = 2
+    name = None
+    status = NOTMERGED
+    last_commit = None
+
+
+def branch_list_get(verbose):
+    """ Generates a list of available remote branches and
+        checks their status (merged/unmerged). A branch is merged
+        if all changes on that branch are also on master. """
+
+    # call git branch -r (list of remote branches)
+    txt_list = subprocess.check_output(["git", "branch", "-r"])
+
+    txt_list = txt_list.split(b"\n")
+
+    # we will store list of suitable branches here
+    out = []
+    for branch in txt_list:
+        # skip empty lines
+        if len(branch) == 0:
+            continue
+
+        # skip branches that are aliases (something -> something_else)
+        if branch.find(b"->") != -1:
+            continue
+
+        # don't complain about master
+        if branch == b"origin/master":
+            continue
+
+        branch_info = Branch()
+
+        # get branch name
+        branch_info.name = branch.strip(b" ")
+        branch_info.name = branch_info.name.decode("utf-8")
+
+        # check if branch is merged or not
+        if verbose:
+            print("Checking branch %s" % branch_info.name)
+
+        # get a diff with changes that are on that branch only
+        # i.e. all unmerged code.
+        cmd = ["git", "diff", "master..." + branch_info.name ]
+        diff = subprocess.check_output(cmd)
+        if len(diff) == 0:
+            # No diff? Then all changes from that branch are on master as well.
+            branch_info.status = Branch.MERGED
+
+            # let's get the last contributor with extra formatting
+            # see man git-log and search for PRETTY FORMATS.
+            # %ai = date, %ae = author e-mail, %an = author name
+            cmd = [ "git" , "log", "-n", "1", "--pretty=\"%ai,%ae,%an\"",
+                    branch_info.name ]
+            offender = subprocess.check_output(cmd)
+            offender = offender.strip(b"\n\"")
+
+            # comment out this 2 lines to disable obfuscation
+            offender = offender.replace(b"@", b"(at)")
+            # Obfuscating a dot does not work too well for folks that use
+            # initials
+            #offender = offender.replace(b".", b"(dot)")
+
+            branch_info.last_commit = offender.decode("utf-8")
+
+        else:
+            # diff is not empty, so there is something to merge
+            branch_info.status = Branch.NOTMERGED
+
+        out.append(branch_info)
+    return out
+
+def branch_print(branches, csv, print_merged, print_notmerged, print_stats):
+    """ prints out list of branches with specified details (using
+        human-readable (or CSV) format. It is possible to specify,
+        which branches should be printed (merged, notmerged) and
+        also print out summary statistics """
+
+    # counters used for statistics
+    merged = 0
+    notmerged = 0
+
+    # compact list of merged/notmerged branches
+    merged_str = ""
+    notmerged_str = ""
+    for branch in branches:
+        if branch.status == Branch.MERGED:
+            merged = merged + 1
+            if not print_merged:
+                continue
+            if csv:
+                print("%s,merged,%s" % (branch.name, branch.last_commit) )
+            else:
+                merged_str = merged_str + " " + branch.name
+        else:
+            # NOT MERGED
+            notmerged = notmerged + 1
+            if not print_notmerged:
+                continue
+            if csv:
+                print("%s,notmerged,%s" % (branch.name, branch.last_commit) )
+            else:
+                notmerged_str = notmerged_str + " " + branch.name
+
+    if not csv:
+        if print_merged:
+            print("Merged branches    : %s" % (merged_str))
+        if print_notmerged:
+            print("NOT merged branches: %s" % (notmerged_str))
+
+    if print_stats:
+        print("#----------")
+        print("#Merged    : %d" % merged)
+        print("#Not merged: %d" % notmerged)
+
+
+def parse_args(args=sys.argv[1:], Parser=OptionParser):
+
+    parser = Parser(description="This script prints out merged and/or unmerged"
+                    " branches of a GIT tree.")
+
+    parser.add_option("-c", "--csv", action="store_true",
+                      default=False, help="generates CSV output")
+    parser.add_option("-u", "--unmerged", action="store_true",
+                      default=False, help="lists unmerged branches")
+    parser.add_option("-m", "--skip-merged", action="store_true",
+                      default=False, help="omits listing merged branches")
+    parser.add_option("-s", "--stats", action="store_true",
+                      default=False, help="prints also statistics")
+
+    (options, args) = parser.parse_args(args)
+
+    if args:
+        parser.print_help()
+        sys.exit(1)
+
+    return options
+
+def main():
+    usage = """%prog
+    Lists all obsolete (fully merged into master) branches.
+    """
+
+    options = parse_args()
+    csv = options.csv
+    merged = not options.skip_merged
+    unmerged = options.unmerged
+    stats = options.stats
+
+    if csv:
+        print("branch name,status,date,last commit(mail),last commit(name)")
+
+    branch_list = branch_list_get(not csv)
+
+    branch_print(branch_list, csv, merged, unmerged, stats)
+
+if __name__ == '__main__':
+   main()



More information about the bind10-changes mailing list