BIND 10 trac2524, updated. b55b8b6686cc80eed41793c53d1779f4de3e9e3c [2524] Merge branch 'master' into trac2524

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jan 7 11:46:32 UTC 2013


The branch, trac2524 has been updated
       via  b55b8b6686cc80eed41793c53d1779f4de3e9e3c (commit)
       via  7234eee9cf529a98841ca502dde655bd15f4299f (commit)
       via  be570bcaf88318286027da7da40a600cbe4223e9 (commit)
       via  e5a354694f4d0192132d00e25223d16eb24f25d7 (commit)
       via  a92495cd29fbe405bef744b98d01ff200caa05c6 (commit)
       via  f516fc484544b7e08475947d6945bc87636d4115 (commit)
       via  50e76dc960ad76ec97004832d718cb337390fce8 (commit)
       via  3e9a69424117d8f9841d76cf71dab353b6aa5435 (commit)
       via  a6ce7e4332223277ca50cd68a65e7e404621717c (commit)
       via  9e85dc2c98c9fde0f1d73c1d6c0d0d6188b41439 (commit)
       via  059fcd024b1611a7c5ca52a6f7055f7c9d14f106 (commit)
       via  4cb0974a3a642d06513ecc43743109bd92e45a08 (commit)
       via  573d2b1fbc3145d18ec71621f56e5c74a328ed3c (commit)
       via  b8e5414ee0445b3b0141203470ac07f42d7bbc32 (commit)
       via  aa32f38e127d1b3b3d4e1349fbfdcac155dc5c5e (commit)
       via  40502d5e2c6cbe71143e842ac7908e8a55b62324 (commit)
       via  60b979b7088f9e282b173c8d71792e2c4b20b57c (commit)
       via  0974318566abe08d0702ddd185156842c6642424 (commit)
       via  31147f0f82fd1e1b8ead164153599269822517ea (commit)
       via  8679b4053024172db8e3d55b7ed50a855efc77c0 (commit)
       via  bef01123fb5dfaee927bc58da465109240811a18 (commit)
       via  82041829bd8cc7400d0fee9d08e4cf1091c25a70 (commit)
       via  baa1ddd9057d0f10f85faa67f92e94268189dcd1 (commit)
       via  5379cabb61c3daff05c541ab6c9675a8a3ebfed7 (commit)
       via  30cbe355d7fcccf3e06472e5fd04c9de870adbe6 (commit)
       via  2629140f12accdfa6b4de4f59e5a76b0cc10ecb6 (commit)
       via  ea8082a263cb6d2b69764da2322b2c483913e5da (commit)
       via  6c20066d5613dfda246d598f47b9e779eef9c70b (commit)
       via  2aa422106dd081138c633ad2f9a3ede0ebdbd94a (commit)
       via  14220fefc56f7970d6b8bfd316c7ee234dd416d4 (commit)
       via  34596a05508cee3e48565a4082e9f6e9b815ad41 (commit)
       via  6b045bcd1f9613e3835551cdebd2616ea8319a36 (commit)
       via  2d46b704c62b47c2313d80b5afafbb180c1b7853 (commit)
       via  ae8851eea4d83050ad2a8e17fc6b8f0b11364fdd (commit)
       via  2078a3a34f198220806f9eabcf6fa7f6f0c9288c (commit)
       via  5ee9971dc60341992cf7da807dbec32811dd14d7 (commit)
       via  025db8d7a207b39174362981868742f1b80bdf69 (commit)
       via  00845ee8d7366b80e23e349b7cf8cd9d52551785 (commit)
       via  5d2a22ac8f92c53a89a5f4507ac3946529b9d49e (commit)
       via  4323bf82f07269a4f99a951b659749a5a1234ef2 (commit)
       via  fb95fbd37114f9c8fed764979cc5dc9124e3439c (commit)
       via  96e4248d61ccbcd23c7563f0a36d5443b607ea02 (commit)
       via  ae5693b89371208d5d5d8e884b52362f861c633f (commit)
       via  778e6009722520325aaa1efc98240fffc8f6b6d1 (commit)
       via  59b176e95540aacd2b0f167cba350d7fa18771db (commit)
       via  b52e736b9e5079aedf5252f04d019a80292bfc41 (commit)
       via  9b0a73f8a2523480f538747d579c940534ac5ac5 (commit)
       via  36fea252b03a38a0a86b830bc6a3373a88966413 (commit)
       via  d4bd5936f52d20ba21865858861dc358f531da04 (commit)
       via  19f2ceb60860badfc183f9d4e7d54ac9105ee296 (commit)
       via  ce6444b69feb909e5c8212e7335eeca14a2b11ea (commit)
       via  33ba1827b513aa735a483f6927667a9e872a2207 (commit)
       via  23035066d0c07c7b83120d3cba9efc1febe18ff5 (commit)
       via  080d47deb3088adddd96434acfd1c82bd14779c9 (commit)
       via  9f5102a30e4cb8da4697b3561206957b9a2aae05 (commit)
       via  0758ead7f4044e0867d4f953427b6c10f7afab45 (commit)
       via  9ab72138d43cb21a620c021a24e6a248b3dd77ab (commit)
       via  ffe973e3df51ab36e17087eda23b1042500d39b5 (commit)
       via  857d7fbf031d29b5b66ebe38fec725dde35008fd (commit)
       via  514b87c307fc834f39f0be155fc9bac9a6508d70 (commit)
       via  6d28690c67369927bb0806618f23533a1457eb6f (commit)
       via  029be672c7bf1b936859e811a17d45d04215ecd4 (commit)
       via  1a0577f9f348a74eacc959e091756f0e7a71accb (commit)
       via  5ec876c98d10dfdb0675ffcde709b5ce9871465b (commit)
       via  09dc37b8319af571d2e7afb0bf5b6da82cd174f6 (commit)
       via  e17ddc8b0f6726a185e982c22dbf787351c22cdd (commit)
       via  8960a571bc2db7129d4ce8861f83808fb71dc81c (commit)
       via  3ff7531f6be864ca6965a60ef1e2744e945a3569 (commit)
       via  786673c6998d886be73950a99a0292344b750cea (commit)
       via  fe40dade09da3ca910f034e650db2a3d5a93a49b (commit)
       via  fb8433b523f854b3978ef1c868d573e172a5a76c (commit)
       via  5d2f35c760be8fe9aa54f566461ca8a9c5d0db78 (commit)
       via  5d8d1e296a4eccc34ab0c19acfa9a4f39a63745c (commit)
       via  08dec0f8e0129ed43373dca3a335d752a3fb5e2c (commit)
       via  6ed60d209f8c6d7d7d4978b19a2feb6170e2c67a (commit)
       via  448686bfad7694acde8b08355dd5921efd4bb2ca (commit)
       via  5deb6bbc2df15dc20f36727f0abeaa4d4ad0c17e (commit)
       via  e44d06354aa0785cf5bd247358e59f7dfe69f5ad (commit)
       via  17cd3618c21b12d7867d9e5e4dba81c1cee46cb6 (commit)
       via  376b3b0760c5198dbf5ccf740c666ed9c9acaf45 (commit)
       via  4beebdd9209a2a75b33068f4d1811b2664b957db (commit)
       via  db55f102b30e76b72b134cbd77bd183cd01f95c0 (commit)
       via  cb9f017de088330bd6e4107bad642b5e4ea66ce9 (commit)
       via  9e6e821c0a33aab0cd0e70e51059d9a2761f76bb (commit)
       via  daf78976a3df91027c2a82cd0a794255b60507b0 (commit)
       via  291591772d83ff6b5ec9412b03624d2c9f13dc82 (commit)
       via  27e7210b344cea5c7098bf233481d5f2bf7cac97 (commit)
       via  230078e5f45f29fec72e35a77e2fd897fb6d3fb7 (commit)
       via  77b42eabd7630b5ba7f5f9fa383de73e0e7fe49d (commit)
       via  31a4bc5aacc85f79416c6c4da2e33e642077e9f9 (commit)
       via  89f331dff577268f90d885dc871cd2a152940ef8 (commit)
       via  c6c569cae91e19aa6267081ef9926f52b87d903d (commit)
       via  c8dcb23d366d559cb685b64514f49d7222df3335 (commit)
       via  e1c2233fab06d393eed6e16fc09f9bf672963112 (commit)
       via  5f8c941e31630163287b5197b6185dae9b03de13 (commit)
       via  fb95fe7735c5995f727ea42dfafcad820a3764da (commit)
       via  2039944b9b5a639d61466c197660a81f047013b3 (commit)
       via  2fa6a93d7f49f8073bc919620afc2cba4c7c0ce3 (commit)
       via  357c297f73a57539c9b90f7f80c4bebbdf2f8db8 (commit)
       via  d8a4ee26573da0832ad2efc424e6c9db9760fb85 (commit)
       via  c661badf19c5e9593d8c4ba719b86cd96b85f787 (commit)
       via  fed1aab5a0f813c41637807f8c0c5f8830d71942 (commit)
       via  bd8497df6615f58b2a6de701cf2a8f02c4d57d9b (commit)
       via  a5aab271f44cce29af4344ae8c24da02167deb05 (commit)
       via  57b40ee87d9689e6479d7fe4a2cf9903db3a3644 (commit)
       via  2156cc8503238162e22f418632837003593c8e3c (commit)
       via  61386c685763040bfcb779f0224bf6867a4dceb9 (commit)
       via  44bd04d531d1881d7c9a8fea9bce4b6c10aea16d (commit)
       via  ef19026395320447f81738be3fd840e278ff8da2 (commit)
       via  7299bfac202b013e968ff19c1c3109d78745ed22 (commit)
       via  bf53fbd4e92ae835280d49fbfdeeebd33e0ce3f2 (commit)
       via  f64ff7004ddec6c81538b2fc5d6827de6b79cfb8 (commit)
       via  8d844f040f2f764f7be009c17d25b06e159b1924 (commit)
       via  4a4642ad16734522436855cfd8e4d09c5ce32d06 (commit)
       via  a878a8107d48c5b1c476930cd4eb769f609fd609 (commit)
       via  8474c4bf697ccf594ca3d08dd7b1f327a44e5e82 (commit)
       via  71c5b61bcf71fb8ef3382569ea0eb059b0cca430 (commit)
       via  667d94d93788e24158ebbec640f006f49b7d438c (commit)
       via  15f662d68bbce471df63609cf8deccb280670048 (commit)
       via  96c1a3cc7becc69e4bb24cd3e40aaa605e3d90e3 (commit)
       via  774a4996dafc7a5809ab0ae08851d412e996e970 (commit)
       via  c864447b8ec187264fac032be1561c77bb3103b6 (commit)
       via  95483cb529b42683002528186eb29d6854e7ce0e (commit)
       via  8d8605b657d5e554f0c496143fe1a3b3cb8d3ea8 (commit)
       via  ff7903d22a1cc553ff22f9a6047e590f14c7dd32 (commit)
       via  fdce9eb73133bb52609ed6ad51fdb77b28447eb3 (commit)
       via  689b015753a9e219bc90af0a0b818ada26cc5968 (commit)
       via  0b4fca9e95537b24fa5a7412c998d424e6f567a5 (commit)
       via  364716d15dc738cea57c0698e99ed8692ead0c9b (commit)
       via  824581101796ab6aa13ce856f778d00ccb710345 (commit)
       via  3b4124f6899a8d45195e03c319252b324f14c3c9 (commit)
       via  8ee51ea2b44d0c9704809d84a55c2401cfe39853 (commit)
       via  fee6f278985f5112cb4d3601bb4c7287930d27ec (commit)
       via  7740087adf45a16b1681e69a41bcb09ca590c3b1 (commit)
       via  70e63e4b4fb5544ca2c8b9ced0100dd73712e847 (commit)
       via  c9d7464aee7f2392305d6f7ea24a318a9b0b8523 (commit)
       via  619c53ae796b76c30bd758095c723bc9835c13c4 (commit)
       via  f91f820e5615292888269a06b502374f3857aa39 (commit)
       via  37cc046b9fc52e7e01e903fb821548f3cec0f2f3 (commit)
       via  a51b040470e32b6e30447a605f25dd2977a5476e (commit)
       via  2b23275bd5730b146658e3bb1f426a815030694a (commit)
       via  6aa012341cc32ff61e67d82960aeaa07ce352fa6 (commit)
       via  b0bc2d360163be4126f6a88b73a3c17a18714736 (commit)
       via  772ce4c2888583e2fe1e9959c705608f6d1b7e98 (commit)
       via  00ae760dd4d30c5944dfb70a892c15004a1f07ee (commit)
       via  94cb9c2b880da087af403da4b5b765ccd77ad594 (commit)
       via  7e61e0a0c0d3b87a7e1033a353a273b1502caa8d (commit)
       via  9394663ad4f49908d99dc2d2a47ddc0a72871f4a (commit)
       via  1b69710afed58b9f7ebff61229f3f29e487f4026 (commit)
       via  4d6495aa80132d3a67ad4ef9aa76d75683772a80 (commit)
       via  530e62ba1a41f63a8dbcd0838fb5cfa95cb3816a (commit)
       via  ed7622df90ee865b5be03b0d13ed5977466bebfc (commit)
       via  ddd815eb743887a54500fdd839e2de17571b1bf1 (commit)
       via  214e0409215d2dc329fb4b54e3f97d2f262ad375 (commit)
       via  e5a42900088bf1403bdbb43ed8b9894109478973 (commit)
       via  72beaf8964363734d4f5e7b4b11ae7b8b4b5557c (commit)
       via  a489ad14ad44a593195b2f50f35c4825258a176f (commit)
       via  62a23854f619349d319d02c3a385d9bc55442d5e (commit)
       via  3449632270ba83f6d2114028e82f6f4c5098c2dc (commit)
       via  9c7390b2e75ff45d42f18e8775998ec5513c0053 (commit)
       via  b5904bd8eed8e36d1d6fd6d4305f687f53fe6733 (commit)
       via  bdc390376f18c7a858744eb9fb11827e792c6243 (commit)
       via  393c70b7a308f5d816a87cf21e017351dbf09260 (commit)
       via  c980c37cce2a445ec40f2c526cd89310cf7c4941 (commit)
       via  44f6dc45fffe9a44d8700bbb601dab169568f7ac (commit)
       via  4114ac95b51b5551e38bc277860abf14775cc817 (commit)
       via  55e0fb24720f98d51eeff77af6bdc0dff8649fa4 (commit)
       via  7454de81c9a382b84361fe8e35a19e1d04890141 (commit)
       via  ee7c565a4d00c5ccac5f22858b430aff836550f2 (commit)
       via  5cd3fa2a026c0cfadffe57b6c859f8621c17c21f (commit)
       via  16bdc3898aa70d2145872a5277f60711c32f2bd9 (commit)
       via  d2cc8db8c12a44199fdfcb5ffbcda82794c1c189 (commit)
       via  e4be11b7e2f072f484fdd6eae589b41688e7de75 (commit)
       via  c4cf36691115c15440b65cac16f1c7fcccc69521 (commit)
       via  655395cdae670d648bb556467773c4014094cce5 (commit)
       via  92ac02b80cb94846eff4cd136d5c426526f5145a (commit)
       via  2a8f04d9e437cc209ebb0921d0269ac4fdf3c760 (commit)
       via  07a0de0905f93bea39de048b3db8ed2fe24ef0a9 (commit)
       via  437477418c413c8a56adcb59441984dd5399f8a5 (commit)
       via  ad24f3f000a06f18ee1dba28ea2b38f8512f2c82 (commit)
       via  fbd4c46f6c801a5a662d28e75ed3f5014c2ae112 (commit)
       via  2c49df942a07bdcdf335a5794e779a1fc0f870c5 (commit)
       via  93cbc6cc8a27e8dad8eb0ef10a66655e7fbf6101 (commit)
       via  d7a0286809c9759de8f33209823eaddfe3f76264 (commit)
       via  d87cdaba29e56dd4b2f8db30749f04a66fa70d43 (commit)
       via  d1f88a92073bfe44db618ecf0b88a05c5dd79aa4 (commit)
       via  ed47bd72388da8de0ab5e92d44da12942ba49b35 (commit)
       via  cecd1c59612f853434cea629f19a4b4a234da966 (commit)
       via  8b616d0d3fbf1da3533c9836687f7fa12a7da1c6 (commit)
       via  f277a67977c700be9e205c6af2c29f2b0764b19c (commit)
       via  862d60332f64f7bb357cf8f300fd0faf610ef638 (commit)
       via  b9fa6ce6e6ba2d491096bc5c632e80c70a73614e (commit)
       via  2aa24772e0d3c790ab2127933159747f30bd5430 (commit)
       via  ed49f68cc90984e065b40a0100e154956403fb35 (commit)
       via  62f943fe6baa7a99990856bb03be039e54341ad3 (commit)
       via  525af02fc521561f117064cfee06288798236287 (commit)
       via  c8f37fef053d256ba32adc921cf677059f61117f (commit)
       via  ea8e70a330e7930e3be85fcb6fe6ac7ca44fc643 (commit)
       via  eda75ca3700979153bcdee08b7d97a10f14519f0 (commit)
       via  625f08940bce63bd70bd9d8dac67d003519a61f1 (commit)
       via  b40cd6851ace87bc97a095cf76b2a0ddee3666ed (commit)
       via  16cc994d2f3e15180fc022976062d616b0c05f38 (commit)
       via  89f2d7507297c5e3c1daf815f45e54499d029e27 (commit)
       via  6f27e6fc9819b5807703ff141e31a0d7a6800a26 (commit)
       via  a435de0e81e1cde5ad40f5a8699252d31a2a39b7 (commit)
       via  a602d28cbe49a1287a4beb85ace69c854d025b02 (commit)
       via  f74f25e7b04afb0bb116fcb6023837e47f471a77 (commit)
       via  5fa05448d68be1c4aafc3806ab0fc3302cd32597 (commit)
       via  f5dc7406f61fb182aeed500fd39c1fd3b486d6c3 (commit)
       via  91455fb76e9acd98a1201f5e261896993e7a2339 (commit)
       via  870ee769dedab453f6fb65082884cd2b45d1ca14 (commit)
       via  2ad9f33c18a74df23e3ca10fd5246289093e2768 (commit)
       via  079e629bdec110c1b07376645361fbcb2e467043 (commit)
       via  165e2775424f0377b5eb8f574d83c0d0dcd67a4b (commit)
       via  66c563e695df604afe0edab5d52b7fd4f2a871dd (commit)
       via  f8f73869c12d0cc0007618e91b943feab7073840 (commit)
       via  00fd5f949667517a3d02c70b36476f430c0721c9 (commit)
       via  90094d0add9f2047b2dbd307f800d4fb9ccccd73 (commit)
       via  50a73567e8067fdbe4405b7ece5b08948ef87f98 (commit)
       via  ca8707b6528b0779ce9cc6de42169d53af561b8a (commit)
       via  5a34019b3d874b4e8d1209d47d38592281b971ca (commit)
       via  dc3f31bea1c264125464906dae050a995829e816 (commit)
       via  45596b6bd556fd78555930c4e883a5025912ec4d (commit)
       via  471c6f2d6c1ad9b71422f1d19b6223492c5b6023 (commit)
       via  3adc5a448824ec9a819c52c89869855cf1de8d0c (commit)
       via  694dda8c2b939cf29d8949693f40dd12439fab0c (commit)
       via  34cb2bf92262addf0fb401365c23773dee3f3e89 (commit)
       via  23a5c8f5a97538d51389303ebef46164f95cd317 (commit)
       via  be2fccd2405459f3b421d2f7a7bec11103710f4c (commit)
       via  01496b7eff80566dd62b4d308ac8c932a16ed13e (commit)
       via  db92f30af10e6688a7dc117b254cb821e54a6d95 (commit)
       via  757d91ff52158a17e9e17990aa39815fda2ed3f6 (commit)
       via  7400eee5cb287236717e78c8956cc09efb424813 (commit)
       via  88dd03a0064c14a1608b6be9e4cff5d5cee7643f (commit)
       via  8a49f520e191d8ff14d8118ec80ab5c83701995d (commit)
       via  c41a723a55a2d5665ee28716b32983ea17a38d97 (commit)
       via  1a7c1e3d9a33115839c64869a28f3c7daf63efc0 (commit)
       via  78688fbf0ddeb0f2e47a4988246b8ad29fe14ba4 (commit)
       via  09d7669d9da37bcf44c914f378715f71e633180c (commit)
       via  5efe6393d73425aedd8a9bdb8b4288dac0f1d358 (commit)
       via  663bd7310eadfe3164d7504c1eceef716eb1086d (commit)
       via  7898eed5a8cf9888d02dea2e3a7519b6148a8efe (commit)
       via  c2d65ae44cf2c167de0b6892359b72e54d8b81ba (commit)
       via  e3b18efc1d76c65fe1bdec7d6eb4821f3745cd52 (commit)
       via  8b805bcd111bfeca94c98f60bd629387fbc7d342 (commit)
       via  935c918578e428b5d24718e2e113dc706ac952da (commit)
       via  0785c84f4f92d3aba3be9ee88a431ec7173929fa (commit)
       via  9d1e869ba98529030a65359ecaeb2273cd2cb14f (commit)
       via  0f4a4a3b12d7a1730bbf79079495eb397a72de08 (commit)
       via  9e7a7d316106c60fdd3e3ec4ff85f7d3a10035a1 (commit)
       via  605e81588c922ad5e93f87b56da722d79075993c (commit)
       via  d08ad5667f5b4fe540a1a00921e8a10d706cd88f (commit)
       via  790d0b5527a8fd832f7236e17b4ac1274b865901 (commit)
       via  8e839a08dc0c54d88c6e5a93b2be00f8eb81c2f9 (commit)
       via  e61c82ed57bfd7d5a0b25404cc1271a95b44c82c (commit)
       via  7450f285f169e7ab894c5b85e303d1065795529e (commit)
       via  4643f0af180b2c360a53a8d47660a92332e48aa9 (commit)
       via  ed8a874e740a95f78eba3f4a133b83794490dea7 (commit)
       via  95cc8210a2f620d6a2a886cf7577ed2d257d2bad (commit)
       via  52f3401fb59545555189a456f98fddbb2001b66d (commit)
       via  3fb09910a4c862fef0e8f3dc631df0fba49c8107 (commit)
       via  94d0481e3dbbc16f3178bcdb71e4b02af6ef85bb (commit)
       via  3d8e49eb1d45b2a70b5ecb90d77116653a26b11f (commit)
       via  d7368c1d83dc9c6cbbfd4230efa03858307062d4 (commit)
       via  c9a786613ef05879fd0bcf88e6db0adf855e6ea1 (commit)
       via  a8b060aa956d679e7a4330450c9baf7e470ad6a5 (commit)
       via  bb85c71585b1bafb659287dba976940d37178630 (commit)
       via  030ab7aacd96bf18bc1748cd30fecb9b4f60b890 (commit)
       via  6ce0831748423ab48611785f29ee999beeaf8322 (commit)
       via  32b6b02bc06bbf0303639964f3544e9cba4b138d (commit)
       via  da3bcafa328abd1bd6125f89a3091196954ef55d (commit)
       via  0ebb7cd975d578f3dea516b790e502fdd65cb683 (commit)
       via  318530edbf905ff288f527ef73bb88da4c57b548 (commit)
       via  3759773a821827277aa2c8d457dd6fe63f1daf75 (commit)
       via  79b7e9ce53c8bf6fc47285795599630cc9dc3ef8 (commit)
       via  13f6a9e896862d5ca4aab33386a4c47c6636fe4d (commit)
       via  d0f80f2a66545a7247e97e7a17c26ae812349d26 (commit)
       via  a651d9b90dc0bc9737e6f104d1fde7f4b7c2110e (commit)
       via  a8c08c6be23f58204300bb2c916fcda7aef7b4c1 (commit)
       via  e47b08e8a356c1bf498aa5a615fcce86958f1fe0 (commit)
       via  8fd5cc452a30a35df0a24b7bd1c30e76ea6bb54d (commit)
       via  7cdae45e8e18a190a5d2097655f33c0684ef9b9f (commit)
       via  0568e990cdd6304326274092234786b82ec61437 (commit)
       via  3b50bb10b7afea442ad30f344c1138b67f7badeb (commit)
       via  9e2d7b9b5f05168b165fb8d87a9c3d1466260dd9 (commit)
       via  c4af7dce242b7a4cd03d97fc27ede32c9d046b3f (commit)
       via  974e63e70778f147863990b81be45eee9609921c (commit)
       via  8693df3c6e3ba9e04f0a4f5d0f70646980986985 (commit)
       via  b3ea9e70fda28527deb8ac9b78c5ad253666ba0c (commit)
       via  a8ac0d4064ef4984b2327c5d27b743d257bf3ced (commit)
       via  5f923b7b3a02bb1335b551e584647f3e7d4c53de (commit)
       via  e7f01efd3518de220064f1eda65bcb95221cfd3c (commit)
       via  0333782cc1f51e1839e5cea59c72ef72a4b05929 (commit)
       via  1f339724101df031a6e5b0338bbd085132f0e1ac (commit)
       via  48a559472ac908be3bf697b11c01fb1a0fe13273 (commit)
       via  8daf69ddf18e902d8e2758ba33538ff5edfbb44c (commit)
       via  72fb34ade6d092d35809280d3b6a25f6a800b778 (commit)
       via  259634a0145c978f78f87cf84724553473e109fc (commit)
       via  99c3c7f2457327d843c7f8d1e1b5adcb2f826373 (commit)
       via  52b93e86bad0bc578336b9fc15e2a76c79dbecb9 (commit)
       via  6bd0c1d9f2f14339f83ce9fb1882f42ae23036a1 (commit)
       via  65ce062ac130500177a0adc4100289b8879c601c (commit)
       via  153bda11f4773ba8123c66d4a2d498a18a7b686a (commit)
       via  6b7ef74b531830333300edd110fa2396630dacaa (commit)
       via  f104a60a20ac0fd402461dbf3e5d94fca14ba81b (commit)
       via  634b1cb2a47760b880e3fb6aca5a30cfd0602871 (commit)
       via  98d529881bbb26780812a812cf0d1916a7195406 (commit)
       via  75effbfc1dd4d69ae829974dcbf48c9d9c13580f (commit)
       via  baa520eb355a55fa9c9459d2cd4cd705533345f9 (commit)
       via  2a98f3d106983b1e917af84747c089c5d8708df5 (commit)
       via  33d80f86895ac4ace68327554995503471054421 (commit)
       via  e2ff6e8c7eb46427bd96557566728cd64593449c (commit)
       via  ea84394f03c42756deddc0c575925dc4ee5b4e90 (commit)
       via  c4824042727cb37018bdc7fdc1986a909b6f0853 (commit)
       via  334ffd13161af15de4bfb404be77e73e085de7f6 (commit)
       via  e5336c30e084c9adb8c3c7aed879ba6004442d71 (commit)
       via  f7470975b47ef0cff5fda310ae1ed17fa20df2ba (commit)
       via  1f1d62190f05bc03a5d58b7cc247ce471bcf2b39 (commit)
       via  84a3a650b35dcc7b69c40383d28795a678d71044 (commit)
       via  9fecf9fce1733d3df9e6dce9545c1427c47265d0 (commit)
       via  606ba69e360a2d841e9ec5eb7e5bd32d2c86f062 (commit)
       via  14e57fb23c208b65884b5b6377836c15ed3d341c (commit)
       via  c5526a8ccd41201df0ec98f7b0da06113b9e6e12 (commit)
       via  9ece3d02afe58dcc487e0ca0a4c464f1ba53eca7 (commit)
       via  15d0b77ba78bc2507b65072a74294a85ee7e5440 (commit)
       via  0e2ef3c72360da9d0fcc4a36470bcb6b02591581 (commit)
       via  822fa31524fc37af1ffdd265f8554fb9d85471f1 (commit)
       via  2c50eecd72f4e59aaea65cab950ae058179ae1bd (commit)
       via  103c817670b1d4f839e8e82ae01fea0fe3aa1298 (commit)
       via  a709670c0cb9ca9f1b8ea4f13a7191fcba0ab8cd (commit)
       via  2d8efd0e48d097a54753ea6504e8e69bf65f41ba (commit)
       via  e0eda94e0b5f31efae914bf00deedf0955590c62 (commit)
       via  04447bd12e293df0f40f46e16c87a77f49cffe25 (commit)
       via  ae7e56576e33265085f6542787d11d28efa097b3 (commit)
       via  1ee4d989baee8e0fe013aa0c1e63f4e6a429d114 (commit)
       via  1649bf2b29e4770739fdc517c4e0d98253dfbe0d (commit)
       via  3c779a4236347f9d6128de3fd7f0ef5433a304a9 (commit)
       via  9e655bc3d628a2ada61d9d8f7518d655b0f84e7f (commit)
       via  21da96d4bcb34a2289e5b16eead8ce181d8eb61c (commit)
       via  7985a4bd28b3fb77fc1245ff1e0fe39350a46db2 (commit)
       via  4b79e1c508a01dbf6b1ae92f31e6256840b80207 (commit)
       via  92e23241bb54197ec11ed79691bf678cd042de6d (commit)
       via  f54d04b85937244a7c9cb00dc388387c1819eaa6 (commit)
       via  26f89e5cc91ec20cb7607e81b82ec7087d40de2f (commit)
       via  351dbdab0925b72ddaad6f95b9008426ceb834ee (commit)
       via  cc557baebc622a96b3f0813bde163f0397964a57 (commit)
       via  e000302572bb2ea1e407218e6d5484cd4bc54f25 (commit)
       via  fe03e032e35e80420588b047a208b76016c3f440 (commit)
       via  bd877566cf970b23df2d0a533b50c4e631e52788 (commit)
       via  09ff2c9562e3d82418e9b7e4adad99eb597fb1c3 (commit)
       via  d1512ddd6307de6227bae7e32c5c72b5f781c834 (commit)
       via  0585f306eca6145a94aa8af700bbf64f49207d15 (commit)
       via  7a2d5ea87d43cd40343eaf920bc2086fadd2b7cb (commit)
       via  50ca8b1b82973adbc44594812733aad0dd28bc2e (commit)
       via  c3d1b18082f595a950de2ca4b9b6692b18cd44a2 (commit)
       via  cdd87a91d1210ddbb24a2a1fb7bcc101dbd65329 (commit)
       via  487503d57b7beeedc2649ac8137c2e44040ec105 (commit)
       via  cc9c51e9a2c5c3ce7a662d8efb7e9fd01ca1c1f5 (commit)
       via  e5baa7542ae9b36a1205703020ce8d2ba0e2e9d9 (commit)
       via  1c0b26f0b66fb051d112e20cc8159d90b65722d4 (commit)
       via  b59a6d92e4d44ba1556b8b11938a2ffc2e2fa7d4 (commit)
      from  18831888667fe9363c22d3d19db9b0f98e56ef70 (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 b55b8b6686cc80eed41793c53d1779f4de3e9e3c
Merge: 1883188 7234eee
Author: Stephen Morris <stephen at isc.org>
Date:   Mon Jan 7 11:35:57 2013 +0000

    [2524] Merge branch 'master' into trac2524

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

Summary of changes:
 .gitignore                                         |    3 +-
 ChangeLog                                          |  108 +-
 README                                             |   24 +-
 configure.ac                                       |   76 +-
 doc/guide/bind10-guide.xml                         |   55 +-
 examples/README                                    |    2 +-
 m4macros/ax_boost_for_bind10.m4                    |  112 ++
 src/bin/auth/auth_messages.mes                     |    8 +-
 src/bin/auth/b10-auth.xml                          |   48 +-
 src/bin/auth/tests/.gitignore                      |    2 +
 src/bin/auth/tests/Makefile.am                     |   38 +-
 src/bin/auth/tests/gen-query-testdata.py           |   98 ++
 src/bin/auth/tests/query_inmemory_unittest.cc      |  123 ---
 src/bin/auth/tests/query_unittest.cc               | 1108 ++++++++++++--------
 src/bin/auth/tests/testdata/.gitignore             |   10 +
 src/bin/auth/tests/testdata/Makefile.am            |    7 +-
 src/bin/auth/tests/testdata/example-base-inc.zone  |  236 +++++
 src/bin/auth/tests/testdata/example-base.zone.in   |    7 +
 .../testdata/example-common-inc-template.zone      |    5 +
 src/bin/auth/tests/testdata/example-nsec3-inc.zone |   16 +
 src/bin/auth/tests/testdata/example-nsec3.zone.in  |    8 +
 src/bin/auth/tests/testdata/example.zone           |  121 ---
 src/bin/auth/tests/testdata/example.zone.in        |    6 +
 src/bin/bind10/bind10.xml                          |    2 +-
 src/bin/bind10/bind10_messages.mes                 |    4 +-
 src/bin/cfgmgr/b10-cfgmgr.xml                      |    2 +-
 src/bin/cmdctl/b10-certgen.xml                     |    2 +-
 src/bin/cmdctl/b10-cmdctl.xml                      |   12 +-
 src/bin/dbutil/dbutil_messages.mes                 |    6 +-
 src/bin/dhcp4/Makefile.am                          |    2 +-
 src/bin/dhcp4/config_parser.cc                     |  481 ++++++++-
 src/bin/dhcp4/config_parser.h                      |    1 +
 src/bin/dhcp4/dhcp4.spec                           |   68 +-
 src/bin/dhcp4/dhcp4_messages.mes                   |   29 +-
 src/bin/dhcp4/tests/Makefile.am                    |    6 +-
 src/bin/dhcp4/tests/config_parser_unittest.cc      |  476 +++++++++
 src/bin/dhcp6/Makefile.am                          |   12 +-
 src/bin/dhcp6/config_parser.cc                     |   39 +-
 src/bin/dhcp6/dhcp6_messages.mes                   |   69 +-
 src/bin/dhcp6/dhcp6_srv.cc                         |  187 +++-
 src/bin/dhcp6/dhcp6_srv.h                          |   39 +-
 src/bin/dhcp6/tests/Makefile.am                    |    9 +-
 src/bin/dhcp6/tests/config_parser_unittest.cc      |   14 +-
 src/bin/dhcp6/tests/dhcp6_srv_unittest.cc          |  382 +++++--
 src/bin/loadzone/.gitignore                        |    1 +
 src/bin/loadzone/Makefile.am                       |   29 +-
 src/bin/loadzone/TODO                              |   13 -
 src/bin/loadzone/b10-loadzone.py.in                |   94 --
 src/bin/loadzone/b10-loadzone.xml                  |  144 ++-
 src/bin/loadzone/loadzone.py.in                    |  342 ++++++
 src/bin/loadzone/loadzone_messages.mes             |   81 ++
 src/bin/loadzone/run_loadzone.sh.in                |   10 +-
 src/bin/{ddns => loadzone}/tests/Makefile.am       |   17 +-
 src/bin/loadzone/tests/correct/Makefile.am         |    5 +-
 src/bin/loadzone/tests/correct/correct_test.sh.in  |   18 +-
 src/bin/loadzone/tests/correct/example.db          |   14 +-
 src/bin/loadzone/tests/correct/include.db          |    8 +-
 src/bin/loadzone/tests/correct/known.test.out      |    4 +-
 src/bin/loadzone/tests/correct/mix1.db             |    8 +-
 src/bin/loadzone/tests/correct/mix2.db             |    8 +-
 src/bin/loadzone/tests/correct/mix2sub2.txt        |    4 +-
 src/bin/loadzone/tests/correct/ttl1.db             |    8 +-
 src/bin/loadzone/tests/correct/ttl2.db             |    8 +-
 src/bin/loadzone/tests/correct/ttlext.db           |    8 +-
 src/bin/loadzone/tests/error/.gitignore            |    1 -
 src/bin/loadzone/tests/error/Makefile.am           |   28 -
 src/bin/loadzone/tests/error/error.known           |   11 -
 src/bin/loadzone/tests/error/error_test.sh.in      |   82 --
 src/bin/loadzone/tests/error/formerr1.db           |   13 -
 src/bin/loadzone/tests/error/formerr2.db           |   12 -
 src/bin/loadzone/tests/error/formerr3.db           |   12 -
 src/bin/loadzone/tests/error/formerr4.db           |   12 -
 src/bin/loadzone/tests/error/formerr5.db           |   13 -
 src/bin/loadzone/tests/error/include.txt           |    1 -
 src/bin/loadzone/tests/error/keyerror1.db          |   12 -
 src/bin/loadzone/tests/error/keyerror2.db          |   12 -
 src/bin/loadzone/tests/error/keyerror3.db          |   13 -
 src/bin/loadzone/tests/error/originerr1.db         |   11 -
 src/bin/loadzone/tests/error/originerr2.db         |   12 -
 src/bin/loadzone/tests/loadzone_test.py            |  342 ++++++
 .../tests/testdata/broken-example.org.zone         |   11 +
 .../loadzone/tests/testdata/example-nons.org.zone  |   10 +
 .../loadzone/tests/testdata/example-nosoa.org.zone |    3 +
 src/bin/loadzone/tests/testdata/example.org.zone   |   10 +
 src/bin/msgq/Makefile.am                           |   13 +-
 src/bin/msgq/msgq.py.in                            |   55 +-
 src/bin/msgq/msgq.xml                              |    2 +-
 src/bin/msgq/msgq_messages.mes                     |   88 ++
 src/bin/msgq/run_msgq.sh.in                        |   18 +-
 src/bin/msgq/tests/Makefile.am                     |    2 +
 src/bin/msgq/tests/msgq_test.in                    |   28 -
 src/bin/msgq/tests/msgq_test.py                    |    3 +
 src/bin/resolver/b10-resolver.xml                  |    4 +-
 src/bin/stats/b10-stats-httpd.xml                  |    8 +-
 src/bin/stats/b10-stats.xml                        |    2 +-
 src/bin/xfrin/xfrin_messages.mes                   |   12 +-
 src/bin/xfrout/xfrout_messages.mes                 |    7 +-
 src/lib/asiodns/asiodns_messages.mes               |   16 +
 src/lib/asiodns/sync_udp_server.cc                 |    8 +-
 src/lib/asiodns/udp_server.cc                      |    8 +-
 src/lib/cc/data.cc                                 |    4 +-
 src/lib/datasrc/database.cc                        |    4 +-
 src/lib/datasrc/datasrc_messages.mes               |    8 +-
 src/lib/datasrc/memory/zone_data_loader.cc         |   27 +-
 .../tests/memory/rdata_serialization_unittest.cc   |    3 +
 .../datasrc/tests/zone_finder_context_unittest.cc  |   32 +-
 src/lib/dhcp/Makefile.am                           |    4 +-
 src/lib/dhcp/iface_mgr.cc                          |   31 +-
 src/lib/dhcp/libdhcp++.cc                          |  113 +-
 src/lib/dhcp/libdhcp++.h                           |    8 +-
 src/lib/dhcp/option.cc                             |   43 +-
 src/lib/dhcp/option.h                              |   16 +
 src/lib/dhcp/option4_addrlst.cc                    |    4 +-
 src/lib/dhcp/option6_addrlst.cc                    |   13 +-
 src/lib/dhcp/option6_iaaddr.cc                     |    8 +-
 src/lib/dhcp/option_custom.cc                      |   16 +-
 src/lib/dhcp/option_data_types.cc                  |   23 +-
 src/lib/dhcp/option_data_types.h                   |    2 +-
 src/lib/dhcp/option_definition.cc                  |   19 +-
 src/lib/dhcp/option_definition.h                   |   37 +-
 src/lib/dhcp/{option6_int.h => option_int.h}       |   29 +-
 .../{option6_int_array.h => option_int_array.h}    |   40 +-
 src/lib/dhcp/pkt4.cc                               |    6 +-
 src/lib/dhcp/std_option_defs.h                     |  209 +++-
 src/lib/dhcp/tests/Makefile.am                     |    4 +-
 src/lib/dhcp/tests/libdhcp++_unittest.cc           |  490 +++++++--
 src/lib/dhcp/tests/option_custom_unittest.cc       |   12 +-
 src/lib/dhcp/tests/option_data_types_unittest.cc   |   12 +-
 src/lib/dhcp/tests/option_definition_unittest.cc   |   87 +-
 ...ay_unittest.cc => option_int_array_unittest.cc} |  207 ++--
 ...ion6_int_unittest.cc => option_int_unittest.cc} |  309 ++++--
 src/lib/dhcp/tests/pkt4_unittest.cc                |   36 +-
 src/lib/dhcpsrv/Makefile.am                        |    2 +-
 src/lib/dhcpsrv/addr_utilities.cc                  |   28 +-
 src/lib/dhcpsrv/alloc_engine.cc                    |   95 +-
 src/lib/dhcpsrv/alloc_engine.h                     |   18 +
 src/lib/dhcpsrv/lease_mgr.cc                       |    8 +
 src/lib/dhcpsrv/lease_mgr.h                        |    4 +
 src/lib/dhcpsrv/memfile_lease_mgr.cc               |    5 +-
 src/lib/dhcpsrv/pool.cc                            |    8 +-
 src/lib/dhcpsrv/subnet.cc                          |    8 +-
 src/lib/dhcpsrv/tests/Makefile.am                  |    1 +
 src/lib/dhcpsrv/tests/alloc_engine_unittest.cc     |  184 +++-
 src/lib/dhcpsrv/tests/lease_mgr_unittest.cc        |   27 +-
 src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc  |   44 +-
 src/lib/dhcpsrv/tests/test_utils.cc                |   58 +
 src/lib/dhcpsrv/tests/test_utils.h                 |   49 +
 src/lib/dns/Makefile.am                            |    3 +-
 src/lib/dns/master_lexer.cc                        |    4 +-
 src/lib/dns/master_lexer.h                         |    3 +-
 src/lib/dns/master_loader.cc                       |  458 ++++++--
 .../logger.cc => dns/master_loader_callbacks.cc}   |   19 +-
 src/lib/dns/master_loader_callbacks.h              |   10 +
 src/lib/dns/python/pydnspp.cc                      |  385 +++----
 src/lib/dns/python/pydnspp_common.cc               |   17 +
 src/lib/dns/python/pydnspp_common.h                |   13 +
 src/lib/dns/rdata.h                                |    2 +-
 src/lib/dns/rdata/generic/detail/char_string.cc    |   27 +-
 src/lib/dns/rdata/generic/detail/char_string.h     |   19 +-
 src/lib/dns/rdata/generic/detail/txt_like.h        |   10 +-
 src/lib/dns/rdata/generic/soa_6.cc                 |   11 +
 src/lib/dns/rdata/generic/soa_6.h                  |    4 +
 src/lib/dns/rrclass-placeholder.h                  |   55 +-
 src/lib/dns/rrclass.cc                             |   17 +-
 src/lib/dns/rrcollator.cc                          |  110 ++
 src/lib/dns/rrcollator.h                           |  133 +++
 src/lib/dns/rrparamregistry-placeholder.cc         |   35 +-
 src/lib/dns/rrparamregistry.h                      |   45 +-
 src/lib/dns/rrttl.cc                               |   64 +-
 src/lib/dns/rrttl.h                                |   69 +-
 src/lib/dns/rrtype.cc                              |    7 +-
 src/lib/dns/tests/Makefile.am                      |    1 +
 src/lib/dns/tests/master_lexer_state_unittest.cc   |    5 +-
 src/lib/dns/tests/master_lexer_unittest.cc         |   20 +-
 src/lib/dns/tests/master_loader_unittest.cc        |  505 ++++++++-
 src/lib/dns/tests/rdata_char_string_unittest.cc    |   59 +-
 src/lib/dns/tests/rdata_soa_unittest.cc            |   19 +-
 src/lib/dns/tests/rdata_txt_like_unittest.cc       |   29 +-
 src/lib/dns/tests/rdata_unittest.cc                |    9 +-
 src/lib/dns/tests/rrclass_unittest.cc              |   10 +-
 src/lib/dns/tests/rrcollator_unittest.cc           |  214 ++++
 src/lib/dns/tests/rrparamregistry_unittest.cc      |    8 +-
 src/lib/dns/tests/rrttl_unittest.cc                |   17 +
 src/lib/dns/tests/testdata/Makefile.am             |    2 +
 src/lib/dns/tests/testdata/example.org             |    2 +
 src/lib/dns/tests/testdata/omitcheck.txt           |    1 +
 src/lib/dns/tests/testdata/origincheck.txt         |    5 +
 src/lib/log/log_messages.mes                       |    4 +-
 src/lib/log/logger_manager_impl.cc                 |   10 +
 src/lib/log/tests/.gitignore                       |    2 +
 src/lib/python/isc/datasrc/Makefile.am             |    4 +-
 src/lib/python/isc/datasrc/__init__.py             |    3 +-
 src/lib/python/isc/datasrc/client_python.cc        |   10 +
 src/lib/python/isc/datasrc/client_python.h         |   11 +
 src/lib/python/isc/datasrc/datasrc.cc              |   53 +-
 src/lib/python/isc/datasrc/master.py               |  616 -----------
 src/lib/python/isc/datasrc/tests/.gitignore        |    1 +
 src/lib/python/isc/datasrc/tests/Makefile.am       |   10 +-
 src/lib/python/isc/datasrc/tests/master_test.py    |   35 -
 .../{notify => datasrc}/tests/testdata/example.com |    2 -
 .../isc/datasrc/tests/testdata/example.com.ch      |    8 +
 .../tests/testdata/example.com.source.sqlite3}     |  Bin 70656 -> 70656 bytes
 .../isc/datasrc/tests/testdata/example.com.sqlite3 |  Bin 70656 -> 70656 bytes
 .../python/isc/datasrc/tests/zone_loader_test.py   |  223 ++++
 src/lib/python/isc/datasrc/zone_loader_inc.cc      |  116 ++
 src/lib/python/isc/datasrc/zone_loader_python.cc   |  262 +++++
 .../zone_loader_python.h}                          |   17 +-
 src/lib/python/isc/ddns/libddns_messages.mes       |    2 +-
 src/lib/python/isc/log_messages/Makefile.am        |    4 +
 .../python/isc/log_messages/loadzone_messages.py   |    1 +
 src/lib/python/isc/log_messages/msgq_messages.py   |    1 +
 src/lib/resolve/resolve_messages.mes               |    6 +-
 src/lib/server_common/server_common_messages.mes   |    4 +-
 src/lib/testutils/dnsmessage_test.h                |    3 +-
 src/lib/util/encode/base_n.cc                      |    3 +-
 tests/system/bindctl/setup.sh                      |    4 +-
 tests/system/bindctl/tests.sh                      |    4 +
 tests/system/conf.sh.in                            |    5 +-
 tests/system/glue/setup.sh.in                      |    8 +-
 tests/system/ixfr/in-1/setup.sh.in                 |    2 +-
 tests/system/ixfr/in-2/setup.sh.in                 |    2 +-
 tests/system/ixfr/in-3/setup.sh.in                 |    2 +-
 tests/system/ixfr/in-4/setup.sh.in                 |    2 +-
 tests/tools/perfdhcp/pkt_transform.cc              |   10 +-
 tests/tools/perfdhcp/tests/.gitignore              |    5 +
 225 files changed, 8762 insertions(+), 3296 deletions(-)
 create mode 100644 m4macros/ax_boost_for_bind10.m4
 create mode 100755 src/bin/auth/tests/gen-query-testdata.py
 delete mode 100644 src/bin/auth/tests/query_inmemory_unittest.cc
 create mode 100644 src/bin/auth/tests/testdata/example-base-inc.zone
 create mode 100644 src/bin/auth/tests/testdata/example-base.zone.in
 create mode 100644 src/bin/auth/tests/testdata/example-common-inc-template.zone
 create mode 100644 src/bin/auth/tests/testdata/example-nsec3-inc.zone
 create mode 100644 src/bin/auth/tests/testdata/example-nsec3.zone.in
 delete mode 100644 src/bin/auth/tests/testdata/example.zone
 create mode 100644 src/bin/auth/tests/testdata/example.zone.in
 delete mode 100644 src/bin/loadzone/b10-loadzone.py.in
 create mode 100755 src/bin/loadzone/loadzone.py.in
 create mode 100644 src/bin/loadzone/loadzone_messages.mes
 copy src/bin/{ddns => loadzone}/tests/Makefile.am (70%)
 delete mode 100644 src/bin/loadzone/tests/error/.gitignore
 delete mode 100644 src/bin/loadzone/tests/error/Makefile.am
 delete mode 100644 src/bin/loadzone/tests/error/error.known
 delete mode 100755 src/bin/loadzone/tests/error/error_test.sh.in
 delete mode 100644 src/bin/loadzone/tests/error/formerr1.db
 delete mode 100644 src/bin/loadzone/tests/error/formerr2.db
 delete mode 100644 src/bin/loadzone/tests/error/formerr3.db
 delete mode 100644 src/bin/loadzone/tests/error/formerr4.db
 delete mode 100644 src/bin/loadzone/tests/error/formerr5.db
 delete mode 100644 src/bin/loadzone/tests/error/include.txt
 delete mode 100644 src/bin/loadzone/tests/error/keyerror1.db
 delete mode 100644 src/bin/loadzone/tests/error/keyerror2.db
 delete mode 100644 src/bin/loadzone/tests/error/keyerror3.db
 delete mode 100644 src/bin/loadzone/tests/error/originerr1.db
 delete mode 100644 src/bin/loadzone/tests/error/originerr2.db
 create mode 100755 src/bin/loadzone/tests/loadzone_test.py
 create mode 100644 src/bin/loadzone/tests/testdata/broken-example.org.zone
 create mode 100644 src/bin/loadzone/tests/testdata/example-nons.org.zone
 create mode 100644 src/bin/loadzone/tests/testdata/example-nosoa.org.zone
 create mode 100644 src/bin/loadzone/tests/testdata/example.org.zone
 create mode 100644 src/bin/msgq/msgq_messages.mes
 delete mode 100755 src/bin/msgq/tests/msgq_test.in
 rename src/lib/dhcp/{option6_int.h => option_int.h} (89%)
 rename src/lib/dhcp/{option6_int_array.h => option_int_array.h} (88%)
 rename src/lib/dhcp/tests/{option6_int_array_unittest.cc => option_int_array_unittest.cc} (67%)
 rename src/lib/dhcp/tests/{option6_int_unittest.cc => option_int_unittest.cc} (50%)
 create mode 100644 src/lib/dhcpsrv/tests/test_utils.cc
 create mode 100644 src/lib/dhcpsrv/tests/test_utils.h
 copy src/lib/{datasrc/memory/logger.cc => dns/master_loader_callbacks.cc} (70%)
 create mode 100644 src/lib/dns/rrcollator.cc
 create mode 100644 src/lib/dns/rrcollator.h
 create mode 100644 src/lib/dns/tests/rrcollator_unittest.cc
 create mode 100644 src/lib/dns/tests/testdata/omitcheck.txt
 create mode 100644 src/lib/dns/tests/testdata/origincheck.txt
 delete mode 100644 src/lib/python/isc/datasrc/master.py
 delete mode 100644 src/lib/python/isc/datasrc/tests/master_test.py
 copy src/lib/python/isc/{notify => datasrc}/tests/testdata/example.com (80%)
 create mode 100644 src/lib/python/isc/datasrc/tests/testdata/example.com.ch
 copy src/lib/{datasrc/tests/testdata/test.sqlite3 => python/isc/datasrc/tests/testdata/example.com.source.sqlite3} (51%)
 create mode 100644 src/lib/python/isc/datasrc/tests/zone_loader_test.py
 create mode 100644 src/lib/python/isc/datasrc/zone_loader_inc.cc
 create mode 100644 src/lib/python/isc/datasrc/zone_loader_python.cc
 copy src/lib/python/isc/{util/cio/socketsession_python.h => datasrc/zone_loader_python.h} (76%)
 create mode 100644 src/lib/python/isc/log_messages/loadzone_messages.py
 create mode 100644 src/lib/python/isc/log_messages/msgq_messages.py

-----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index dd6903c..7bc41b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,5 +34,6 @@ TAGS
 /all.info
 /coverage-cpp-html
 /dns++.pc
-/report.info
+/local.zone.sqlite3
 /logger_lockfile
+/report.info
diff --git a/ChangeLog b/ChangeLog
index eb15baf..3b5fb24 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,110 @@
-526.	[bug]		syephen
+538.	[bug]		muks
+	Added escaping of special characters (double-quotes, semicolon,
+	backslash, etc.) in text-like RRType's toText() implementation.
+	Without this change, some TXT and SPF RDATA were incorrectly
+	stored in SQLite3 datasource as they were not escaped.
+	(Trac #2535, git f516fc484544b7e08475947d6945bc87636d4115)
+
+537.	[func]		tomek
+	b10-dhcp6: Support for RELEASE message has been added. Clients
+	are now able to release their non-temporary IPv6 addresses.
+	(Trac #2326, git 0974318566abe08d0702ddd185156842c6642424)
+
+536.	[build]		jinmei
+	Detect a build issue on FreeBSD with g++ 4.2 and Boost installed via
+	FreeBSD ports at ./configure time.  This seems to be a bug of
+	FreeBSD	ports setup and has been reported to the maintainer:
+	http://www.freebsd.org/cgi/query-pr.cgi?pr=174753
+	Until it's fixed, you need to build BIND 10 for FreeBSD that has
+	this problem with specifying --without-werror, with clang++
+	(development version), or with manually extracted Boost header
+	files (no compiled Boost library is necessary).
+	(Trac #1991, git 6b045bcd1f9613e3835551cdebd2616ea8319a36)
+
+535.	[bug]		jelte
+	The log4cplus internal logging mechanism has been disabled, and no
+	output from the log4cplus library itself should be printed to
+	stderr anymore. This output can be enabled by using the
+	compile-time option --enable-debug.
+	(Trac #1081, git db55f102b30e76b72b134cbd77bd183cd01f95c0)
+
+534.	[func]*			vorner
+	The b10-msgq now uses the same logging format as the rest
+	of the system. However, it still doesn't obey the common
+	configuration, as due to technical issues it is not able
+	to read it yet.
+	(git 9e6e821c0a33aab0cd0e70e51059d9a2761f76bb)
+
+bind10-1.0.0-beta released on December 20, 2012
+
+533.	[build]*		jreed
+	Changed the package name in configure.ac from bind10-devel
+	to bind10. This means the default sub-directories for
+	etc, include, libexec, share, share/doc, and var are changed.
+	If upgrading from a previous version, you may need to move
+	and update your configurations or change references for the
+	old locations.
+	(git bf53fbd4e92ae835280d49fbfdeeebd33e0ce3f2)
+
+532.	[func]		marcin
+	Implemented configuration of DHCPv4 option values using
+	the configuration manager. In order to set values for the
+	data fields carried by a particular option, the user
+	specifies a string of hexadecimal digits that is converted
+	to binary data and stored in the option buffer. A more
+	user-friendly way of specifying option content is planned.
+	(Trac #2544, git fed1aab5a0f813c41637807f8c0c5f8830d71942)
+
+531.	[func]		tomek
+	b10-dhcp6: Added support for expired leases. Leases for IPv6
+	addresses that are past their valid lifetime may be recycled, i.e.
+	rellocated to other clients if needed.
+	(Trac #2327, git 62a23854f619349d319d02c3a385d9bc55442d5e)
+
+530.	[func]*		team
+	b10-loadzone was fully overhauled.  It now uses C++-based zone
+	parser and loader library, performing stricter checks, having
+	more complete support for master file formats, producing more
+	helpful logs, is more extendable for various types of data
+	sources, and yet much faster than the old version.  In
+	functionality the new version should be generally backwards
+	compatible to the old version, but there are some
+	incompatibilities: name fields of RDATA (in NS, SOA, etc) must
+	be absolute for now; due to the stricter checks some input that was
+	(incorrectly) accepted by the old version may now be rejected;
+	command line options and arguments are not compatible.
+	(Trac #2380, git 689b015753a9e219bc90af0a0b818ada26cc5968)
+
+529.	[func]*		team
+	The in-memory data source now uses a more complete master
+	file parser to load textual zone files.  As of this change
+	it supports multi-line RR representation and more complete
+	support for escaped and quoted strings.  It also produces
+	more helpful log messages when there is an error in the zone
+	file.  It will be enhanced as more specific tasks in the
+	#2368 meta ticket are completed.  The new parser is generally
+	backward compatible to the previous one, but due to the
+	tighter checks some input that has been accepted so far
+	could now be rejected, so it's advisable to check if you
+	use textual zone files directly loaded to memory.
+	(Trac #2470, git c4cf36691115c15440b65cac16f1c7fcccc69521)
+
+528.	[func]		marcin
+	Implemented definitions for DHCPv4 option definitions identified
+	by option codes: 1 to 63, 77, 81-82, 90-92, 118-119, 124-125.
+	These definitions are now used by the DHCPv4 server to parse
+	options received from a client.
+	(Trac #2526, git 50a73567e8067fdbe4405b7ece5b08948ef87f98)
+
+527.	[bug]		jelte
+	Fixed a bug in the synchronous UDP server code where unexpected
+	errors from ASIO or the system libraries could cause b10-auth to
+	stop. In asynchronous mode these errors would be ignored
+	completely. Both types have been updated to report the problem with
+	an ERROR log message, drop the packet, and continue service.
+	(Trac #2494, git db92f30af10e6688a7dc117b254cb821e54a6d95)
+
+526.	[bug]		stephen
 	Miscellaneous fixes to DHCP code including rationalisation of
 	some methods in LeaseMgr and resolving some Doxygen/cppcheck
 	issues.
diff --git a/README b/README
index fdc11a1..306b7d8 100644
--- a/README
+++ b/README
@@ -7,16 +7,20 @@ DHCP. BIND 10 is written in C++ and Python and provides a modular
 environment for serving, maintaining, and developing DNS and DHCP.
 
 This release includes the bind10 master process, b10-msgq message
-bus, b10-auth authoritative DNS server (with SQLite3 and in-memory
-backends), b10-resolver recursive or forwarding DNS server, b10-cmdctl
-remote control daemon, b10-cfgmgr configuration manager, b10-xfrin
-AXFR inbound service, b10-xfrout outgoing AXFR service, b10-zonemgr
-secondary manager, b10-stats statistics collection and reporting
-daemon, b10-stats-httpd for HTTP access to XML-formatted stats,
-b10-host DNS lookup utility, and a new libdns++ library for C++
-with a python wrapper. BIND 10 also provides experimental DHCPv4
-and DHCPv6 servers, b10-dhcp4 and b10-dhcp6, a portable DHCP library,
-libdhcp++, and a DHCP benchmarking tool, perfdhcp.
+bus, b10-cmdctl remote control daemon, b10-cfgmgr configuration
+manager, b10-stats statistics collection and reporting daemon, and
+b10-stats-httpd for HTTP access to XML-formatted stats.
+
+For DNS services, it provides the b10-auth authoritative DNS server
+(with SQLite3 and in-memory backends), b10-resolver recursive or
+forwarding DNS server, b10-xfrin IXFR/AXFR inbound service, b10-xfrout
+outgoing IXFR/AXFR service, b10-zonemgr secondary manager, libdns++
+library for C++ with a python wrapper, and many tests and example
+programs.
+
+BIND 10 also provides experimental DHCPv4 and DHCPv6 servers,
+b10-dhcp4 and b10-dhcp6, a portable DHCP library, libdhcp++, and
+a DHCP benchmarking tool, perfdhcp.
 
 Documentation is included with the source. See doc/guide/bind10-guide.txt
 (or bind10-guide.html) for installation instructions.  The
diff --git a/configure.ac b/configure.ac
index 3f7a269..892bc6c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.59])
-AC_INIT(bind10-devel, 20120817, bind10-dev at isc.org)
+AC_INIT(bind10, 20121219, bind10-dev at isc.org)
 AC_CONFIG_SRCDIR(README)
 AM_INIT_AUTOMAKE([foreign])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])dnl be backward compatible
@@ -166,8 +166,6 @@ fi
 
 fi				dnl GXX = yes
 
-AM_CONDITIONAL(GCC_WERROR_OK, test $werror_ok = 1)
-
 # allow building programs with static link.  we need to make it selective
 # because loadable modules cannot be statically linked.
 AC_ARG_ENABLE([static-link],
@@ -838,57 +836,22 @@ LIBS=$LIBS_SAVED
 #
 # Configure Boost header path
 #
-# If explicitly specified, use it.
-AC_ARG_WITH([boost-include],
-  AC_HELP_STRING([--with-boost-include=PATH],
-    [specify exact directory for Boost headers]),
-    [boost_include_path="$withval"])
-# If not specified, try some common paths.
-if test -z "$with_boost_include"; then
-	boostdirs="/usr/local /usr/pkg /opt /opt/local"
-	for d in $boostdirs
-	do
-		if test -f $d/include/boost/shared_ptr.hpp; then
-			boost_include_path=$d/include
-			break
-		fi
-	done
+AX_BOOST_FOR_BIND10
+# Boost offset_ptr is required in one library and not optional right now, so
+# we unconditionally fail here if it doesn't work.
+if test "$BOOST_OFFSET_PTR_FAILURE" = "yes"; then
+    AC_MSG_ERROR([Failed to compile a required header file.  Try upgrading Boost to 1.44 or higher (when using clang++) or specifying --without-werror.  See the ChangeLog entry for Trac no. 2147 for more details.])
 fi
-CPPFLAGS_SAVES="$CPPFLAGS"
-if test "${boost_include_path}" ; then
-	BOOST_INCLUDES="-I${boost_include_path}"
-	CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
+
+# There's a known bug in FreeBSD ports for Boost that would trigger a false
+# warning in build with g++ and -Werror (we exclude clang++ explicitly to
+# avoid unexpected false positives).
+if test "$BOOST_NUMERIC_CAST_WOULDFAIL" = "yes" -a X"$werror_ok" = X1 -a $CLANGPP = "no"; then
+    AC_MSG_ERROR([Failed to compile a required header file.  If you are using FreeBSD and Boost installed via ports, retry with specifying --without-werror.  See the ChangeLog entry for Trac no. 1991 for more details.])
 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.]))
-
-# 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)])
-
-# Boost offset_ptr is required in one library (not optional right now), and
-# it's known it doesn't compile on some platforms, depending on boost version,
-# its local configuration, and compiler.
-AC_MSG_CHECKING([Boost offset_ptr compiles])
-AC_TRY_COMPILE([
-#include <boost/interprocess/offset_ptr.hpp>
-],,
-[AC_MSG_RESULT(yes)],
-[AC_MSG_RESULT(no)
- AC_MSG_ERROR([Failed to compile a required header file.  Try upgrading Boost to 1.44 or higher (when using clang++) or specifying --without-werror.  See the ChangeLog entry for Trac no. 2147 for more details.])])
 
-CPPFLAGS="$CPPFLAGS_SAVES $CPPFLAGS_BOOST_THREADCONF"
-AC_SUBST(BOOST_INCLUDES)
+# Add some default CPP flags needed for Boost, identified by the AX macro.
+CPPFLAGS="$CPPFLAGS $CPPFLAGS_BOOST_THREADCONF"
 
 # I can't get some of the #include <asio.hpp> right without this
 # TODO: find the real cause of asio/boost wanting pthreads
@@ -1176,8 +1139,8 @@ AC_CONFIG_FILES([Makefile
                  src/bin/dbutil/tests/Makefile
                  src/bin/dbutil/tests/testdata/Makefile
                  src/bin/loadzone/Makefile
+                 src/bin/loadzone/tests/Makefile
                  src/bin/loadzone/tests/correct/Makefile
-                 src/bin/loadzone/tests/error/Makefile
                  src/bin/msgq/Makefile
                  src/bin/msgq/tests/Makefile
                  src/bin/auth/Makefile
@@ -1350,15 +1313,16 @@ AC_OUTPUT([doc/version.ent
            src/bin/bindctl/tests/bindctl_test
            src/bin/loadzone/run_loadzone.sh
            src/bin/loadzone/tests/correct/correct_test.sh
-           src/bin/loadzone/tests/error/error_test.sh
-           src/bin/loadzone/b10-loadzone.py
+           src/bin/loadzone/loadzone.py
            src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            src/bin/usermgr/b10-cmdctl-usermgr.py
            src/bin/msgq/msgq.py
-           src/bin/msgq/tests/msgq_test
            src/bin/msgq/run_msgq.sh
            src/bin/auth/auth.spec.pre
            src/bin/auth/spec_config.h.pre
+           src/bin/auth/tests/testdata/example.zone
+           src/bin/auth/tests/testdata/example-base.zone
+           src/bin/auth/tests/testdata/example-nsec3.zone
            src/bin/dhcp4/spec_config.h.pre
            src/bin/dhcp6/spec_config.h.pre
            src/bin/tests/process_rename_test.py
@@ -1418,11 +1382,9 @@ AC_OUTPUT([doc/version.ent
            chmod +x src/bin/bindctl/run_bindctl.sh
            chmod +x src/bin/loadzone/run_loadzone.sh
            chmod +x src/bin/loadzone/tests/correct/correct_test.sh
-           chmod +x src/bin/loadzone/tests/error/error_test.sh
            chmod +x src/bin/sysinfo/run_sysinfo.sh
            chmod +x src/bin/usermgr/run_b10-cmdctl-usermgr.sh
            chmod +x src/bin/msgq/run_msgq.sh
-           chmod +x src/bin/msgq/tests/msgq_test
            chmod +x src/lib/dns/gen-rdatacode.py
            chmod +x src/lib/log/tests/console_test.sh
            chmod +x src/lib/log/tests/destination_test.sh
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index 61a9ee4..23756b4 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -449,8 +449,10 @@ var/
 
         <listitem>
           <para>Load desired zone file(s), for example:
-            <screen>$ <userinput>b10-loadzone <replaceable>your.zone.example.org</replaceable></userinput></screen>
+            <screen>$ <userinput>b10-loadzone <replaceable>-c '{"database_file": "/usr/local/var/bind10/zone.sqlite3"}'</replaceable> <replaceable>your.zone.example.org</replaceable> <replaceable>your.zone.file</replaceable></userinput></screen>
           </para>
+	  (If you use the sqlite3 data source with the default DB
+	  file, you can omit the -c option).
         </listitem>
 
         <listitem>
@@ -501,7 +503,7 @@ var/
           </listitem>
           <listitem>
           <simpara>
-            <filename>etc/bind10-devel/</filename> —
+            <filename>etc/bind10/</filename> —
             configuration files.
           </simpara>
           </listitem>
@@ -513,7 +515,7 @@ var/
           </listitem>
           <listitem>
             <simpara>
-              <filename>libexec/bind10-devel/</filename> —
+              <filename>libexec/bind10/</filename> —
               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
@@ -528,13 +530,13 @@ var/
           </listitem>
           <listitem>
             <simpara>
-              <filename>share/bind10-devel/</filename> —
+              <filename>share/bind10/</filename> —
               configuration specifications.
             </simpara>
           </listitem>
           <listitem>
             <simpara>
-              <filename>share/doc/bind10-devel/</filename> —
+              <filename>share/doc/bind10/</filename> —
               this guide and other supplementary documentation.
             </simpara>
           </listitem>
@@ -546,7 +548,7 @@ var/
           </listitem>
           <listitem>
             <simpara>
-              <filename>var/bind10-devel/</filename> —
+              <filename>var/bind10/</filename> —
               data source and configuration databases.
             </simpara>
           </listitem>
@@ -908,7 +910,7 @@ as a dependency earlier -->
         Administrators do not communicate directly with the
         <command>b10-msgq</command> daemon.
         By default, BIND 10 uses a UNIX domain socket file named
-        <filename>/usr/local/var/bind10-devel/msg_socket</filename>
+        <filename>/usr/local/var/bind10/msg_socket</filename>
         for this interprocess communication.
       </para>
 
@@ -970,7 +972,7 @@ config changes are actually commands to cfgmgr
 <!-- TODO: what about command line switch to change this? -->
       <para>
         The stored configuration file is at
-        <filename>/usr/local/var/bind10-devel/b10-config.db</filename>.
+        <filename>/usr/local/var/bind10/b10-config.db</filename>.
         (The directory is what was defined at build configure time for
         <option>--localstatedir</option>.
         The default is <filename>/usr/local/var/</filename>.)
@@ -1063,13 +1065,13 @@ but you might wanna check with likun
     <para>The HTTPS server requires a private key,
       such as a RSA PRIVATE KEY.
       The default location is at
-      <filename>/usr/local/etc/bind10-devel/cmdctl-keyfile.pem</filename>.
+      <filename>/usr/local/etc/bind10/cmdctl-keyfile.pem</filename>.
       (A sample key is at
-      <filename>/usr/local/share/bind10-devel/cmdctl-keyfile.pem</filename>.)
+      <filename>/usr/local/share/bind10/cmdctl-keyfile.pem</filename>.)
       It also uses a certificate located at
-      <filename>/usr/local/etc/bind10-devel/cmdctl-certfile.pem</filename>.
+      <filename>/usr/local/etc/bind10/cmdctl-certfile.pem</filename>.
       (A sample certificate is at
-      <filename>/usr/local/share/bind10-devel/cmdctl-certfile.pem</filename>.)
+      <filename>/usr/local/share/bind10/cmdctl-certfile.pem</filename>.)
       This may be a self-signed certificate or purchased from a
       certification authority.
     </para>
@@ -1105,11 +1107,11 @@ but that is a single file, maybe this should go back to that format?
     <para>
       The <command>b10-cmdctl</command> daemon also requires
       the user account file located at
-      <filename>/usr/local/etc/bind10-devel/cmdctl-accounts.csv</filename>.
+      <filename>/usr/local/etc/bind10/cmdctl-accounts.csv</filename>.
       This comma-delimited file lists the accounts with a user name,
       hashed password, and salt.
       (A sample file is at
-      <filename>/usr/local/share/bind10-devel/cmdctl-accounts.csv</filename>.
+      <filename>/usr/local/share/bind10/cmdctl-accounts.csv</filename>.
       It contains the user named <quote>root</quote> with the password
       <quote>bind10</quote>.)
     </para>
@@ -1139,14 +1141,14 @@ or accounts database -->
         The configuration items for <command>b10-cmdctl</command> are:
         <varname>accounts_file</varname> which defines the path to the
         user accounts database (the default is
-        <filename>/usr/local/etc/bind10-devel/cmdctl-accounts.csv</filename>);
+        <filename>/usr/local/etc/bind10/cmdctl-accounts.csv</filename>);
         <varname>cert_file</varname> which defines the path to the
         PEM certificate file (the default is
-        <filename>/usr/local/etc/bind10-devel/cmdctl-certfile.pem</filename>);
+        <filename>/usr/local/etc/bind10/cmdctl-certfile.pem</filename>);
         and
 	<varname>key_file</varname> which defines the path to the
 	PEM private key file (the default is
-        <filename>/usr/local/etc/bind10-devel/cmdctl-keyfile.pem</filename>).
+        <filename>/usr/local/etc/bind10/cmdctl-keyfile.pem</filename>).
       </para>
 
     </section>
@@ -1870,7 +1872,7 @@ tsig_keys/keys[0]   "example.key.:c2VjcmV0" string  (modified)
         Each rule is a name-value mapping (a dictionary, in the JSON
         terminology). Each rule must contain exactly one mapping called
         "action", which describes what should happen if the rule applies.
-        There may be more mappings, calld matches, which describe the
+        There may be more mappings, called matches, which describe the
         conditions under which the rule applies.
       </para>
 
@@ -2457,7 +2459,7 @@ can use various data source backends.
         data source — one that serves things like
         <quote>AUTHORS.BIND.</quote>. The IN class contains single SQLite3
         data source with database file located at
-        <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
+        <filename>/usr/local/var/bind10/zone.sqlite3</filename>.
       </para>
 
       <para>
@@ -2636,19 +2638,10 @@ can use various data source backends.
 
       </para>
 
-      <para>
-        The <option>-o</option> argument may be used to define the
-        default origin for loaded zone file records.
-      </para>
-
       <note>
       <para>
         In the current release, only the SQLite3 back
         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
-        database filename.
         Multiple zones are stored in a single SQLite3 zone database.
       </para>
       </note>
@@ -3680,7 +3673,7 @@ mysql></screen>
          <para>
           3. Create the database tables:
           <screen>mysql> <userinput>CONNECT kea;</userinput>
-mysql> <userinput>SOURCE <replaceable><path-to-bind10></replaceable>/share/bind10-devel/dhcpdb_create.mysql</userinput></screen>
+mysql> <userinput>SOURCE <replaceable><path-to-bind10></replaceable>/share/bind10/dhcpdb_create.mysql</userinput></screen>
         </para>
          <para>
           4. Create the user under which BIND 10 will access the database and grant it access to the database tables:
@@ -3878,7 +3871,7 @@ Dhcp6/subnet6	         []     list    (default)</screen>
     <section id="dhcp6-limit">
       <title>DHCPv6 Server Limitations</title>
       <para> These are the current limitations and known problems
-      with the the DHCPv6 server
+      with the DHCPv6 server
       software. Most of them are reflections of the early stage of
       development and should be treated as <quote>not implemented
       yet</quote>, rather than actual limitations.</para>
@@ -4163,7 +4156,7 @@ specify module-wide logging and see what appears...
           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
+          precedence. For example, if there are entries for
           both <quote>*</quote> and <quote>Resolver</quote>, the
           resolver module — and all libraries it uses —
           will log messages according to the configuration in the
diff --git a/examples/README b/examples/README
index 08f53fa..aa5f3c9 100644
--- a/examples/README
+++ b/examples/README
@@ -15,7 +15,7 @@ the "m4" subdirectory as a template for your own project.  The key is
 to call the AX_ISC_BIND10 function (as the sample configure.ac does)
 from your configure.ac.  Then it will check the availability of
 necessary stuff and set some corresponding AC variables.  You can then
-use the resulting variables in your Makefile.in or Makefile.ac.
+use the resulting variables in your Makefile.in or Makefile.am.
 
 If you use automake, don't forget adding the following line to the top
 level Makefile.am:
diff --git a/m4macros/ax_boost_for_bind10.m4 b/m4macros/ax_boost_for_bind10.m4
new file mode 100644
index 0000000..1ce367e
--- /dev/null
+++ b/m4macros/ax_boost_for_bind10.m4
@@ -0,0 +1,112 @@
+dnl @synopsis AX_BOOST_FOR_BIND10
+dnl
+dnl Test for the Boost C++ header files intended to be used within BIND 10
+dnl
+dnl If no path to the installed boost header files is given via the
+dnl --with-boost-include option,  the macro searchs under
+dnl /usr/local /usr/pkg /opt /opt/local directories.
+dnl If it cannot detect any workable path for Boost, this macro treats it
+dnl as a fatal error (so it cannot be called if the availability of Boost
+dnl is optional).
+dnl
+dnl This macro also tries to identify some known portability issues, and
+dnl sets corresponding variables so the caller can react to (or ignore,
+dnl depending on other configuration) specific issues appropriately.
+dnl
+dnl This macro calls:
+dnl
+dnl   AC_SUBST(BOOST_INCLUDES)
+dnl
+dnl And possibly sets:
+dnl   CPPFLAGS_BOOST_THREADCONF should be added to CPPFLAGS by caller
+dnl   BOOST_OFFSET_PTR_WOULDFAIL set to "yes" if offset_ptr would cause build
+dnl                              error; otherwise set to "no"
+dnl   BOOST_NUMERIC_CAST_WOULDFAIL set to "yes" if numeric_cast would cause
+dnl                                build error; otherwise set to "no"
+dnl
+
+AC_DEFUN([AX_BOOST_FOR_BIND10], [
+AC_LANG_SAVE
+AC_LANG([C++])
+
+#
+# Configure Boost header path
+#
+# If explicitly specified, use it.
+AC_ARG_WITH([boost-include],
+  AC_HELP_STRING([--with-boost-include=PATH],
+    [specify exact directory for Boost headers]),
+    [boost_include_path="$withval"])
+# If not specified, try some common paths.
+if test -z "$with_boost_include"; then
+	boostdirs="/usr/local /usr/pkg /opt /opt/local"
+	for d in $boostdirs
+	do
+		if test -f $d/include/boost/shared_ptr.hpp; then
+			boost_include_path=$d/include
+			break
+		fi
+	done
+fi
+
+# Check the path with some specific headers.
+CPPFLAGS_SAVED="$CPPFLAGS"
+if test "${boost_include_path}" ; then
+	BOOST_INCLUDES="-I${boost_include_path}"
+	CPPFLAGS="$CPPFLAGS $BOOST_INCLUDES"
+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.]))
+
+# 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)])
+
+# Boost offset_ptr is known to not compile on some platforms, depending on
+# boost version, its local configuration, and compiler.  Detect it.
+AC_MSG_CHECKING([Boost offset_ptr compiles])
+AC_TRY_COMPILE([
+#include <boost/interprocess/offset_ptr.hpp>
+],,
+[AC_MSG_RESULT(yes)
+ BOOST_OFFSET_PTR_WOULDFAIL=no],
+[AC_MSG_RESULT(no)
+ BOOST_OFFSET_PTR_WOULDFAIL=yes])
+
+# Detect build failure case known to happen with Boost installed via
+# FreeBSD ports
+if test "X$GXX" = "Xyes"; then
+   CXXFLAGS_SAVED="$CXXFLAGS"
+   CXXFLAGS="$CXXFLAGS -Werror"
+
+   AC_MSG_CHECKING([Boost numeric_cast compiles with -Werror])
+   AC_TRY_COMPILE([
+   #include <boost/numeric/conversion/cast.hpp>
+   ],[
+   return (boost::numeric_cast<short>(0));
+   ],[AC_MSG_RESULT(yes)
+      BOOST_NUMERIC_CAST_WOULDFAIL=no],
+   [AC_MSG_RESULT(no)
+    BOOST_NUMERIC_CAST_WOULDFAIL=yes])
+
+   CXXFLAGS="$CXXFLAGS_SAVED"
+else
+  # This doesn't matter for non-g++
+  BOOST_NUMERIC_CAST_WOULDFAIL=no
+fi
+
+AC_SUBST(BOOST_INCLUDES)
+
+CPPFLAGS="$CPPFLAGS_SAVED"
+AC_LANG_RESTORE
+])dnl AX_BOOST_FOR_BIND10
diff --git a/src/bin/auth/auth_messages.mes b/src/bin/auth/auth_messages.mes
index ac9ffb9..d93da51 100644
--- a/src/bin/auth/auth_messages.mes
+++ b/src/bin/auth/auth_messages.mes
@@ -141,7 +141,7 @@ The specific problem is printed in the log message.
 The thread for maintaining data source clients has received a command to
 reconfigure, and has now started this process.
 
-% AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_SUCCESS data source reconfiguration completed succesfully
+% AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_SUCCESS data source reconfiguration completed successfully
 The thread for maintaining data source clients has finished reconfiguring
 the data source clients, and is now running with the new configuration.
 
@@ -169,7 +169,7 @@ probably better to stop and restart it.
 
 % AUTH_DATA_SOURCE data source database file: %1
 This is a debug message produced by the authoritative server when it accesses a
-datebase data source, listing the file that is being accessed.
+database data source, listing the file that is being accessed.
 
 % AUTH_DNS_SERVICES_CREATED DNS services created
 This is a debug message indicating that the component that will handling
@@ -184,7 +184,7 @@ 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
+An error was encountered when the authoritative server specified
 statistics data which is invalid for the auth specification file.
 
 % AUTH_LOAD_TSIG loading TSIG keys
@@ -208,7 +208,7 @@ requests to b10-ddns) to handle it, but it failed.  The authoritative
 server returns SERVFAIL to the client on behalf of the separate
 process.  The error could be configuration mismatch between b10-auth
 and the recipient component, or it may be because the requests are
-coming too fast and the receipient process cannot keep up with the
+coming too fast and the recipient process cannot keep up with the
 rate, or some system level failure.  In either case this means the
 BIND 10 system is not working as expected, so the administrator should
 look into the cause and address the issue.  The log message includes
diff --git a/src/bin/auth/b10-auth.xml b/src/bin/auth/b10-auth.xml
index b34009d..88f80e2 100644
--- a/src/bin/auth/b10-auth.xml
+++ b/src/bin/auth/b10-auth.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>June 20, 2012</date>
+    <date>December 18, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -100,7 +100,7 @@
       <varname>database_file</varname> defines the path to the
       SQLite3 zone file when using the sqlite datasource.
       The default is
-      <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
+      <filename>/usr/local/var/bind10/zone.sqlite3</filename>.
     </para>
 
     <para>
@@ -157,6 +157,7 @@
       incoming TCP connections, in milliseconds. If the query
       is not sent within this time, the connection is closed.
       Setting this to 0 will disable TCP timeouts completely.
+      The default is 5000 (five seconds).
     </para>
 
 <!-- TODO: formating -->
@@ -165,6 +166,15 @@
     </para>
 
     <para>
+      <command>getstats</command> tells <command>b10-auth</command>
+      to report its defined statistics data in JSON format.
+      It will not report about unused counters.
+      This is used by the
+      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry> daemon.
+      (The <command>sendstats</command> command is deprecated.)
+    </para>
+
+    <para>
       <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
@@ -181,13 +191,6 @@
     </para>
 
     <para>
-      <command>sendstats</command> tells <command>b10-auth</command>
-      to send its statistics data to
-      <citerefentry><refentrytitle>b10-stats</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      immediately.
-    </para>
-
-    <para>
       <command>shutdown</command> exits <command>b10-auth</command>.
       This has an optional <varname>pid</varname> argument to
       select the process ID to stop.
@@ -195,6 +198,28 @@
       if configured.)
     </para>
 
+    <para>
+      <command>start_ddns_forwarder</command> starts (or restarts) the
+      internal forwarding of DDNS Update messages.
+      This is used by the
+      <citerefentry><refentrytitle>b10-ddns</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      daemon to tell <command>b10-auth</command> that DDNS Update
+      messages can be forwarded.
+      <note><simpara>This is not expected to be called by administrators;
+        it will be removed as a public command in the future.</simpara></note>
+    </para>
+
+    <para>
+      <command>stop_ddns_forwarder</command> stops the internal
+      forwarding of DDNS Update messages.
+      This is used by the
+      <citerefentry><refentrytitle>b10-ddns</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      daemon to tell <command>b10-auth</command> that DDNS Update
+      messages should not be forwarded.
+      <note><simpara>This is not expected to be called by administrators;
+        it will be removed as a public command in the future.</simpara></note>
+    </para>
+
   </refsect1>
 
   <refsect1>
@@ -230,7 +255,7 @@
   <refsect1>
     <title>FILES</title>
     <para>
-      <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>
+      <filename>/usr/local/var/bind10/zone.sqlite3</filename>
       — Location for the SQLite3 zone database
       when <emphasis>database_file</emphasis> configuration is not
       defined.
@@ -244,6 +269,9 @@
         <refentrytitle>b10-cfgmgr</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
+        <refentrytitle>b10-ddns</refentrytitle><manvolnum>8</manvolnum>
+      </citerefentry>,
+      <citerefentry>
         <refentrytitle>b10-loadzone</refentrytitle><manvolnum>8</manvolnum>
       </citerefentry>,
       <citerefentry>
diff --git a/src/bin/auth/tests/.gitignore b/src/bin/auth/tests/.gitignore
index d6d1ec8..a45eff7 100644
--- a/src/bin/auth/tests/.gitignore
+++ b/src/bin/auth/tests/.gitignore
@@ -1 +1,3 @@
 /run_unittests
+/example_base_inc.cc
+/example_nsec3_inc.cc
diff --git a/src/bin/auth/tests/Makefile.am b/src/bin/auth/tests/Makefile.am
index 42f202b..36a3c68 100644
--- a/src/bin/auth/tests/Makefile.am
+++ b/src/bin/auth/tests/Makefile.am
@@ -7,7 +7,8 @@ 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_OWN_DATA_DIR=\"$(abs_top_srcdir)/src/bin/auth/tests/testdata\"
+AM_CPPFLAGS += -DTEST_OWN_DATA_DIR=\"$(abs_srcdir)/testdata\"
+AM_CPPFLAGS += -DTEST_OWN_DATA_BUILDDIR=\"$(abs_builddir)/testdata\"
 AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/testutils/testdata\"
 AM_CPPFLAGS += -DDSRC_DIR=\"$(abs_top_builddir)/src/lib/datasrc\"
 AM_CPPFLAGS += -DPLUGIN_DATA_PATH=\"$(abs_top_builddir)/src/bin/cfgmgr/plugins\"
@@ -50,7 +51,6 @@ 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
-run_unittests_SOURCES += query_inmemory_unittest.cc
 run_unittests_SOURCES += statistics_unittest.cc
 run_unittests_SOURCES += test_datasrc_clients_mgr.h test_datasrc_clients_mgr.cc
 run_unittests_SOURCES += datasrc_clients_builder_unittest.cc
@@ -81,6 +81,40 @@ run_unittests_LDADD += $(top_builddir)/src/lib/util/threads/libb10-threads.la
 run_unittests_LDADD += $(GTEST_LDADD)
 run_unittests_LDADD += $(SQLITE_LIBS)
 
+# The following are definitions for auto-generating test data for query
+# tests.
+BUILT_SOURCES = example_base_inc.cc example_nsec3_inc.cc
+BUILT_SOURCES += testdata/example-base.sqlite3
+BUILT_SOURCES += testdata/example-nsec3.sqlite3
+
+EXTRA_DIST = gen-query-testdata.py
+
+CLEANFILES += example_base_inc.cc example_nsec3_inc.cc
+
+example_base_inc.cc: $(srcdir)/testdata/example-base-inc.zone
+	$(PYTHON) $(srcdir)/gen-query-testdata.py \
+		$(srcdir)/testdata/example-base-inc.zone example_base_inc.cc
+
+example_nsec3_inc.cc: $(srcdir)/testdata/example-nsec3-inc.zone
+	$(PYTHON) $(srcdir)/gen-query-testdata.py \
+		$(srcdir)/testdata/example-nsec3-inc.zone example_nsec3_inc.cc
+
+testdata/example-base.sqlite3: testdata/example-base.zone
+	$(top_srcdir)/install-sh -c \
+		$(srcdir)/testdata/example-common-inc-template.zone \
+		testdata/example-common-inc.zone
+	$(SHELL) $(top_builddir)/src/bin/loadzone/run_loadzone.sh \
+		-c "{\"database_file\": \"$(builddir)/testdata/example-base.sqlite3\"}" \
+		example.com testdata/example-base.zone
+
+testdata/example-nsec3.sqlite3: testdata/example-nsec3.zone
+	$(top_srcdir)/install-sh -c \
+		$(srcdir)/testdata/example-common-inc-template.zone \
+		testdata/example-common-inc.zone
+	$(SHELL) $(top_builddir)/src/bin/loadzone/run_loadzone.sh \
+		-c "{\"database_file\": \"$(builddir)/testdata/example-nsec3.sqlite3\"}" \
+		example.com testdata/example-nsec3.zone
+
 check-local:
 	B10_FROM_BUILD=${abs_top_builddir} ./run_unittests
 
diff --git a/src/bin/auth/tests/gen-query-testdata.py b/src/bin/auth/tests/gen-query-testdata.py
new file mode 100755
index 0000000..a71deb0
--- /dev/null
+++ b/src/bin/auth/tests/gen-query-testdata.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+
+# 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 is a supplemental script to auto generate test data in the form of
+C++ source code from a DNS zone file.
+
+Usage: python gen-query-testdata.py source_file output-cc-file
+
+The usage doesn't matter much, though, because it's expected to be invoked
+from Makefile, and that would be only use case of this script.
+"""
+
+import sys
+import re
+
+# Markup for variable definition
+re_start_rr = re.compile('^;var=(.*)')
+
+# Skip lines starting with ';' (comments) or empty lines.  re_start_rr
+# will also match this expression, so it should be checked first.
+re_skip = re.compile('(^;)|(^\s*$)')
+
+def parse_input(input_file):
+    '''Build an internal list of RR data from the input source file.
+
+    It generates a list of (variable_name, list of RR) tuples, where
+    variable_name is the expected C++ variable name for the subsequent RRs
+    if they are expected to be named.  It can be an empty string if the RRs
+    are only expected to appear in the zone file.
+    The second element of the tuple is a list of strings, each of which
+    represents a single RR, e.g., "example.com 3600 IN A 192.0.2.1".
+
+    '''
+    result = []
+    rrs = None
+    with open(input_file) as f:
+        for line in f:
+            m = re_start_rr.match(line)
+            if m:
+                if rrs is not None:
+                    result.append((rr_varname, rrs))
+                rrs = []
+                rr_varname = m.group(1)
+            elif re_skip.match(line):
+                continue
+            else:
+                rrs.append(line.rstrip('\n'))
+
+        # if needed, store the last RRs (they are not followed by 'var=' mark)
+        if rrs is not None:
+            result.append((rr_varname, rrs))
+
+    return result
+
+def generate_variables(out_file, rrsets_data):
+    '''Generate a C++ source file containing a C-string variables for RRs.
+
+    This produces a definition of C-string for each RRset that is expected
+    to be named as follows:
+    const char* const var_name =
+        "example.com. 3600 IN A 192.0.2.1\n"
+        "example.com. 3600 IN A 192.0.2.2\n";
+
+    Escape character '\' in the string will be further escaped so it will
+    compile.
+
+    '''
+    with open(out_file, 'w') as out:
+        for (var_name, rrs) in rrsets_data:
+            if len(var_name) > 0:
+                out.write('const char* const ' + var_name + ' =\n')
+                # Combine all RRs, escaping '\' as a C-string
+                out.write('\n'.join(['    \"%s\\n\"' %
+                                     (rr.replace('\\', '\\\\'))
+                                     for rr in rrs]))
+                out.write(';\n')
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        sys.stderr.write('gen-query-testdata.py require 2 args\n')
+        sys.exit(1)
+    rrsets_data = parse_input(sys.argv[1])
+    generate_variables(sys.argv[2], rrsets_data)
+
diff --git a/src/bin/auth/tests/query_inmemory_unittest.cc b/src/bin/auth/tests/query_inmemory_unittest.cc
deleted file mode 100644
index f587ba2..0000000
--- a/src/bin/auth/tests/query_inmemory_unittest.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <dns/name.h>
-#include <dns/message.h>
-#include <dns/rcode.h>
-#include <dns/opcode.h>
-
-#include <cc/data.h>
-
-#include <datasrc/client_list.h>
-
-#include <auth/query.h>
-
-#include <testutils/dnsmessage_test.h>
-
-#include <gtest/gtest.h>
-
-#include <string>
-
-using namespace isc::dns;
-using namespace isc::auth;
-using namespace isc::testutils;
-using isc::datasrc::ConfigurableClientList;
-using std::string;
-
-namespace {
-
-// The DNAME to do tests against
-const char* const dname_txt =
-    "dname.example.com. 3600 IN DNAME "
-    "somethinglong.dnametarget.example.com.\n";
-// This is not inside the zone, this is created at runtime
-const char* const synthetized_cname_txt =
-    "www.dname.example.com. 3600 IN CNAME "
-    "www.somethinglong.dnametarget.example.com.\n";
-
-// This is a subset of QueryTest using (subset of) the same test data, but
-// with the production in-memory data source.  Both tests should be eventually
-// unified to avoid duplicates.
-class InMemoryQueryTest : public ::testing::Test {
-protected:
-    InMemoryQueryTest() : list(RRClass::IN()), response(Message::RENDER) {
-        response.setRcode(Rcode::NOERROR());
-        response.setOpcode(Opcode::QUERY());
-        list.configure(isc::data::Element::fromJSON(
-                           "[{\"type\": \"MasterFiles\","
-                           "  \"cache-enable\": true, "
-                           "  \"params\": {\"example.com\": \"" +
-                           string(TEST_OWN_DATA_DIR "/example.zone") +
-                           "\"}}]"), true);
-    }
-
-    ConfigurableClientList list;
-    Message response;
-    Query query;
-};
-
-// A wrapper to check resulting response message commonly used in
-// tests below.
-// check_origin needs to be specified only when the authority section has
-// an SOA RR.  The interface is not generic enough but should be okay
-// for our test cases in practice.
-void
-responseCheck(Message& response, const isc::dns::Rcode& rcode,
-              unsigned int flags, const unsigned int ancount,
-              const unsigned int nscount, const unsigned int arcount,
-              const char* const expected_answer,
-              const char* const expected_authority,
-              const char* const expected_additional,
-              const Name& check_origin = Name::ROOT_NAME())
-{
-    // In our test cases QID, Opcode, and QDCOUNT should be constant, so
-    // we don't bother the test cases specifying these values.
-    headerCheck(response, response.getQid(), rcode, Opcode::QUERY().getCode(),
-                flags, 0, ancount, nscount, arcount);
-    if (expected_answer != NULL) {
-        rrsetsCheck(expected_answer,
-                    response.beginSection(Message::SECTION_ANSWER),
-                    response.endSection(Message::SECTION_ANSWER),
-                    check_origin);
-    }
-    if (expected_authority != NULL) {
-        rrsetsCheck(expected_authority,
-                    response.beginSection(Message::SECTION_AUTHORITY),
-                    response.endSection(Message::SECTION_AUTHORITY),
-                    check_origin);
-    }
-    if (expected_additional != NULL) {
-        rrsetsCheck(expected_additional,
-                    response.beginSection(Message::SECTION_ADDITIONAL),
-                    response.endSection(Message::SECTION_ADDITIONAL));
-    }
-}
-
-/*
- * Test a query under a domain with DNAME. We should get a synthetized CNAME
- * as well as the DNAME.
- *
- * TODO: Once we have CNAME chaining, check it works with synthetized CNAMEs
- * as well. This includes tests pointing inside the zone, outside the zone,
- * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
- */
-TEST_F(InMemoryQueryTest, DNAME) {
-    query.process(list, 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(),
-        NULL, NULL);
-}
-}
diff --git a/src/bin/auth/tests/query_unittest.cc b/src/bin/auth/tests/query_unittest.cc
index 84b7f8a..a22d2d7 100644
--- a/src/bin/auth/tests/query_unittest.cc
+++ b/src/bin/auth/tests/query_unittest.cc
@@ -12,10 +12,6 @@
 // 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 <boost/bind.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/static_assert.hpp>
@@ -24,9 +20,12 @@
 
 #include <dns/masterload.h>
 #include <dns/message.h>
+#include <dns/master_loader.h>
 #include <dns/name.h>
+#include <dns/nsec3hash.h>
 #include <dns/opcode.h>
 #include <dns/rcode.h>
+#include <dns/rrcollator.h>
 #include <dns/rrttl.h>
 #include <dns/rrtype.h>
 #include <dns/rdataclass.h>
@@ -40,6 +39,12 @@
 
 #include <gtest/gtest.h>
 
+#include <cstdlib>
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <vector>
+
 using namespace std;
 using namespace isc::dns;
 using namespace isc::dns::rdata;
@@ -49,7 +54,7 @@ using namespace isc::testutils;
 
 namespace {
 
-// Simple wrapper for a sincle data source client.
+// Simple wrapper for a single data source client.
 // The list simply delegates all the answers to the single
 // client.
 class SingletonList : public ClientList {
@@ -79,150 +84,21 @@ private:
     DataSourceClient& client_;
 };
 
+// These are commonly used data (auto-generated).  There are some exceptional
+// data that are only used in a limited scenario, which are defined separately
+// below.
+#include <auth/tests/example_base_inc.cc>
+#include <auth/tests/example_nsec3_inc.cc>
 
-// This is the content of the mock zone (see below).
-// It's a sequence of textual RRs that is supposed to be parsed by
-// dns::masterLoad().  Some of the RRs are also used as the expected
-// data in specific tests, in which case they are referenced via specific
-// local variables (such as soa_txt).
-//
-// For readability consistency, all strings are placed in a separate line,
-// even if they are very short and can reasonably fit in a single line with
-// the corresponding variable.  For example, we write
-// const char* const foo_txt =
-//  "foo.example.com. 3600 IN AAAA 2001:db8::1\n";
-// instead of
-// const char* const foo_txt = "foo.example.com. 3600 IN AAAA 2001:db8::1\n";
-const char* const soa_txt =
-    "example.com. 3600 IN SOA . . 0 0 0 0 0\n";
-const char* const zone_ns_txt =
-    "example.com. 3600 IN NS glue.delegation.example.com.\n"
-    "example.com. 3600 IN NS noglue.example.com.\n"
-    "example.com. 3600 IN NS example.net.\n";
+// This is used only in one pathological test case.
 const char* const zone_ds_txt =
     "example.com. 3600 IN DS 57855 5 1 "
         "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
-const char* const ns_addrs_txt =
-    "glue.delegation.example.com. 3600 IN A 192.0.2.153\n"
-    "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n"
-    "noglue.example.com. 3600 IN A 192.0.2.53\n";
-const char* const delegation_txt =
-    "delegation.example.com. 3600 IN NS glue.delegation.example.com.\n"
-    "delegation.example.com. 3600 IN NS noglue.example.com.\n"
-    "delegation.example.com. 3600 IN NS cname.example.com.\n"
-    "delegation.example.com. 3600 IN NS example.org.\n";
-// Borrowed from the RFC4035
-const char* const delegation_ds_txt =
-    "delegation.example.com. 3600 IN DS 57855 5 1 "
-        "B6DCD485719ADCA18E5F3D48A2331627FDD3 636B\n";
-const char* const mx_txt =
-    "mx.example.com. 3600 IN MX 10 www.example.com.\n"
-    "mx.example.com. 3600 IN MX 20 mailer.example.org.\n"
-    "mx.example.com. 3600 IN MX 30 mx.delegation.example.com.\n";
-const char* const www_a_txt =
-    "www.example.com. 3600 IN A 192.0.2.80\n";
-const char* const cname_txt =
-    "cname.example.com. 3600 IN CNAME www.example.com.\n";
-const char* const cname_nxdom_txt =
-    "cnamenxdom.example.com. 3600 IN CNAME nxdomain.example.com.\n";
-// CNAME Leading out of zone
-const char* const cname_out_txt =
-    "cnameout.example.com. 3600 IN CNAME www.example.org.\n";
-// The DNAME to do tests against
-const char* const dname_txt =
-    "dname.example.com. 3600 IN DNAME "
-    "somethinglong.dnametarget.example.com.\n";
-// Some data at the dname node (allowed by RFC 2672)
-const char* const dname_a_txt =
-    "dname.example.com. 3600 IN A 192.0.2.5\n";
+
 // This is not inside the zone, this is created at runtime
 const char* const synthetized_cname_txt =
     "www.dname.example.com. 3600 IN CNAME "
     "www.somethinglong.dnametarget.example.com.\n";
-// The rest of data won't be referenced from the test cases.
-const char* const other_zone_rrs =
-    "cnamemailer.example.com. 3600 IN CNAME www.example.com.\n"
-    "cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.\n"
-    "mx.delegation.example.com. 3600 IN A 192.0.2.100\n";
-// Wildcards
-const char* const wild_txt =
-    "*.wild.example.com. 3600 IN A 192.0.2.7\n";
-const char* const nsec_wild_txt =
-    "*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG\n";
-const char* const cnamewild_txt =
-    "*.cnamewild.example.com. 3600 IN CNAME www.example.org.\n";
-const char* const nsec_cnamewild_txt =
-    "*.cnamewild.example.com. 3600 IN NSEC "
-    "delegation.example.com. CNAME NSEC RRSIG\n";
-// Wildcard_nxrrset
-const char* const wild_txt_nxrrset =
-    "*.uwild.example.com. 3600 IN A 192.0.2.9\n";
-const char* const nsec_wild_txt_nxrrset =
-    "*.uwild.example.com. 3600 IN NSEC www.uwild.example.com. A NSEC RRSIG\n";
-const char* const wild_txt_next =
-    "www.uwild.example.com. 3600 IN A 192.0.2.11\n";
-const char* const nsec_wild_txt_next =
-    "www.uwild.example.com. 3600 IN NSEC *.wild.example.com. A NSEC RRSIG\n";
-// Wildcard empty
-const char* const empty_txt =
-    "b.*.t.example.com. 3600 IN A 192.0.2.13\n";
-const char* const nsec_empty_txt =
-    "b.*.t.example.com. 3600 IN NSEC *.uwild.example.com. A NSEC RRSIG\n";
-const char* const empty_prev_txt =
-    "t.example.com. 3600 IN A 192.0.2.15\n";
-const char* const nsec_empty_prev_txt =
-    "t.example.com. 3600 IN NSEC b.*.t.example.com. A NSEC RRSIG\n";
-// Used in NXDOMAIN proof test.  We are going to test some unusual case where
-// the best possible wildcard is below the "next domain" of the NSEC RR that
-// proves the NXDOMAIN, i.e.,
-// mx.example.com. (exist)
-// (.no.example.com. (qname, NXDOMAIN)
-// ).no.example.com. (exist)
-// *.no.example.com. (best possible wildcard, not exist)
-const char* const no_txt =
-    ").no.example.com. 3600 IN AAAA 2001:db8::53\n";
-// NSEC records.
-const char* const nsec_apex_txt =
-    "example.com. 3600 IN NSEC cname.example.com. NS SOA NSEC RRSIG\n";
-const char* const nsec_mx_txt =
-    "mx.example.com. 3600 IN NSEC ).no.example.com. MX NSEC RRSIG\n";
-const char* const nsec_no_txt =
-    ").no.example.com. 3600 IN NSEC nz.no.example.com. AAAA NSEC RRSIG\n";
-// We'll also test the case where a single NSEC proves both NXDOMAIN and the
-// non existence of wildcard.  The following records will be used for that
-// test.
-// ).no.example.com. (exist, whose NSEC proves everything)
-// *.no.example.com. (best possible wildcard, not exist)
-// nx.no.example.com. (NXDOMAIN)
-// nz.no.example.com. (exist)
-const char* const nz_txt =
-    "nz.no.example.com. 3600 IN AAAA 2001:db8::5300\n";
-const char* const nsec_nz_txt =
-    "nz.no.example.com. 3600 IN NSEC noglue.example.com. AAAA NSEC RRSIG\n";
-const char* const nsec_nxdomain_txt =
-    "noglue.example.com. 3600 IN NSEC nonsec.example.com. A\n";
-
-// NSEC for the normal NXRRSET case
-const char* const nsec_www_txt =
-    "www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG\n";
-
-// Authoritative data without NSEC
-const char* const nonsec_a_txt =
-    "nonsec.example.com. 3600 IN A 192.0.2.0\n";
-
-// NSEC3 RRs.  You may also need to add mapping to MockZoneFinder::hash_map_.
-const string nsec3_apex_txt =
-    "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 "
-    "aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG\n";
-const string nsec3_apex_rrsig_txt =
-    "0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG NSEC3 5 3 "
-    "3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE";
-const string nsec3_www_txt =
-    "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 "
-    "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
-const string nsec3_www_rrsig_txt =
-    "q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG NSEC3 5 3 "
-    "3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE";
 
 // NSEC3 for wild.example.com (used in wildcard tests, will be added on
 // demand not to confuse other tests)
@@ -246,42 +122,13 @@ const char* const nsec3_uwild_txt =
     "t644ebqk9bibcna874givr6joj62mlhv.example.com. 3600 IN NSEC3 1 1 12 "
     "aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG\n";
 
-// (Secure) delegation data; Delegation with DS record
-const char* const signed_delegation_txt =
-    "signed-delegation.example.com. 3600 IN NS ns.example.net.\n";
-const char* const signed_delegation_ds_txt =
-    "signed-delegation.example.com. 3600 IN DS 12345 8 2 "
-    "764501411DE58E8618945054A3F620B36202E115D015A7773F4B78E0F952CECA\n";
-
 // (Secure) delegation data; Delegation without DS record (and both NSEC
 // and NSEC3 denying its existence)
-const char* const unsigned_delegation_txt =
-    "unsigned-delegation.example.com. 3600 IN NS ns.example.net.\n";
-const char* const unsigned_delegation_nsec_txt =
-    "unsigned-delegation.example.com. 3600 IN NSEC "
-    "unsigned-delegation-optout.example.com. NS RRSIG NSEC\n";
 // This one will be added on demand
 const char* const unsigned_delegation_nsec3_txt =
     "q81r598950igr1eqvc60aedlq66425b5.example.com. 3600 IN NSEC3 1 1 12 "
     "aabbccdd 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom NS RRSIG\n";
 
-// Delegation without DS record, and no direct matching NSEC3 record
-const char* const unsigned_delegation_optout_txt =
-    "unsigned-delegation-optout.example.com. 3600 IN NS ns.example.net.\n";
-const char* const unsigned_delegation_optout_nsec_txt =
-    "unsigned-delegation-optout.example.com. 3600 IN NSEC "
-    "*.uwild.example.com. NS RRSIG NSEC\n";
-
-// (Secure) delegation data; Delegation where the DS lookup will raise an
-// exception.
-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
 // sense in terms of the DNSSEC protocol, but for our testing purposes it's
@@ -314,58 +161,14 @@ textToRRset(const string& text_rrset, const Name& origin = Name::ROOT_NAME()) {
     return (rrset);
 }
 
-// This is a mock Zone Finder class for testing.
-// It is a derived class of ZoneFinder for the convenient of tests.
-// Its find() method emulates the common behavior of protocol compliant
-// ZoneFinder classes, but simplifies some minor cases and also supports broken
-// behavior.
-// For simplicity, most names are assumed to be "in zone"; delegations
-// to child zones are identified by the existence of non origin NS records.
-// Another special name is "dname.example.com".  Query names under this name
-// will result in DNAME.
-// This mock zone doesn't handle empty non terminal nodes (if we need to test
-// such cases find() should have specialized code for it).
-class MockZoneFinder : public ZoneFinder {
+// Setup for faked NSEC3 hash used throughout this test.
+class TestNSEC3Hash : public NSEC3Hash {
+private:
+    typedef map<Name, string> NSEC3HashMap;
+    typedef NSEC3HashMap::value_type NSEC3HashPair;
+    NSEC3HashMap hash_map_;
 public:
-    MockZoneFinder() :
-        origin_(Name("example.com")),
-        bad_signed_delegation_name_("bad-delegation.example.com"),
-        dname_name_("dname.example.com"),
-        has_SOA_(true),
-        has_apex_NS_(true),
-        rrclass_(RRClass::IN()),
-        include_rrsig_anyway_(false),
-        use_nsec3_(false),
-        nsec_name_(origin_),
-        nsec3_fake_(NULL),
-        nsec3_name_(NULL)
-    {
-        stringstream zone_stream;
-        zone_stream << soa_txt << zone_ns_txt << ns_addrs_txt <<
-            delegation_txt << delegation_ds_txt << mx_txt << www_a_txt <<
-            cname_txt << cname_nxdom_txt << cname_out_txt << dname_txt <<
-            dname_a_txt << other_zone_rrs << no_txt << nz_txt <<
-            nsec_apex_txt << nsec_mx_txt << nsec_no_txt << nsec_nz_txt <<
-            nsec_nxdomain_txt << nsec_www_txt << nonsec_a_txt <<
-            wild_txt << nsec_wild_txt << cnamewild_txt << nsec_cnamewild_txt <<
-            wild_txt_nxrrset << nsec_wild_txt_nxrrset << wild_txt_next <<
-            nsec_wild_txt_next << empty_txt << nsec_empty_txt <<
-            empty_prev_txt << nsec_empty_prev_txt <<
-            nsec3_apex_txt << nsec3_www_txt <<
-            signed_delegation_txt << signed_delegation_ds_txt <<
-            unsigned_delegation_txt << unsigned_delegation_nsec_txt <<
-            unsigned_delegation_optout_txt <<
-            unsigned_delegation_optout_nsec_txt <<
-            bad_delegation_txt << nosec_delegation_txt;
-
-        masterLoad(zone_stream, origin_, rrclass_,
-                   boost::bind(&MockZoneFinder::loadRRset, this, _1));
-
-        empty_nsec_rrset_ = ConstRRsetPtr(new RRset(Name::ROOT_NAME(),
-                                                    RRClass::IN(),
-                                                    RRType::NSEC(),
-                                                    RRTTL(3600)));
-
+    TestNSEC3Hash() {
         // (Faked) NSEC3 hash map.  For convenience we use hardcoded built-in
         // map instead of calculating and using actual hash.
         // The used hash values are borrowed from RFC5155 examples (they are
@@ -411,6 +214,79 @@ public:
         hash_map_[Name("www1.uwild.example.com")] =
             "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
     }
+    virtual string calculate(const Name& name) const {
+        const NSEC3HashMap::const_iterator found = hash_map_.find(name);
+        if (found != hash_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);
+    }
+};
+
+class TestNSEC3HashCreator : public isc::dns::NSEC3HashCreator {
+public:
+    TestNSEC3HashCreator() {}
+    virtual isc::dns::NSEC3Hash*
+    create(const isc::dns::rdata::generic::NSEC3PARAM&) const {
+        return (new TestNSEC3Hash);
+    }
+
+    virtual isc::dns::NSEC3Hash*
+    create(const isc::dns::rdata::generic::NSEC3&) const {
+        return (new TestNSEC3Hash);
+    }
+
+    virtual isc::dns::NSEC3Hash*
+    create(uint8_t, uint16_t, const uint8_t*, size_t) const {
+        return (new TestNSEC3Hash);
+    }
+};
+
+// This is a mock Zone Finder class for testing.
+// It is a derived class of ZoneFinder for the convenient of tests.
+// Its find() method emulates the common behavior of protocol compliant
+// ZoneFinder classes, but simplifies some minor cases and also supports broken
+// behavior.
+// For simplicity, most names are assumed to be "in zone"; delegations
+// to child zones are identified by the existence of non origin NS records.
+// Another special name is "dname.example.com".  Query names under this name
+// will result in DNAME.
+// This mock zone doesn't handle empty non terminal nodes (if we need to test
+// such cases find() should have specialized code for it).
+class MockZoneFinder : public ZoneFinder {
+public:
+    MockZoneFinder() :
+        origin_(Name("example.com")),
+        bad_signed_delegation_name_("bad-delegation.example.com"),
+        dname_name_("dname.example.com"),
+        has_SOA_(true),
+        has_apex_NS_(true),
+        rrclass_(RRClass::IN()),
+        include_rrsig_anyway_(false),
+        use_nsec3_(false),
+        nsec_name_(origin_),
+        nsec3_fake_(NULL),
+        nsec3_name_(NULL)
+    {
+        RRCollator collator(boost::bind(&MockZoneFinder::loadRRset, this, _1));
+        MasterLoader loader(TEST_OWN_DATA_BUILDDIR "/example-nsec3.zone",
+                            origin_, rrclass_,
+                            MasterLoaderCallbacks::getNullCallbacks(),
+                            collator.getCallback());
+        loader.load();
+
+        empty_nsec_rrset_ = ConstRRsetPtr(new RRset(Name::ROOT_NAME(),
+                                                    RRClass::IN(),
+                                                    RRType::NSEC(),
+                                                    RRTTL(3600)));
+    }
     virtual isc::dns::Name getOrigin() const { return (origin_); }
     virtual isc::dns::RRClass getClass() const { return (rrclass_); }
     virtual ZoneFinderContextPtr find(const isc::dns::Name& name,
@@ -513,6 +389,19 @@ private:
     };
 
     void loadRRset(RRsetPtr rrset) {
+        // For simplicity we dynamically generate RRSIGs and add them below.
+        // The RRSIG RDATA should be consistent with that defined in the
+        // zone file.
+        if (rrset->getType() == RRType::RRSIG()) {
+            return;
+        }
+
+        // NSEC3PARAM is not used in the mock data source (and it would confuse
+        // non-NSEC3 test cases).
+        if (rrset->getType() == RRType::NSEC3PARAM()) {
+            return;
+        }
+
         if (rrset->getType() == RRType::NSEC3()) {
             // NSEC3 should go to the dedicated table
             nsec3_domains_[rrset->getName()][rrset->getType()] = rrset;
@@ -565,9 +454,7 @@ private:
     // Enabled when not NULL
     const FindNSEC3Result* nsec3_fake_;
     const Name* nsec3_name_;
-public:
-    // Public, to allow tests looking up the right names for something
-    map<Name, string> hash_map_;
+    TestNSEC3Hash nsec3_hash_;
 };
 
 // A helper function that generates a new RRset based on "wild_rrset",
@@ -627,11 +514,7 @@ MockZoneFinder::findNSEC3(const Name& name, bool recursive) {
     // For brevity, we assume several things below: maps should have an
     // expected entry when operator[] is used; maps are not empty.
     for (int i = 0; i < labels; ++i) {
-        const string hlabel = hash_map_[name.split(i, labels - i)];
-        if (hlabel.empty()) {
-            isc_throw(isc::Unexpected, "findNSEC3() hash failure for " <<
-                      name.split(i, labels - i));
-        }
+        const string hlabel = nsec3_hash_.calculate(name.split(i, labels - i));
         const Name hname = Name(hlabel + ".example.com");
         // We don't use const_iterator so that we can use operator[] below
         Domains::iterator found_domain = nsec3_domains_.lower_bound(hname);
@@ -893,10 +776,55 @@ MockZoneFinder::find(const Name& name, const RRType& type,
     return (createContext(options,NXDOMAIN, RRsetPtr()));
 }
 
-class QueryTest : public ::testing::Test {
+enum DataSrcType {
+    MOCK,
+    INMEMORY,
+    SQLITE3
+};
+
+boost::shared_ptr<ClientList>
+createDataSrcClientList(DataSrcType type, DataSourceClient& client) {
+    boost::shared_ptr<ConfigurableClientList> list;
+    switch (type) {
+    case MOCK:
+        return (boost::shared_ptr<ClientList>(new SingletonList(client)));
+    case INMEMORY:
+        list.reset(new ConfigurableClientList(RRClass::IN()));
+        list->configure(isc::data::Element::fromJSON(
+                            "[{\"type\": \"MasterFiles\","
+                            "  \"cache-enable\": true, "
+                            "  \"params\": {\"example.com\": \"" +
+                            string(TEST_OWN_DATA_BUILDDIR "/example.zone") +
+                            "\"}}]"), true);
+        return (list);
+    case SQLITE3:
+        // The copy should succeed; if it failed we should notice it in
+        // test cases.  However, we check the return value to avoid problems
+        // in some glibcs where "system()" is annotated with the "warn unused
+        // result" attribute.
+        EXPECT_EQ(0, std::system(INSTALL_PROG " -c " TEST_OWN_DATA_BUILDDIR
+                                 "/example-base.sqlite3 "
+                                 TEST_OWN_DATA_BUILDDIR
+                                 "/example-base.sqlite3.copied"));
+        list.reset(new ConfigurableClientList(RRClass::IN()));
+        list->configure(isc::data::Element::fromJSON(
+                            "[{\"type\": \"sqlite3\","
+                            "  \"cache-enable\": false, "
+                            "  \"cache-zones\": [], "
+                            "  \"params\": {\"database_file\": \"" +
+                            string(TEST_OWN_DATA_BUILDDIR
+                                   "/example-base.sqlite3.copied") +
+                            "\"}}]"), true);
+         return (list);
+    default:
+        isc_throw(isc::Unexpected,
+                  "Unexpected data source type, should be a bug in test code");
+    }
+}
+
+class QueryTest : public ::testing::TestWithParam<DataSrcType> {
 protected:
     QueryTest() :
-        list(memory_client),
         qname(Name("www.example.com")), qclass(RRClass::IN()),
         qtype(RRType::A()), response(Message::RENDER),
         qid(response.getQid()), query_code(Opcode::QUERY().getCode()),
@@ -906,22 +834,166 @@ protected:
                              "glue.delegation.example.com. 3600 IN RRSIG " +
                              getCommonRRSIGText("AAAA") + "\n" +
                              "noglue.example.com. 3600 IN RRSIG " +
-                             getCommonRRSIGText("A"))
+                             getCommonRRSIGText("A")),
+        base_zone_file(TEST_OWN_DATA_BUILDDIR "/example-base.zone"),
+        nsec3_zone_file(TEST_OWN_DATA_BUILDDIR "/example-nsec3.zone"),
+        common_zone_file(TEST_OWN_DATA_BUILDDIR "/example-common-inc.zone"),
+        rrsets_added_(false)
     {
+        // Set up the faked hash calculator.
+        setNSEC3HashCreator(&nsec3hash_creator_);
+
         response.setRcode(Rcode::NOERROR());
         response.setOpcode(Opcode::QUERY());
         // create and add a matching zone.
         mock_finder = new MockZoneFinder();
         memory_client.addZone(ZoneFinderPtr(mock_finder));
     }
+
+    virtual void SetUp() {
+        // clear the commonly included zone file.
+        ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_OWN_DATA_DIR
+                                 "/example-common-inc-template.zone "
+                                 TEST_OWN_DATA_BUILDDIR
+                                 "/example-common-inc.zone"));
+
+        // We create data source clients here, not in the constructor, so this
+        // doesn't happen for derived test class.  This also ensures the
+        // data source clients are configured after setting NSEC3 hash in case
+        // there's dependency.
+        list_ = createDataSrcClientList(GetParam(), memory_client);
+    }
+
+    virtual void TearDown() {
+        // make sure to clear the commonly included zone file to prevent
+        // any remaining contents from affecting the next test.
+        ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_OWN_DATA_DIR
+                                 "/example-common-inc-template.zone "
+                                 TEST_OWN_DATA_BUILDDIR
+                                 "/example-common-inc.zone"));
+    }
+
+    virtual ~QueryTest() {
+        // Make sure we reset the hash creator to the default
+        setNSEC3HashCreator(NULL);
+    }
+
+    void enableNSEC3(const vector<string>& rrsets_to_add) {
+        boost::shared_ptr<ConfigurableClientList> new_list;
+        switch (GetParam()) {
+        case MOCK:
+            mock_finder->setNSEC3Flag(true);
+            addRRsets(rrsets_to_add, *list_, "");
+            break;
+        case INMEMORY:
+            addRRsets(rrsets_to_add, *list_, nsec3_zone_file);
+            break;
+        case SQLITE3:
+            ASSERT_EQ(0, std::system(INSTALL_PROG " -c " TEST_OWN_DATA_BUILDDIR
+                                     "/example-nsec3.sqlite3 "
+                                     TEST_OWN_DATA_BUILDDIR
+                                     "/example-nsec3.sqlite3.copied"));
+            new_list.reset(new ConfigurableClientList(RRClass::IN()));
+            new_list->configure(isc::data::Element::fromJSON(
+                                    "[{\"type\": \"sqlite3\","
+                                    "  \"cache-enable\": false, "
+                                    "  \"cache-zones\": [], "
+                                    "  \"params\": {\"database_file\": \"" +
+                                    string(TEST_OWN_DATA_BUILDDIR
+                                           "/example-nsec3.sqlite3.copied") +
+                                    "\"}}]"), true);
+            addRRsets(rrsets_to_add, *new_list, "");
+            list_ = new_list;
+            break;
+        }
+    }
+
+    // A helper to add some RRsets to the test zone in the middle of a test
+    // case.  The detailed behavior is quite different depending on the
+    // data source type, and not all parameters are used in all cases.
+    //
+    // Note: due to limitation of its implementation, this method doesn't
+    // work correctly for in-memory if called more than once.  This condition
+    // is explicitly checked so any accidental violation would be noted as a
+    // test failure.
+    void addRRsets(const vector<string>& rrsets_to_add, ClientList& list,
+                   const string& zone_file)
+    {
+        boost::shared_ptr<ConfigurableClientList> new_list;
+        ofstream ofs;
+
+        switch (GetParam()) {
+        case MOCK:
+            // directly add them to the mock data source; ignore the passed
+            // list.
+            for (vector<string>::const_iterator it = rrsets_to_add.begin();
+                 it != rrsets_to_add.end();
+                 ++it) {
+                mock_finder->addRecord(*it);
+            }
+            break;
+        case INMEMORY:
+            ASSERT_FALSE(rrsets_added_);
+            rrsets_added_ = true;
+
+            // dump the RRsets to be added to the placeholder of commonly
+            // included zone file (removing any existing contents) and do
+            // full reconfiguration.
+            ofs.open(common_zone_file.c_str(), ios_base::trunc);
+            for (vector<string>::const_iterator it = rrsets_to_add.begin();
+                 it != rrsets_to_add.end();
+                 ++it) {
+                ofs << *it << "\n";
+                ofs << createRRSIG(textToRRset(*it))->toText() << "\n";
+            }
+            ofs.close();
+
+            new_list.reset(new ConfigurableClientList(RRClass::IN()));
+            new_list->configure(isc::data::Element::fromJSON(
+                                    "[{\"type\": \"MasterFiles\","
+                                    "  \"cache-enable\": true, "
+                                    "  \"params\": {\"example.com\": \"" +
+                                    zone_file + "\"}}]"), true);
+            list_ = new_list;
+            break;
+        case SQLITE3:
+            const Name origin("example.com");
+            ZoneUpdaterPtr updater =
+                list.find(origin, true, false).dsrc_client_->
+                getUpdater(origin, false);
+            for (vector<string>::const_iterator it = rrsets_to_add.begin();
+                 it != rrsets_to_add.end();
+                 ++it) {
+                ConstRRsetPtr rrset = textToRRset(*it);
+                updater->addRRset(*rrset);
+                updater->addRRset(*createRRSIG(rrset));
+            }
+            updater->commit();
+            break;
+        }
+    }
+
+private:
+    // A helper for enableNSEC3, creating an RRSIG RRset for the corresponding
+    // non-sig RRset, using the commonly used parameters.
+    static ConstRRsetPtr createRRSIG(ConstRRsetPtr rrset) {
+        RRsetPtr sig_rrset(new RRset(rrset->getName(), rrset->getClass(),
+                                     RRType::RRSIG(), rrset->getTTL()));
+        sig_rrset->addRdata(generic::RRSIG(
+                                getCommonRRSIGText(rrset->getType().
+                                                   toText())));
+        return (sig_rrset);
+    }
+
+protected:
     MockZoneFinder* mock_finder;
     // We use InMemoryClient here. We could have some kind of mock client
     // here, but historically, the Query supported only InMemoryClient
     // (originally named MemoryDataSrc) and was tested with it, so we keep
     // it like this for now.
     InMemoryClient memory_client;
-    // A wrapper client list to wrap the single data source.
-    SingletonList list;
+
+    boost::shared_ptr<ClientList> list_;
     const Name qname;
     const RRClass qclass;
     const RRType qtype;
@@ -930,6 +1002,37 @@ protected:
     const uint16_t query_code;
     const string ns_addrs_and_sig_txt; // convenient shortcut
     Query query;
+    TestNSEC3Hash nsec3_hash_;
+    vector<string> rrsets_to_add_;
+    const string base_zone_file;
+private:
+    const string nsec3_zone_file;
+    const string common_zone_file;
+    const TestNSEC3HashCreator nsec3hash_creator_;
+    bool rrsets_added_;
+};
+
+// We test the in-memory and SQLite3 data source implementations.  SQLite3
+// will require a loadable module, which doesn't work with static link for
+// all platforms.
+INSTANTIATE_TEST_CASE_P(, QueryTest,
+                        ::testing::Values(MOCK, INMEMORY
+#ifndef USE_STATIC_LINK
+                                          , SQLITE3
+#endif
+                            ));
+
+// This inherit the QueryTest cases except for the parameterized setup;
+// it's intended to be used selected test cases that only work for mock
+// data sources either because of some limitation or because of the type of
+// tests (relying on a "broken" data source behavior that can't happen with
+// production data source implementations).
+class QueryTestForMockOnly : public QueryTest {
+protected:
+    // Override SetUp() to avoid parameterized setup
+    virtual void SetUp() {
+        list_ = createDataSrcClientList(MOCK, memory_client);
+    }
 };
 
 // A wrapper to check resulting response message commonly used in
@@ -969,7 +1072,7 @@ responseCheck(Message& response, const isc::dns::Rcode& rcode,
     }
 }
 
-TEST_F(QueryTest, noZone) {
+TEST_P(QueryTest, noZone) {
     // There's no zone in the memory datasource.  So the response should have
     // REFUSED.
     InMemoryClient empty_memory_client;
@@ -979,15 +1082,15 @@ TEST_F(QueryTest, noZone) {
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
-TEST_F(QueryTest, exactMatch) {
-    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
+TEST_P(QueryTest, exactMatch) {
+    EXPECT_NO_THROW(query.process(*list_, 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(list, qname, qtype, response));
+TEST_P(QueryTest, exactMatchMultipleQueries) {
+    EXPECT_NO_THROW(query.process(*list_, qname, qtype, response));
     // find match rrset
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
                   www_a_txt, zone_ns_txt, ns_addrs_txt);
@@ -996,27 +1099,26 @@ TEST_F(QueryTest, exactMatchMultipleQueries) {
     response.clear(isc::dns::Message::RENDER);
     response.setRcode(Rcode::NOERROR());
     response.setOpcode(Opcode::QUERY());
-    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
+    EXPECT_NO_THROW(query.process(*list_, 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);
 }
 
-TEST_F(QueryTest, exactMatchIgnoreSIG) {
+TEST_P(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);
-    EXPECT_NO_THROW(query.process(list, qname, qtype, response));
+    EXPECT_NO_THROW(query.process(*list_, 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, dnssecPositive) {
+TEST_P(QueryTest, dnssecPositive) {
     // Just like exactMatch, but the signatures should be included as well
-    EXPECT_NO_THROW(query.process(list, qname, qtype, response,
-                                  true));
+    EXPECT_NO_THROW(query.process(*list_, 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 "
@@ -1031,10 +1133,10 @@ TEST_F(QueryTest, dnssecPositive) {
                   ns_addrs_and_sig_txt.c_str());
 }
 
-TEST_F(QueryTest, exactAddrMatch) {
+TEST_P(QueryTest, exactAddrMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(query.process(list,
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("noglue.example.com"),
                                   qtype, response));
 
@@ -1044,10 +1146,10 @@ TEST_F(QueryTest, exactAddrMatch) {
                   "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
 }
 
-TEST_F(QueryTest, apexNSMatch) {
+TEST_P(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.process(list, Name("example.com"),
+    EXPECT_NO_THROW(query.process(*list_, Name("example.com"),
                                   RRType::NS(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 0, 3,
@@ -1055,10 +1157,16 @@ TEST_F(QueryTest, apexNSMatch) {
 }
 
 // test type any query logic
-TEST_F(QueryTest, exactAnyMatch) {
+TEST_P(QueryTest, exactAnyMatch) {
+    // This is an in-memory specific bug (#2585), until it's fixed we
+    // tentatively skip the test for in-memory
+    if (GetParam() == INMEMORY) {
+        return;
+    }
+
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(query.process(list, Name("noglue.example.com"),
+    EXPECT_NO_THROW(query.process(*list_, Name("noglue.example.com"),
                                   RRType::ANY(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 3, 2,
@@ -1069,10 +1177,10 @@ TEST_F(QueryTest, exactAnyMatch) {
                   "glue.delegation.example.com. 3600 IN AAAA 2001:db8::53\n");
 }
 
-TEST_F(QueryTest, apexAnyMatch) {
+TEST_P(QueryTest, apexAnyMatch) {
     // find match rrset, omit additional data which has already been provided
     // in the answer section from the additional.
-    EXPECT_NO_THROW(query.process(list, Name("example.com"),
+    EXPECT_NO_THROW(query.process(*list_, Name("example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 5, 0, 3,
                   (string(soa_txt) + string(zone_ns_txt) +
@@ -1080,23 +1188,23 @@ TEST_F(QueryTest, apexAnyMatch) {
                   NULL, ns_addrs_txt, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, mxANYMatch) {
-    EXPECT_NO_THROW(query.process(list, Name("mx.example.com"),
+TEST_P(QueryTest, mxANYMatch) {
+    EXPECT_NO_THROW(query.process(*list_, 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.process(list, Name("delegation.example.com"),
+TEST_P(QueryTest, glueANYMatch) {
+    EXPECT_NO_THROW(query.process(*list_, 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.process(list, Name("nxdomain.example.com"),
+TEST_P(QueryTest, nodomainANY) {
+    EXPECT_NO_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                   RRType::ANY(), response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
@@ -1105,17 +1213,20 @@ TEST_F(QueryTest, nodomainANY) {
 // This tests that when we need to look up Zone's apex NS records for
 // authoritative answer, and there is no apex NS records. It should
 // throw in that case.
-TEST_F(QueryTest, noApexNS) {
+//
+// This only works with mock data source (for production datasrc the
+// post-load would reject such a zone)
+TEST_F(QueryTestForMockOnly, noApexNS) {
     // Disable apex NS record
     mock_finder->setApexNSFlag(false);
 
-    EXPECT_THROW(query.process(list, Name("noglue.example.com"), qtype,
+    EXPECT_THROW(query.process(*list_, 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.process(list,
+TEST_P(QueryTest, delegation) {
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("delegation.example.com"),
                                   qtype, response));
 
@@ -1123,19 +1234,19 @@ TEST_F(QueryTest, delegation) {
                   NULL, delegation_txt, ns_addrs_txt);
 }
 
-TEST_F(QueryTest, delegationWithDNSSEC) {
+TEST_P(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(list, Name("www.nosec-delegation.example.com"),
+    query.process(*list_, 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.process(list,
+TEST_P(QueryTest, secureDelegation) {
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("foo.signed-delegation.example.com"),
                                   qtype, response, true));
 
@@ -1149,8 +1260,8 @@ TEST_F(QueryTest, secureDelegation) {
                   NULL);
 }
 
-TEST_F(QueryTest, secureUnsignedDelegation) {
-    EXPECT_NO_THROW(query.process(list,
+TEST_P(QueryTest, secureUnsignedDelegation) {
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("foo.unsigned-delegation.example.com"),
                                   qtype, response, true));
 
@@ -1164,33 +1275,33 @@ TEST_F(QueryTest, secureUnsignedDelegation) {
                   NULL);
 }
 
-TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3) {
+TEST_P(QueryTest, secureUnsignedDelegationWithNSEC3) {
     // Similar to the previous case, but the zone is signed with NSEC3,
     // and this delegation is NOT an optout.
-    const Name insecurechild_name("unsigned-delegation.example.com");
-    mock_finder->setNSEC3Flag(true);
-    mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+    rrsets_to_add_.push_back(unsigned_delegation_nsec3_txt);
+    enableNSEC3(rrsets_to_add_);
 
-    query.process(list,
+    query.process(*list_,
                   Name("foo.unsigned-delegation.example.com"),
                   qtype, response, true);
 
     // The response should contain the NS and matching NSEC3 with its RRSIG
+    const Name insecurechild_name("unsigned-delegation.example.com");
     responseCheck(response, Rcode::NOERROR(), 0, 0, 3, 0,
                   NULL,
                   (string(unsigned_delegation_txt) +
                    string(unsigned_delegation_nsec3_txt) +
-                   mock_finder->hash_map_[insecurechild_name] +
+                   nsec3_hash_.calculate(insecurechild_name) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3")).c_str(),
                   NULL);
 }
 
-TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
+TEST_P(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
     // Similar to the previous case, but the delegation is an optout.
-    mock_finder->setNSEC3Flag(true);
+    enableNSEC3(rrsets_to_add_);
 
-    query.process(list,
+    query.process(*list_,
                   Name("foo.unsigned-delegation.example.com"),
                   qtype, response, true);
 
@@ -1202,44 +1313,46 @@ TEST_F(QueryTest, secureUnsignedDelegationWithNSEC3OptOut) {
                   NULL,
                   (string(unsigned_delegation_txt) +
                    string(nsec3_apex_txt) +
-                   mock_finder->hash_map_[mock_finder->getOrigin()] +
+                   nsec3_hash_.calculate(mock_finder->getOrigin()) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n" +
                    string(nsec3_www_txt) +
-                   mock_finder->hash_map_[Name("www.example.com")] +
+                   nsec3_hash_.calculate(Name("www.example.com")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3")).c_str(),
                   NULL);
 }
 
-TEST_F(QueryTest, badSecureDelegation) {
+TEST_F(QueryTestForMockOnly, badSecureDelegation) {
+    // This is a broken data source scenario; works only with mock.
+
     // Test whether exception is raised if DS query at delegation results in
     // something different than SUCCESS or NXRRSET
-    EXPECT_THROW(query.process(list,
+    EXPECT_THROW(query.process(*list_,
                                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.process(list,
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("bad-delegation.example.com"),
                                   qtype, response));
 }
 
 
-TEST_F(QueryTest, nxdomain) {
-    EXPECT_NO_THROW(query.process(list,
+TEST_P(QueryTest, nxdomain) {
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("nxdomain.example.com"), qtype,
                                   response));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainWithNSEC) {
+TEST_P(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.process(list,
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("nxdomain.example.com"), qtype,
                                   response, true));
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
@@ -1255,12 +1368,18 @@ TEST_F(QueryTest, nxdomainWithNSEC) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainWithNSEC2) {
+TEST_P(QueryTest, nxdomainWithNSEC2) {
+    // there seems to be a bug in the SQLite3 (or database in general) data
+    // source and this doesn't work (Trac #2586).
+    if (GetParam() == SQLITE3) {
+        return;
+    }
+
     // See comments about no_txt.  In this case the best possible wildcard
     // 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.process(list, Name("(.no.example.com"), qtype, response,
+    query.process(*list_, Name("(.no.example.com"), qtype, response,
                   true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
@@ -1275,10 +1394,17 @@ TEST_F(QueryTest, nxdomainWithNSEC2) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainWithNSECDuplicate) {
+TEST_P(QueryTest, nxdomainWithNSECDuplicate) {
+    // there seems to be a bug in the SQLite3 (or database in general) data
+    // source and this doesn't work.  This is probably the same type of bug
+    // as nxdomainWithNSEC2 (Trac #2586).
+    if (GetParam() == SQLITE3) {
+        return;
+    }
+
     // See comments about nz_txt.  In this case we only need one NSEC,
     // which proves both NXDOMAIN and the non existence of wildcard.
-    query.process(list, Name("nx.no.example.com"), qtype, response,
+    query.process(*list_, Name("nx.no.example.com"), qtype, response,
                   true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 4, 0,
                   NULL, (string(soa_txt) +
@@ -1290,52 +1416,62 @@ TEST_F(QueryTest, nxdomainWithNSECDuplicate) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainBadNSEC1) {
+TEST_F(QueryTestForMockOnly, nxdomainBadNSEC1) {
+    // This is a broken data source scenario; works only with mock.
+
     // ZoneFinder::find() returns NXDOMAIN with non NSEC RR.
     mock_finder->setNSECResult(Name("badnsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(query.process(list, Name("badnsec.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("badnsec.example.com"),
                                qtype, response, true),
                  std::bad_cast);
 }
 
-TEST_F(QueryTest, nxdomainBadNSEC2) {
+TEST_F(QueryTestForMockOnly, nxdomainBadNSEC2) {
+    // This is a broken data source scenario; works only with mock.
+
     // ZoneFinder::find() returns NXDOMAIN with an empty NSEC RR.
     mock_finder->setNSECResult(Name("emptynsec.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(query.process(list, Name("emptynsec.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("emptynsec.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, nxdomainBadNSEC3) {
+TEST_F(QueryTestForMockOnly, nxdomainBadNSEC) {
+    // This is a broken data source scenario; works only with mock.
+
     // "no-wildcard proof" returns SUCCESS.  it should be NXDOMAIN.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, nxdomainBadNSEC4) {
+TEST_F(QueryTestForMockOnly, nxdomainBadNSEC4) {
+    // This is a broken data source scenario; works only with mock.
+
     // "no-wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, nxdomainBadNSEC5) {
+TEST_F(QueryTestForMockOnly, nxdomainBadNSEC5) {
+    // This is a broken data source scenario; works only with mock.
+
     // "no-wildcard proof" returns non NSEC.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->dname_rrset_);
     // This is a bit odd, but we'll simply include the returned RRset.
-    query.process(list, Name("nxdomain.example.com"), qtype,
+    query.process(*list_, Name("nxdomain.example.com"), qtype,
                   response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
                   NULL, (string(soa_txt) +
@@ -1350,28 +1486,30 @@ TEST_F(QueryTest, nxdomainBadNSEC5) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainBadNSEC6) {
+TEST_F(QueryTestForMockOnly, nxdomainBadNSEC6) {
+    // This is a broken data source scenario; works only with mock.
+
     // "no-wildcard proof" returns empty NSEC.
     mock_finder->setNSECResult(Name("*.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                qtype, response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, nxrrset) {
-    EXPECT_NO_THROW(query.process(list, Name("www.example.com"),
+TEST_P(QueryTest, nxrrset) {
+    EXPECT_NO_THROW(query.process(*list_, Name("www.example.com"),
                                   RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
                   NULL, soa_txt, NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxrrsetWithNSEC) {
+TEST_P(QueryTest, nxrrsetWithNSEC) {
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC that proves the
     // NXRRSET and their RRSIGs.
-    query.process(list, Name("www.example.com"), RRType::TXT(),
+    query.process(*list_, Name("www.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -1383,7 +1521,7 @@ TEST_F(QueryTest, nxrrsetWithNSEC) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, emptyNameWithNSEC) {
+TEST_P(QueryTest, emptyNameWithNSEC) {
     // Empty non terminal with DNSSEC proof.  This is one of the cases of
     // Section 3.1.3.2 of RFC4035.
     // mx.example.com. NSEC ).no.example.com. proves no.example.com. is a
@@ -1392,7 +1530,7 @@ 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.process(list, Name("no.example.com"), RRType::A(),
+    query.process(*list_, Name("no.example.com"), RRType::A(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -1404,11 +1542,11 @@ TEST_F(QueryTest, emptyNameWithNSEC) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxrrsetWithoutNSEC) {
+TEST_P(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.process(list, Name("nonsec.example.com"), RRType::TXT(),
+    query.process(*list_, Name("nonsec.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
@@ -1417,10 +1555,10 @@ TEST_F(QueryTest, nxrrsetWithoutNSEC) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, wildcardNSEC) {
+TEST_P(QueryTest, wildcardNSEC) {
     // The qname matches *.wild.example.com.  The response should contain
     // an NSEC that proves the non existence of a closer name.
-    query.process(list, Name("www.wild.example.com"), RRType::A(),
+    query.process(*list_, 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") +
@@ -1437,10 +1575,10 @@ TEST_F(QueryTest, wildcardNSEC) {
                   mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, CNAMEwildNSEC) {
+TEST_P(QueryTest, CNAMEwildNSEC) {
     // Similar to the previous case, but the matching wildcard record is
     // CNAME.
-    query.process(list, Name("www.cnamewild.example.com"),
+    query.process(*list_, 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") +
@@ -1453,17 +1591,17 @@ TEST_F(QueryTest, CNAMEwildNSEC) {
                   mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, wildcardNSEC3) {
+TEST_P(QueryTest, wildcardNSEC3) {
     // Similar to wildcardNSEC, but the zone is signed with NSEC3.
     // The next closer is y.wild.example.com, the covering NSEC3 for it
     // is (in our setup) the NSEC3 for the apex.
-    mock_finder->setNSEC3Flag(true);
-
-    // This is NSEC3 for wild.example.com, which will be used in the middle
+    //
+    // Adding NSEC3 for wild.example.com, which will be used in the middle
     // of identifying the next closer name.
-    mock_finder->addRecord(nsec3_atwild_txt);
+    rrsets_to_add_.push_back(nsec3_atwild_txt);
+    enableNSEC3(rrsets_to_add_);
 
-    query.process(list, Name("x.y.wild.example.com"), RRType::A(),
+    query.process(*list_, 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") +
@@ -1474,35 +1612,37 @@ TEST_F(QueryTest, wildcardNSEC3) {
                    getCommonRRSIGText("NS") + "\n" +
                    // NSEC3 for the wildcard proof and its RRSIG
                    string(nsec3_apex_txt) +
-                   mock_finder->hash_map_[Name("example.com.")] +
+                   nsec3_hash_.calculate(Name("example.com.")) +
                    string(".example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("NSEC3")).c_str(),
                   NULL, // we are not interested in additionals in this test
                   mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, CNAMEwildNSEC3) {
+TEST_P(QueryTest, CNAMEwildNSEC3) {
     // Similar to CNAMEwildNSEC, but with NSEC3.
     // The next closer is qname itself, the covering NSEC3 for it
     // is (in our setup) the NSEC3 for the www.example.com.
-    mock_finder->setNSEC3Flag(true);
-    mock_finder->addRecord(nsec3_atcnamewild_txt);
+    rrsets_to_add_.push_back(nsec3_atcnamewild_txt);
+    enableNSEC3(rrsets_to_add_);
 
-    query.process(list, Name("www.cnamewild.example.com"),
+    query.process(*list_, 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 ") +
                    getCommonRRSIGText("CNAME") + "\n").c_str(),
                   (string(nsec3_www_txt) +
-                   mock_finder->hash_map_[Name("www.example.com.")] +
+                   nsec3_hash_.calculate(Name("www.example.com.")) +
                    string(".example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("NSEC3")).c_str(),
                   NULL, // we are not interested in additionals in this test
                   mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, badWildcardNSEC3) {
+TEST_F(QueryTestForMockOnly, badWildcardNSEC3) {
+    // This is a broken data source scenario; works only with mock.
+
     // Similar to wildcardNSEC3, but emulating run time collision by
     // returning NULL in the next closer proof for the closest encloser
     // proof.
@@ -1511,46 +1651,58 @@ TEST_F(QueryTest, badWildcardNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC3);
 }
 
-TEST_F(QueryTest, badWildcardProof1) {
+TEST_F(QueryTestForMockOnly, badWildcardProof1) {
+    // This is a broken data source scenario; works only with mock.
+
     // Unexpected case in wildcard proof: ZoneFinder::find() returns SUCCESS
     // when NXDOMAIN is expected.
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::SUCCESS,
                                mock_finder->dname_rrset_);
-    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, badWildcardProof2) {
+TEST_F(QueryTestForMockOnly, badWildcardProof2) {
+    // This is a broken data source scenario; works only with mock.
+
     // "wildcard proof" doesn't return RRset.
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN, ConstRRsetPtr());
-    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, badWildcardProof3) {
+TEST_F(QueryTestForMockOnly, badWildcardProof3) {
+    // This is a broken data source scenario; works only with mock.
+
     // "wildcard proof" returns empty NSEC.
     mock_finder->setNSECResult(Name("www.wild.example.com"),
                                ZoneFinder::NXDOMAIN,
                                mock_finder->empty_nsec_rrset_);
-    EXPECT_THROW(query.process(list, Name("www.wild.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www.wild.example.com"),
                                RRType::A(), response, true),
                  Query::BadNSEC);
 }
 
-TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
+TEST_P(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
+    // This is an in-memory specific bug (#2585), until it's fixed we
+    // tentatively skip the test for in-memory
+    if (GetParam() == INMEMORY) {
+        return;
+    }
+
     // 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.process(list, Name("www.wild.example.com"), RRType::TXT(),
+    query.process(*list_, Name("www.wild.example.com"), RRType::TXT(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -1562,12 +1714,18 @@ TEST_F(QueryTest, wildcardNxrrsetWithDuplicateNSEC) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
+TEST_P(QueryTest, wildcardNxrrsetWithNSEC) {
+    // This is an in-memory specific bug (#2585), until it's fixed we
+    // tentatively skip the test for in-memory
+    if (GetParam() == INMEMORY) {
+        return;
+    }
+
     // WILDCARD + NXRRSET with DNSSEC proof.  We should have SOA, NSEC that
     // 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.process(list, Name("www1.uwild.example.com"),
+    query.process(*list_, Name("www1.uwild.example.com"),
                   RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
@@ -1582,15 +1740,15 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC) {
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
+TEST_P(QueryTest, wildcardNxrrsetWithNSEC3) {
     // Similar to the previous case, but providing NSEC3 proofs according to
     // RFC5155 Section 7.2.5.
 
-    mock_finder->addRecord(nsec3_wild_txt);
-    mock_finder->addRecord(nsec3_uwild_txt);
-    mock_finder->setNSEC3Flag(true);
+    rrsets_to_add_.push_back(nsec3_wild_txt);
+    rrsets_to_add_.push_back(nsec3_uwild_txt);
+    enableNSEC3(rrsets_to_add_);
 
-    query.process(list, Name("www1.uwild.example.com"),
+    query.process(*list_, Name("www1.uwild.example.com"),
                   RRType::TXT(), response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 8, 0, NULL,
@@ -1599,23 +1757,25 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3) {
                    getCommonRRSIGText("SOA") + "\n" +
                    // NSEC3 for the closest encloser + its RRSIG
                    string(nsec3_uwild_txt) +
-                   mock_finder->hash_map_[Name("uwild.example.com.")] +
+                   nsec3_hash_.calculate(Name("uwild.example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n" +
                    // NSEC3 for the next closer + its RRSIG
                    string(nsec3_www_txt) +
-                   mock_finder->hash_map_[Name("www.example.com.")] +
+                   nsec3_hash_.calculate(Name("www.example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n" +
                    // NSEC3 for the wildcard + its RRSIG
                    string(nsec3_wild_txt) +
-                   mock_finder->hash_map_[Name("*.uwild.example.com.")] +
+                   nsec3_hash_.calculate(Name("*.uwild.example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3")).c_str(),
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Collision) {
+TEST_F(QueryTestForMockOnly, wildcardNxrrsetWithNSEC3Collision) {
+    // This is a broken data source scenario; works only with mock.
+
     // Similar to the previous case, but emulating run time collision by
     // returning NULL in the next closer proof for the closest encloser
     // proof.
@@ -1624,15 +1784,17 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Collision) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(list, Name("www1.uwild.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www1.uwild.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
-TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
+TEST_F(QueryTestForMockOnly, wildcardNxrrsetWithNSEC3Broken) {
+    // This is a broken data source scenario; works only with mock.
+
     // Similar to wildcardNxrrsetWithNSEC3, but no matching NSEC3 for the
     // wildcard name will be returned.  This shouldn't happen in a reasonably
-    // NSEC-signed zone, and should result in an exception.
+    // NSEC3-signed zone, and should result in an exception.
     mock_finder->setNSEC3Flag(true);
     const Name wname("*.uwild.example.com.");
     ZoneFinder::FindNSEC3Result nsec3(false, 0, textToRRset(nsec3_apex_txt),
@@ -1641,16 +1803,16 @@ TEST_F(QueryTest, wildcardNxrrsetWithNSEC3Broken) {
     mock_finder->addRecord(nsec3_wild_txt);
     mock_finder->addRecord(nsec3_uwild_txt);
 
-    EXPECT_THROW(query.process(list, Name("www1.uwild.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www1.uwild.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
-TEST_F(QueryTest, wildcardEmptyWithNSEC) {
+TEST_P(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.process(list, Name("a.t.example.com"), RRType::A(),
+    query.process(*list_, Name("a.t.example.com"), RRType::A(),
                   response, true);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
@@ -1669,24 +1831,26 @@ TEST_F(QueryTest, wildcardEmptyWithNSEC) {
  * This tests that when there's no SOA and we need a negative answer. It should
  * throw in that case.
  */
-TEST_F(QueryTest, noSOA) {
+TEST_F(QueryTestForMockOnly, noSOA) {
+    // This is a broken data source scenario; works only with mock.
+
     // disable zone's SOA RR.
     mock_finder->setSOAFlag(false);
 
     // The NX Domain
-    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                qtype, response), Query::NoSOA);
     // Of course, we don't look into the response, as it throwed
 
     // NXRRSET
-    EXPECT_THROW(query.process(list, Name("nxrrset.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("nxrrset.example.com"),
                                qtype, response), Query::NoSOA);
 }
 
-TEST_F(QueryTest, noMatchZone) {
+TEST_P(QueryTest, noMatchZone) {
     // there's a zone in the memory datasource but it doesn't match the qname.
     // should result in REFUSED.
-    query.process(list, Name("example.org"), qtype, response);
+    query.process(*list_, Name("example.org"), qtype, response);
     EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
 }
 
@@ -1696,8 +1860,8 @@ TEST_F(QueryTest, noMatchZone) {
  * The MX RRset has two RRs, one pointing to a known domain with
  * A record, other to unknown out of zone one.
  */
-TEST_F(QueryTest, MX) {
-    query.process(list, Name("mx.example.com"), RRType::MX(),
+TEST_P(QueryTest, MX) {
+    query.process(*list_, Name("mx.example.com"), RRType::MX(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 3, 3, 4,
@@ -1710,8 +1874,8 @@ TEST_F(QueryTest, MX) {
  *
  * This should not trigger the additional processing for the exchange.
  */
-TEST_F(QueryTest, MXAlias) {
-    query.process(list, Name("cnamemx.example.com"), RRType::MX(),
+TEST_P(QueryTest, MXAlias) {
+    query.process(*list_, Name("cnamemx.example.com"), RRType::MX(),
                   response);
 
     // there shouldn't be no additional RRs for the exchanges (we have 3
@@ -1730,69 +1894,69 @@ TEST_F(QueryTest, MXAlias) {
  * TODO: We currently don't do chaining, so only the CNAME itself should be
  * returned.
  */
-TEST_F(QueryTest, CNAME) {
-    query.process(list, Name("cname.example.com"), RRType::A(),
+TEST_P(QueryTest, CNAME) {
+    query.process(*list_, Name("cname.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_txt, NULL, NULL);
 }
 
-TEST_F(QueryTest, explicitCNAME) {
+TEST_P(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.process(list, Name("cname.example.com"), RRType::CNAME(),
+    query.process(*list_, Name("cname.example.com"), RRType::CNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_txt, zone_ns_txt, ns_addrs_txt);
 }
 
-TEST_F(QueryTest, CNAME_NX_RRSET) {
+TEST_P(QueryTest, CNAME_NX_RRSET) {
     // Leads to www.example.com, it doesn't have TXT
     // 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.process(list, Name("cname.example.com"), RRType::TXT(),
+    query.process(*list_, Name("cname.example.com"), RRType::TXT(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_txt, NULL, NULL);
 }
 
-TEST_F(QueryTest, explicitCNAME_NX_RRSET) {
+TEST_P(QueryTest, explicitCNAME_NX_RRSET) {
     // same owner name as the NXRRSET test but explicitly query for CNAME RR.
-    query.process(list, Name("cname.example.com"), RRType::CNAME(),
+    query.process(*list_, Name("cname.example.com"), RRType::CNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
         cname_txt, zone_ns_txt, ns_addrs_txt);
 }
 
-TEST_F(QueryTest, CNAME_NX_DOMAIN) {
+TEST_P(QueryTest, CNAME_NX_DOMAIN) {
     // Leads to nxdomain.example.com
     // note: with chaining, what should be expected is not trivial:
     // BIND 9 returns the CNAME in answer and SOA in authority, no additional,
     // RCODE being NXDOMAIN.
     // NSD returns the CNAME, NS in authority, A/AAAA for NS in additional,
     // RCODE being NOERROR.
-    query.process(list, Name("cnamenxdom.example.com"), RRType::A(),
+    query.process(*list_, Name("cnamenxdom.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_nxdom_txt, NULL, NULL);
 }
 
-TEST_F(QueryTest, explicitCNAME_NX_DOMAIN) {
+TEST_P(QueryTest, explicitCNAME_NX_DOMAIN) {
     // same owner name as the NXDOMAIN test but explicitly query for CNAME RR.
-    query.process(list, Name("cnamenxdom.example.com"),
+    query.process(*list_, 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);
 }
 
-TEST_F(QueryTest, CNAME_OUT) {
+TEST_P(QueryTest, CNAME_OUT) {
     /*
      * This leads out of zone. This should have only the CNAME even
      * when we do chaining.
@@ -1801,16 +1965,16 @@ 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.process(list, Name("cnameout.example.com"), RRType::A(),
+    query.process(*list_, Name("cnameout.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 0, 0,
         cname_out_txt, NULL, NULL);
 }
 
-TEST_F(QueryTest, explicitCNAME_OUT) {
+TEST_P(QueryTest, explicitCNAME_OUT) {
     // same owner name as the OUT test but explicitly query for CNAME RR.
-    query.process(list, Name("cnameout.example.com"), RRType::CNAME(),
+    query.process(*list_, Name("cnameout.example.com"), RRType::CNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1825,8 +1989,8 @@ TEST_F(QueryTest, explicitCNAME_OUT) {
  * as well. This includes tests pointing inside the zone, outside the zone,
  * pointing to NXRRSET and NXDOMAIN cases (similarly as with CNAME).
  */
-TEST_F(QueryTest, DNAME) {
-    query.process(list, Name("www.dname.example.com"), RRType::A(),
+TEST_P(QueryTest, DNAME) {
+    query.process(*list_, Name("www.dname.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
@@ -1841,8 +2005,8 @@ TEST_F(QueryTest, DNAME) {
  * ANY is handled specially sometimes. We check it is not the case with
  * DNAME.
  */
-TEST_F(QueryTest, DNAME_ANY) {
-    query.process(list, Name("www.dname.example.com"), RRType::ANY(),
+TEST_P(QueryTest, DNAME_ANY) {
+    query.process(*list_, Name("www.dname.example.com"), RRType::ANY(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 0, 0,
@@ -1850,8 +2014,8 @@ TEST_F(QueryTest, DNAME_ANY) {
 }
 
 // Test when we ask for DNAME explicitly, it does no synthetizing.
-TEST_F(QueryTest, explicitDNAME) {
-    query.process(list, Name("dname.example.com"), RRType::DNAME(),
+TEST_P(QueryTest, explicitDNAME) {
+    query.process(*list_, Name("dname.example.com"), RRType::DNAME(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1862,8 +2026,8 @@ TEST_F(QueryTest, explicitDNAME) {
  * Request a RRset at the domain with DNAME. It should not synthetize
  * the CNAME, it should return the RRset.
  */
-TEST_F(QueryTest, DNAME_A) {
-    query.process(list, Name("dname.example.com"), RRType::A(),
+TEST_P(QueryTest, DNAME_A) {
+    query.process(*list_, Name("dname.example.com"), RRType::A(),
                   response);
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 1, 3, 3,
@@ -1874,8 +2038,8 @@ TEST_F(QueryTest, DNAME_A) {
  * Request a RRset at the domain with DNAME that is not there (NXRRSET).
  * It should not synthetize the CNAME.
  */
-TEST_F(QueryTest, DNAME_NX_RRSET) {
-    EXPECT_NO_THROW(query.process(list, Name("dname.example.com"),
+TEST_P(QueryTest, DNAME_NX_RRSET) {
+    EXPECT_NO_THROW(query.process(*list_, Name("dname.example.com"),
                     RRType::TXT(), response));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 1, 0,
@@ -1887,7 +2051,7 @@ TEST_F(QueryTest, DNAME_NX_RRSET) {
  * however, should not throw (and crash the server), but respond with
  * YXDOMAIN.
  */
-TEST_F(QueryTest, LongDNAME) {
+TEST_P(QueryTest, LongDNAME) {
     // A name that is as long as it can be
     Name longname(
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
@@ -1895,7 +2059,7 @@ TEST_F(QueryTest, LongDNAME) {
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(query.process(list, longname, RRType::A(),
+    EXPECT_NO_THROW(query.process(*list_, longname, RRType::A(),
                     response));
 
     responseCheck(response, Rcode::YXDOMAIN(), AA_FLAG, 1, 0, 0,
@@ -1907,14 +2071,14 @@ TEST_F(QueryTest, LongDNAME) {
  * This tests that we don't reject valid one by some kind of off by
  * one mistake.
  */
-TEST_F(QueryTest, MaxLenDNAME) {
+TEST_P(QueryTest, MaxLenDNAME) {
     Name longname(
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa."
         "dname.example.com.");
-    EXPECT_NO_THROW(query.process(list, longname, RRType::A(),
+    EXPECT_NO_THROW(query.process(*list_, longname, RRType::A(),
                     response));
 
     // Check the answer is OK
@@ -1963,7 +2127,10 @@ nsec3Check(bool expected_matched, uint8_t expected_labels,
                 actual_rrsets.end());
 }
 
-TEST_F(QueryTest, findNSEC3) {
+TEST_F(QueryTestForMockOnly, findNSEC3) {
+    // This test is intended to test the mock data source behavior; no need
+    // to do it for others.
+
     // In all test cases in the recursive mode, the closest encloser is the
     // apex, and result's closest_labels should be the number of apex labels.
     // (In non recursive mode closest_labels should be the # labels of the
@@ -1975,7 +2142,7 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("apex, non recursive");
         nsec3Check(true, expected_closest_labels,
-                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
+                   string(nsec3_apex_txt) + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("example.com"), false));
     }
 
@@ -1983,7 +2150,7 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("apex, recursive");
         nsec3Check(true, expected_closest_labels,
-                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
+                   string(nsec3_apex_txt) + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("example.com"), true));
     }
 
@@ -1992,7 +2159,7 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, non recursive");
         nsec3Check(false, 4,
-                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
+                   string(nsec3_www_txt) + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain.example.com"),
                                           false));
     }
@@ -2002,7 +2169,7 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, recursive");
         nsec3Check(true, expected_closest_labels,
-                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt + "\n" +
+                   string(nsec3_apex_txt) + "\n" + nsec3_apex_rrsig_txt + "\n" +
                    nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain.example.com"), true));
     }
@@ -2012,7 +2179,8 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("nxdomain, next closer != qname");
         nsec3Check(true, expected_closest_labels,
-                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt + "\n" +
+                   string(nsec3_apex_txt) + "\n" +
+                   nsec3_apex_rrsig_txt + "\n" +
                    nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nx.domain.example.com"),
                                           true));
@@ -2022,14 +2190,14 @@ TEST_F(QueryTest, findNSEC3) {
     {
         SCOPED_TRACE("largest");
         nsec3Check(false, 4,
-                   nsec3_apex_txt + "\n" + nsec3_apex_rrsig_txt,
+                   string(nsec3_apex_txt) + "\n" + nsec3_apex_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain2.example.com"),
                                           false));
     }
     {
         SCOPED_TRACE("smallest");
         nsec3Check(false, 4,
-                   nsec3_www_txt + "\n" + nsec3_www_rrsig_txt,
+                   string(nsec3_www_txt) + "\n" + nsec3_www_rrsig_txt,
                    mock_finder->findNSEC3(Name("nxdomain3.example.com"),
                                           false));
     }
@@ -2100,14 +2268,17 @@ private:
     const bool have_ds_;
 };
 
-TEST_F(QueryTest, dsAboveDelegation) {
+TEST_F(QueryTestForMockOnly, dsAboveDelegation) {
+    // We could setup the child zone for other data sources, but it won't be
+    // simple addition.  For now we test it for mock only.
+
     // Pretending to have authority for the child zone, too.
     memory_client.addZone(ZoneFinderPtr(new AlternateZoneFinder(
                                             Name("delegation.example.com"))));
 
     // The following will succeed only if the search goes to the parent
     // zone, not the child one we added above.
-    EXPECT_NO_THROW(query.process(list,
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("delegation.example.com"),
                                   RRType::DS(), response, true));
 
@@ -2121,7 +2292,7 @@ TEST_F(QueryTest, dsAboveDelegation) {
                   ns_addrs_and_sig_txt.c_str());
 }
 
-TEST_F(QueryTest, dsAboveDelegationNoData) {
+TEST_P(QueryTest, dsAboveDelegationNoData) {
     // Similar to the previous case, but the query is for an unsigned zone
     // (which doesn't have a DS at the parent).  The response should be a
     // "no data" error.  The query should still be handled at the parent.
@@ -2131,7 +2302,7 @@ 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.process(list,
+    EXPECT_NO_THROW(query.process(*list_,
                                   Name("unsigned-delegation.example.com"),
                                   RRType::DS(), response, true));
 
@@ -2148,8 +2319,8 @@ TEST_F(QueryTest, dsAboveDelegationNoData) {
 // This one checks that type-DS query results in a "no data" response
 // 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.process(list, Name("example.com"),
+TEST_P(QueryTest, dsBelowDelegation) {
+    EXPECT_NO_THROW(query.process(*list_, Name("example.com"),
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
@@ -2164,9 +2335,10 @@ TEST_F(QueryTest, dsBelowDelegation) {
 // Similar to the previous case, but even more pathological: the DS somehow
 // exists in the child zone.  The Query module should still return SOA.
 // 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.process(list, Name("example.com"),
+TEST_P(QueryTest, dsBelowDelegationWithDS) {
+    rrsets_to_add_.push_back(zone_ds_txt);
+    addRRsets(rrsets_to_add_, *list_, base_zone_file);
+    EXPECT_NO_THROW(query.process(*list_, Name("example.com"),
                                   RRType::DS(), response, true));
 
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
@@ -2178,16 +2350,16 @@ TEST_F(QueryTest, dsBelowDelegationWithDS) {
 // DS query received at a completely irrelevant (neither parent nor child)
 // 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.process(list, Name("example"), RRType::DS(), response,
+TEST_P(QueryTest, dsNoZone) {
+    query.process(*list_, 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.process(list, Name("grand.delegation.example.com"),
+TEST_P(QueryTest, dsAtGrandParent) {
+    query.process(*list_, 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) +
@@ -2201,12 +2373,15 @@ TEST_F(QueryTest, dsAtGrandParent) {
 // side and should result in no data with SOA.  Note that the server doesn't
 // have authority for the "parent".  Unlike the dsAboveDelegation test case
 // the query should be handled in the child zone, not in the grandparent.
-TEST_F(QueryTest, dsAtGrandParentAndChild) {
+TEST_F(QueryTestForMockOnly, dsAtGrandParentAndChild) {
+    // We could setup the child zone for other data sources, but it won't be
+    // simple addition.  For now we test it for mock only.
+
     // Pretending to have authority for the child zone, too.
     const Name childname("grand.delegation.example.com");
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(childname)));
-    query.process(list, childname, RRType::DS(), response, true);
+    query.process(*list_, 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 " +
@@ -2220,11 +2395,14 @@ TEST_F(QueryTest, dsAtGrandParentAndChild) {
 // DS query for the root name (quite pathological).  Since there's no "parent",
 // the query will be handled in the root zone anyway, and should (normally)
 // result in no data.
-TEST_F(QueryTest, dsAtRoot) {
+TEST_F(QueryTestForMockOnly, dsAtRoot) {
+    // We could setup the additional zone for other data sources, but it
+    // won't be simple addition.  For now we test it for mock only.
+
     // Pretend to be a root server.
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME())));
-    query.process(list, Name::ROOT_NAME(), RRType::DS(), response,
+    query.process(*list_, 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") +
@@ -2237,11 +2415,14 @@ TEST_F(QueryTest, dsAtRoot) {
 // Even more pathological case: A faked root zone actually has its own DS
 // query.  How we respond wouldn't matter much in practice, but check if
 // it behaves as it's intended.  This implementation should return the DS.
-TEST_F(QueryTest, dsAtRootWithDS) {
+TEST_F(QueryTestForMockOnly, dsAtRootWithDS) {
+    // We could setup the additional zone for other data sources, but it
+    // won't be simple addition.  For now we test it for mock only.
+
     memory_client.addZone(ZoneFinderPtr(
                               new AlternateZoneFinder(Name::ROOT_NAME(),
                                                       true)));
-    query.process(list, Name::ROOT_NAME(), RRType::DS(), response,
+    query.process(*list_, Name::ROOT_NAME(), RRType::DS(), response,
                   true);
     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
                   (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD"
@@ -2253,19 +2434,19 @@ TEST_F(QueryTest, dsAtRootWithDS) {
 }
 
 // Check the signature is present when an NXRRSET is returned
-TEST_F(QueryTest, nxrrsetWithNSEC3) {
-    mock_finder->setNSEC3Flag(true);
+TEST_P(QueryTest, nxrrsetWithNSEC3) {
+    enableNSEC3(rrsets_to_add_);
 
     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC3 that proves the
     // NXRRSET and their RRSIGs.
-    query.process(list, Name("www.example.com"), RRType::TXT(),
+    query.process(*list_, 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 ") +
                    getCommonRRSIGText("SOA") + "\n" +
                    string(nsec3_www_txt) + "\n" +
-                   mock_finder->hash_map_[Name("www.example.com.")] +
+                   nsec3_hash_.calculate(Name("www.example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n").c_str(),
                   NULL, mock_finder->getOrigin());
@@ -2273,7 +2454,9 @@ TEST_F(QueryTest, nxrrsetWithNSEC3) {
 
 // Check the exception is correctly raised when the NSEC3 thing isn't in the
 // zone
-TEST_F(QueryTest, nxrrsetMissingNSEC3) {
+TEST_F(QueryTestForMockOnly, nxrrsetMissingNSEC3) {
+    // This is a broken data source scenario; works only with mock.
+
     mock_finder->setNSEC3Flag(true);
     // We just need it to return false for "matched". This indicates
     // there's no exact match for NSEC3 on www.example.com.
@@ -2281,67 +2464,67 @@ TEST_F(QueryTest, nxrrsetMissingNSEC3) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(list, Name("www.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("www.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
-TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
-    mock_finder->addRecord(unsigned_delegation_nsec3_txt);
-    mock_finder->setNSEC3Flag(true);
+TEST_P(QueryTest, nxrrsetWithNSEC3_ds_exact) {
+    rrsets_to_add_.push_back(unsigned_delegation_nsec3_txt);
+    enableNSEC3(rrsets_to_add_);
 
     // This delegation has no DS, but does have a matching NSEC3 record
     // (See RFC5155 section 7.2.4)
-    query.process(list, Name("unsigned-delegation.example.com."),
+    query.process(*list_, 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" +
                    string(unsigned_delegation_nsec3_txt) + "\n" +
-                   mock_finder->
-                        hash_map_[Name("unsigned-delegation.example.com.")] +
+                   nsec3_hash_.calculate(
+                       Name("unsigned-delegation.example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n").c_str(),
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
-    mock_finder->addRecord(unsigned_delegation_nsec3_txt);
-    mock_finder->setNSEC3Flag(true);
+TEST_P(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
+    rrsets_to_add_.push_back(unsigned_delegation_nsec3_txt);
+    enableNSEC3(rrsets_to_add_);
 
     // This delegation has no DS, and no directly matching NSEC3 record
     // So the response should contain closest encloser proof (and the
     // 'next closer' should have opt-out set, though that is not
     // actually checked)
     // (See RFC5155 section 7.2.4)
-    query.process(list, Name("unsigned-delegation-optout.example.com."),
+    query.process(*list_, 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" +
                    string(nsec3_apex_txt) + "\n" +
-                   mock_finder->hash_map_[Name("example.com.")] +
+                   nsec3_hash_.calculate(Name("example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n" +
                    string(unsigned_delegation_nsec3_txt) + "\n" +
-                   mock_finder->
-                        hash_map_[Name("unsigned-delegation.example.com.")] +
+                   nsec3_hash_.calculate(
+                       Name("unsigned-delegation.example.com.")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n").c_str(),
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
+TEST_P(QueryTest, nxdomainWithNSEC3Proof) {
     // Name Error (NXDOMAIN) case with NSEC3 proof per RFC5155 Section 7.2.2.
 
-    // Enable NSEC3
-    mock_finder->setNSEC3Flag(true);
     // This will be the covering NSEC3 for the next closer
-    mock_finder->addRecord(nsec3_uwild_txt);
+    rrsets_to_add_.push_back(nsec3_uwild_txt);
     // This will be the covering NSEC3 for the possible wildcard
-    mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+    rrsets_to_add_.push_back(unsigned_delegation_nsec3_txt);
+    // Enable NSEC3
+    enableNSEC3(rrsets_to_add_);
 
-    query.process(list, Name("nxdomain.example.com"), qtype,
+    query.process(*list_, Name("nxdomain.example.com"), qtype,
                   response, true);
     responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 8, 0, NULL,
                   // SOA + its RRSIG
@@ -2350,24 +2533,26 @@ TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
                    getCommonRRSIGText("SOA") + "\n" +
                    // NSEC3 for the closest encloser + its RRSIG
                    string(nsec3_apex_txt) + "\n" +
-                   mock_finder->hash_map_[mock_finder->getOrigin()] +
+                   nsec3_hash_.calculate(mock_finder->getOrigin()) +
                    string(".example.com. 3600 IN RRSIG ") +
                    getCommonRRSIGText("NSEC3") + "\n" +
                    // NSEC3 for the next closer + its RRSIG
                    string(nsec3_uwild_txt) + "\n" +
-                   mock_finder->hash_map_[Name("uwild.example.com")] +
+                   nsec3_hash_.calculate(Name("uwild.example.com")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3") + "\n" +
                    // NSEC3 for the wildcard + its RRSIG
                    string(unsigned_delegation_nsec3_txt) +
-                   mock_finder->hash_map_[
-                       Name("unsigned-delegation.example.com")] +
+                   nsec3_hash_.calculate(
+                       Name("unsigned-delegation.example.com")) +
                    ".example.com. 3600 IN RRSIG " +
                    getCommonRRSIGText("NSEC3")).c_str(),
                   NULL, mock_finder->getOrigin());
 }
 
-TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
+TEST_F(QueryTestForMockOnly, nxdomainWithBadNextNSEC3Proof) {
+    // This is a broken data source scenario; works only with mock.
+
     // Similar to the previous case, but emulating run time collision by
     // returning NULL in the next closer proof for the closest encloser
     // proof.
@@ -2376,12 +2561,14 @@ TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3);
 
-    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"),
+    EXPECT_THROW(query.process(*list_, Name("nxdomain.example.com"),
                                RRType::TXT(), response, true),
                  Query::BadNSEC3);
 }
 
-TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
+TEST_F(QueryTestForMockOnly, nxdomainWithBadWildcardNSEC3Proof) {
+    // This is a broken data source scenario; works only with mock.
+
     // Similar to nxdomainWithNSEC3Proof, but let findNSEC3() return a matching
     // NSEC3 for the possible wildcard name, emulating run-time collision.
     // This should result in BadNSEC3 exception.
@@ -2395,7 +2582,7 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
                                       ConstRRsetPtr());
     mock_finder->setNSEC3Result(&nsec3, &wname);
 
-    EXPECT_THROW(query.process(list, Name("nxdomain.example.com"), qtype,
+    EXPECT_THROW(query.process(*list_, Name("nxdomain.example.com"), qtype,
                                response, true),
                  Query::BadNSEC3);
 }
@@ -2403,10 +2590,13 @@ TEST_F(QueryTest, nxdomainWithBadWildcardNSEC3Proof) {
 // The following are tentative tests until we really add tests for the
 // query logic for these cases.  At that point it's probably better to
 // clean them up.
-TEST_F(QueryTest, emptyNameWithNSEC3) {
-    mock_finder->setNSEC3Flag(true);
-    ZoneFinderContextPtr result = mock_finder->find(
-        Name("no.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
+TEST_P(QueryTest, emptyNameWithNSEC3) {
+    enableNSEC3(rrsets_to_add_);
+    const Name qname("no.example.com");
+    ASSERT_TRUE(list_->find(qname).finder_);
+    ZoneFinderContextPtr result =
+        list_->find(qname).finder_->find(qname, RRType::A(),
+                                         ZoneFinder::FIND_DNSSEC);
     EXPECT_EQ(ZoneFinder::NXRRSET, result->code);
     EXPECT_FALSE(result->rrset);
     EXPECT_TRUE(result->isNSEC3Signed());
@@ -2447,7 +2637,9 @@ loadRRsetVector() {
                loadRRsetVectorCallback);
 }
 
-TEST_F(QueryTest, DuplicateNameRemoval) {
+// Note: this is an independent test; don't have to be in the QueryTest
+// fixture.
+TEST(QueryTestSingle, DuplicateNameRemoval) {
 
     // Load some RRsets into the master vector.
     loadRRsetVector();
diff --git a/src/bin/auth/tests/testdata/.gitignore b/src/bin/auth/tests/testdata/.gitignore
index b2e0e50..37acc8a 100644
--- a/src/bin/auth/tests/testdata/.gitignore
+++ b/src/bin/auth/tests/testdata/.gitignore
@@ -6,3 +6,13 @@
 /shortanswer_fromWire.wire
 /simplequery_fromWire.wire
 /simpleresponse_fromWire.wire
+/example-base.sqlite3
+/example-base.sqlite3.copied
+/example-base.zone
+/example-base.zone
+/example-common-inc.zone
+/example-nsec3-inc.zone
+/example-nsec3.sqlite3
+/example-nsec3.sqlite3.copied
+/example-nsec3.zone
+/example.zone
diff --git a/src/bin/auth/tests/testdata/Makefile.am b/src/bin/auth/tests/testdata/Makefile.am
index a4ea1a5..fed498a 100644
--- a/src/bin/auth/tests/testdata/Makefile.am
+++ b/src/bin/auth/tests/testdata/Makefile.am
@@ -1,4 +1,6 @@
-CLEANFILES = *.wire
+CLEANFILES = *.wire *.copied
+CLEANFILES += example-base.sqlite3 example-nsec3.sqlite3
+CLEANFILES += example-common-inc.zone
 
 BUILT_SOURCES = badExampleQuery_fromWire.wire examplequery_fromWire.wire
 BUILT_SOURCES += iqueryresponse_fromWire.wire multiquestion_fromWire.wire
@@ -24,5 +26,8 @@ EXTRA_DIST += example.com
 EXTRA_DIST += example.zone
 EXTRA_DIST += example.sqlite3
 
+EXTRA_DIST += example-base-inc.zone example-nsec3-inc.zone
+EXTRA_DIST += example-common-inc-template.zone
+
 .spec.wire:
 	$(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $<
diff --git a/src/bin/auth/tests/testdata/example-base-inc.zone b/src/bin/auth/tests/testdata/example-base-inc.zone
new file mode 100644
index 0000000..bbcbef1
--- /dev/null
+++ b/src/bin/auth/tests/testdata/example-base-inc.zone
@@ -0,0 +1,236 @@
+;; This file defines a set of RRs commonly used in query tests in the
+;; form of standard master zone file.
+;;
+;; It's a sequence of the following pattern:
+;; ;var=<var_name>
+;; RR_1
+;; RR_2
+;; ..
+;; RR_n
+;;
+;; where var_name is a string that can be used as a variable name in a
+;; C/C++ source file or an empty string.  RR_x is a single-line
+;; textual representation of an arbitrary DNS RR.
+;;
+;; If var_name is non empty, the generator script will define a C
+;; variable of C-string type for that set of RRs so that it can be referred
+;; to in the test source file.
+;;
+;; Note that lines beginning ';var=' is no different from other
+;; comment lines as a zone file.  It has special meaning only for the
+;; generator script.  Obviously, real comment lines cannot begin with
+;; ';var=' (which should be less likely to happen in practice though).
+;;
+;; These RRs will be loaded into in-memory data source in that order.
+;; Note that it may impose stricter restriction on the order of RRs.
+;; In general, each RRset of the same name and type and its RRSIG (if
+;; any) is expected to be grouped.
+
+;var=soa_txt
+example.com. 3600 IN SOA . . 1 0 0 0 0
+;var=zone_ns_txt
+example.com. 3600 IN NS glue.delegation.example.com.
+example.com. 3600 IN NS noglue.example.com.
+example.com. 3600 IN NS example.net.
+
+;var=
+example.com. 3600 IN RRSIG SOA 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+example.com. 3600 IN RRSIG NS 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; Note: the position of the next RR is tricky.  It's placed here to
+;; be grouped with the subsequent A RR of the name.  But we also want
+;; to group the A RR with other RRs of a different owner name, so the RRSIG
+;; cannot be placed after the A RR.  The empty 'var=' specification is
+;; not necessary here, but in case we want to reorganize the ordering
+;; (in which case it's more likely to be needed), we keep it here.
+;var=
+noglue.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;var=ns_addrs_txt
+noglue.example.com. 3600 IN A 192.0.2.53
+glue.delegation.example.com. 3600 IN A 192.0.2.153
+glue.delegation.example.com. 3600 IN AAAA 2001:db8::53
+
+;var=
+glue.delegation.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+glue.delegation.example.com. 3600 IN RRSIG AAAA 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;var=delegation_txt
+delegation.example.com. 3600 IN NS glue.delegation.example.com.
+delegation.example.com. 3600 IN NS noglue.example.com.
+delegation.example.com. 3600 IN NS cname.example.com.
+delegation.example.com. 3600 IN NS example.org.
+
+;var=
+delegation.example.com. 3600 IN RRSIG DS 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; Borrowed from the RFC4035
+;var=delegation_ds_txt
+delegation.example.com. 3600 IN DS 57855 5 1 B6DCD485719ADCA18E5F3D48A2331627FDD3 636B
+;var=mx_txt
+mx.example.com. 3600 IN MX 10 www.example.com.
+mx.example.com. 3600 IN MX 20 mailer.example.org.
+mx.example.com. 3600 IN MX 30 mx.delegation.example.com.
+;var=www_a_txt
+www.example.com. 3600 IN A 192.0.2.80
+
+;var=
+www.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;var=cname_txt
+cname.example.com. 3600 IN CNAME www.example.com.
+;var=cname_nxdom_txt
+cnamenxdom.example.com. 3600 IN CNAME nxdomain.example.com.
+;; CNAME Leading out of zone
+;var=cname_out_txt
+cnameout.example.com. 3600 IN CNAME www.example.org.
+;; The DNAME to do tests against
+;var=dname_txt
+dname.example.com. 3600 IN DNAME somethinglong.dnametarget.example.com.
+;; Some data at the dname node (allowed by RFC 2672)
+;var=dname_a_txt
+dname.example.com. 3600 IN A 192.0.2.5
+;; This is not inside the zone, this is created at runtime
+;; www.dname.example.com. 3600 IN CNAME www.somethinglong.dnametarget.example.com.
+;; The rest of data won't be referenced from the test cases.
+;var=other_zone_rrs
+cnamemailer.example.com. 3600 IN CNAME www.example.com.
+cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.
+mx.delegation.example.com. 3600 IN A 192.0.2.100
+;; Wildcards
+;var=wild_txt
+*.wild.example.com. 3600 IN A 192.0.2.7
+;var=nsec_wild_txt
+*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG
+
+;var=
+*.wild.example.com. 3600 IN RRSIG A 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+*.wild.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;var=cnamewild_txt
+*.cnamewild.example.com. 3600 IN CNAME www.example.org.
+;var=nsec_cnamewild_txt
+*.cnamewild.example.com. 3600 IN NSEC delegation.example.com. CNAME NSEC RRSIG
+
+;var=
+*.cnamewild.example.com. 3600 IN RRSIG CNAME 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+*.cnamewild.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; Wildcard_nxrrset
+;var=wild_txt_nxrrset
+*.uwild.example.com. 3600 IN A 192.0.2.9
+;var=nsec_wild_txt_nxrrset
+*.uwild.example.com. 3600 IN NSEC www.uwild.example.com. A NSEC RRSIG
+;var=
+*.uwild.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+;var=wild_txt_next
+www.uwild.example.com. 3600 IN A 192.0.2.11
+;var=
+www.uwild.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+;var=nsec_wild_txt_next
+www.uwild.example.com. 3600 IN NSEC *.wild.example.com. A NSEC RRSIG
+;; Wildcard empty
+;var=empty_txt
+b.*.t.example.com. 3600 IN A 192.0.2.13
+;var=nsec_empty_txt
+b.*.t.example.com. 3600 IN NSEC *.uwild.example.com. A NSEC RRSIG
+
+;var=
+b.*.t.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;var=empty_prev_txt
+t.example.com. 3600 IN A 192.0.2.15
+;var=nsec_empty_prev_txt
+t.example.com. 3600 IN NSEC b.*.t.example.com. A NSEC RRSIG
+
+;var=
+t.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; Used in NXDOMAIN proof test.  We are going to test some unusual case where
+;; the best possible wildcard is below the "next domain" of the NSEC RR that
+;; proves the NXDOMAIN, i.e.,
+;; mx.example.com. (exist)
+;; (.no.example.com. (qname, NXDOMAIN)
+;; ).no.example.com. (exist)
+;; *.no.example.com. (best possible wildcard, not exist)
+;var=no_txt
+\).no.example.com. 3600 IN AAAA 2001:db8::53
+;; NSEC records.
+;var=nsec_apex_txt
+example.com. 3600 IN NSEC cname.example.com. NS SOA NSEC RRSIG
+;var=
+example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+;var=nsec_mx_txt
+mx.example.com. 3600 IN NSEC \).no.example.com. MX NSEC RRSIG
+
+;var=
+mx.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;var=nsec_no_txt
+\).no.example.com. 3600 IN NSEC nz.no.example.com. AAAA NSEC RRSIG
+
+;var=
+\).no.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; We'll also test the case where a single NSEC proves both NXDOMAIN and the
+;; non existence of wildcard.  The following records will be used for that
+;; test.
+;; ).no.example.com. (exist, whose NSEC proves everything)
+;; *.no.example.com. (best possible wildcard, not exist)
+;; nx.no.example.com. (NXDOMAIN)
+;; nz.no.example.com. (exist)
+;var=nz_txt
+nz.no.example.com. 3600 IN AAAA 2001:db8::5300
+;var=nsec_nz_txt
+nz.no.example.com. 3600 IN NSEC noglue.example.com. AAAA NSEC RRSIG
+;var=nsec_nxdomain_txt
+noglue.example.com. 3600 IN NSEC nonsec.example.com. A
+
+;var=
+noglue.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; NSEC for the normal NXRRSET case
+;var=nsec_www_txt
+www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG
+
+;var=
+www.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; Authoritative data without NSEC
+;var=nonsec_a_txt
+nonsec.example.com. 3600 IN A 192.0.2.0
+
+;; (Secure) delegation data; Delegation with DS record
+;var=signed_delegation_txt
+signed-delegation.example.com. 3600 IN NS ns.example.net.
+;var=signed_delegation_ds_txt
+signed-delegation.example.com. 3600 IN DS 12345 8 2 764501411DE58E8618945054A3F620B36202E115D015A7773F4B78E0F952CECA
+
+;var=
+signed-delegation.example.com. 3600 IN RRSIG DS 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; (Secure) delegation data; Delegation without DS record (and both NSEC
+;; and NSEC3 denying its existence)
+;var=unsigned_delegation_txt
+unsigned-delegation.example.com. 3600 IN NS ns.example.net.
+;var=unsigned_delegation_nsec_txt
+unsigned-delegation.example.com. 3600 IN NSEC unsigned-delegation-optout.example.com. NS RRSIG NSEC
+
+;var=
+unsigned-delegation.example.com. 3600 IN RRSIG NSEC 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+
+;; Delegation without DS record, and no direct matching NSEC3 record
+;var=unsigned_delegation_optout_txt
+unsigned-delegation-optout.example.com. 3600 IN NS ns.example.net.
+;var=unsigned_delegation_optout_nsec_txt
+unsigned-delegation-optout.example.com. 3600 IN NSEC *.uwild.example.com. NS RRSIG NSEC
+
+;; (Secure) delegation data; Delegation where the DS lookup will raise an
+;; exception.
+;var=bad_delegation_txt
+bad-delegation.example.com. 3600 IN NS ns.example.net.
+
+;; Delegation from an unsigned parent.  There's no DS, and there's no NSEC
+;; or NSEC3 that proves it.
+;var=nosec_delegation_txt
+nosec-delegation.example.com. 3600 IN NS ns.nosec.example.net.
diff --git a/src/bin/auth/tests/testdata/example-base.zone.in b/src/bin/auth/tests/testdata/example-base.zone.in
new file mode 100644
index 0000000..63d2af0
--- /dev/null
+++ b/src/bin/auth/tests/testdata/example-base.zone.in
@@ -0,0 +1,7 @@
+;;
+;; This is a complete (but crafted and somewhat broken) zone file used
+;; in query tests.
+;;
+
+$INCLUDE @abs_srcdir@/example-base-inc.zone
+$INCLUDE @abs_builddir@/example-common-inc.zone
diff --git a/src/bin/auth/tests/testdata/example-common-inc-template.zone b/src/bin/auth/tests/testdata/example-common-inc-template.zone
new file mode 100644
index 0000000..d7259bf
--- /dev/null
+++ b/src/bin/auth/tests/testdata/example-common-inc-template.zone
@@ -0,0 +1,5 @@
+;;
+;; This is an initial template of part of test zone file used in query test
+;; and expected to be included from other zone files.  This is
+;; intentionally kept empty.
+;;
diff --git a/src/bin/auth/tests/testdata/example-nsec3-inc.zone b/src/bin/auth/tests/testdata/example-nsec3-inc.zone
new file mode 100644
index 0000000..7742df0
--- /dev/null
+++ b/src/bin/auth/tests/testdata/example-nsec3-inc.zone
@@ -0,0 +1,16 @@
+;; See query_testzone_data.txt for general notes.
+
+;; NSEC3PARAM.  This is needed for database-based data source to
+;; signal the zone is NSEC3-signed
+;var=
+example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
+
+;; NSEC3 RRs.  You may also need to add mapping to MockZoneFinder::hash_map_.
+;var=nsec3_apex_txt
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG
+;var=nsec3_apex_rrsig_txt
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG NSEC3 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
+;var=nsec3_www_txt
+q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
+;var=nsec3_www_rrsig_txt
+q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG NSEC3 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
diff --git a/src/bin/auth/tests/testdata/example-nsec3.zone.in b/src/bin/auth/tests/testdata/example-nsec3.zone.in
new file mode 100644
index 0000000..aaf3d6a
--- /dev/null
+++ b/src/bin/auth/tests/testdata/example-nsec3.zone.in
@@ -0,0 +1,8 @@
+;;
+;; This is a complete (but crafted and somewhat broken) zone file used
+;; in query tests including NSEC3 records, making the zone is "NSEC3 signed".
+;;
+
+$INCLUDE @abs_srcdir@/example-base-inc.zone
+$INCLUDE @abs_srcdir@/example-nsec3-inc.zone
+$INCLUDE @abs_builddir@/example-common-inc.zone
diff --git a/src/bin/auth/tests/testdata/example.zone b/src/bin/auth/tests/testdata/example.zone
deleted file mode 100644
index efbbaf2..0000000
--- a/src/bin/auth/tests/testdata/example.zone
+++ /dev/null
@@ -1,121 +0,0 @@
-;;
-;; This is a complete (but crafted and somewhat broken) zone file used
-;; in query tests.
-;;
-
-example.com. 3600 IN SOA . . 0 0 0 0 0
-example.com. 3600 IN NS glue.delegation.example.com.
-example.com. 3600 IN NS noglue.example.com.
-example.com. 3600 IN NS example.net.
-example.com. 3600 IN DS 57855 5 1 B6DCD485719ADCA18E5F3D48A2331627FDD3 636B
-glue.delegation.example.com. 3600 IN A 192.0.2.153
-glue.delegation.example.com. 3600 IN AAAA 2001:db8::53
-noglue.example.com. 3600 IN A 192.0.2.53
-delegation.example.com. 3600 IN NS glue.delegation.example.com.
-delegation.example.com. 3600 IN NS noglue.example.com.
-delegation.example.com. 3600 IN NS cname.example.com.
-delegation.example.com. 3600 IN NS example.org.
-;; Borrowed from the RFC4035
-delegation.example.com. 3600 IN DS 57855 5 1 B6DCD485719ADCA18E5F3D48A2331627FDD3 636B
-mx.example.com. 3600 IN MX 10 www.example.com.
-mx.example.com. 3600 IN MX 20 mailer.example.org.
-mx.example.com. 3600 IN MX 30 mx.delegation.example.com.
-www.example.com. 3600 IN A 192.0.2.80
-cname.example.com. 3600 IN CNAME www.example.com.
-cnamenxdom.example.com. 3600 IN CNAME nxdomain.example.com.
-;; CNAME Leading out of zone
-cnameout.example.com. 3600 IN CNAME www.example.org.
-;; The DNAME to do tests against
-dname.example.com. 3600 IN DNAME somethinglong.dnametarget.example.com.
-;; Some data at the dname node (allowed by RFC 2672)
-dname.example.com. 3600 IN A 192.0.2.5
-;; The rest of data won't be referenced from the test cases.
-cnamemailer.example.com. 3600 IN CNAME www.example.com.
-cnamemx.example.com. 3600 IN MX 10 cnamemailer.example.com.
-mx.delegation.example.com. 3600 IN A 192.0.2.100
-;; Wildcards
-*.wild.example.com. 3600 IN A 192.0.2.7
-*.wild.example.com. 3600 IN NSEC www.example.com. A NSEC RRSIG
-*.cnamewild.example.com. 3600 IN CNAME www.example.org.
-*.cnamewild.example.com. 3600 IN NSEC delegation.example.com. CNAME NSEC RRSIG
-;; Wildcard_nxrrset
-*.uwild.example.com. 3600 IN A 192.0.2.9
-*.uwild.example.com. 3600 IN NSEC www.uwild.example.com. A NSEC RRSIG
-www.uwild.example.com. 3600 IN A 192.0.2.11
-www.uwild.example.com. 3600 IN NSEC *.wild.example.com. A NSEC RRSIG
-;; Wildcard empty
-b.*.t.example.com. 3600 IN A 192.0.2.13
-b.*.t.example.com. 3600 IN NSEC *.uwild.example.com. A NSEC RRSIG
-t.example.com. 3600 IN A 192.0.2.15
-t.example.com. 3600 IN NSEC b.*.t.example.com. A NSEC RRSIG
-;; Used in NXDOMAIN proof test.  We are going to test some unusual case where
-;; the best possible wildcard is below the "next domain" of the NSEC RR that
-;; proves the NXDOMAIN, i.e.,
-;; mx.example.com. (exist)
-;; (.no.example.com. (qname, NXDOMAIN)
-;; ).no.example.com. (exist)
-;; *.no.example.com. (best possible wildcard, not exist)
-).no.example.com. 3600 IN AAAA 2001:db8::53
-;; NSEC records.
-example.com. 3600 IN NSEC cname.example.com. NS SOA NSEC RRSIG
-mx.example.com. 3600 IN NSEC ).no.example.com. MX NSEC RRSIG
-).no.example.com. 3600 IN NSEC nz.no.example.com. AAAA NSEC RRSIG
-;; We'll also test the case where a single NSEC proves both NXDOMAIN and the
-;; non existence of wildcard.  The following records will be used for that
-;; test.
-;; ).no.example.com. (exist, whose NSEC proves everything)
-;; *.no.example.com. (best possible wildcard, not exist)
-;; nx.no.example.com. (NXDOMAIN)
-;; nz.no.example.com. (exist)
-nz.no.example.com. 3600 IN AAAA 2001:db8::5300
-nz.no.example.com. 3600 IN NSEC noglue.example.com. AAAA NSEC RRSIG
-noglue.example.com. 3600 IN NSEC nonsec.example.com. A
-
-;; NSEC for the normal NXRRSET case
-www.example.com. 3600 IN NSEC example.com. A NSEC RRSIG
-
-;; Authoritative data without NSEC
-nonsec.example.com. 3600 IN A 192.0.2.0
-
-;; NSEC3 RRs.  You may also need to add mapping to MockZoneFinder::hash_map_.
-0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN NSEC3 1 1 12 aabbccdd 2t7b4g4vsa5smi47k61mv5bv1a22bojr NS SOA NSEC3PARAM RRSIG
-0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG NSEC3 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
-q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
-q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG NSEC3 5 3 3600 20000101000000 20000201000000 12345 example.com. FAKEFAKEFAKE
-
-;; NSEC3 for wild.example.com (used in wildcard tests, will be added on
-;; demand not to confuse other tests)
-ji6neoaepv8b5o6k4ev33abha8ht9fgc.example.com. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en
-
-;; NSEC3 for cnamewild.example.com (used in wildcard tests, will be added on
-;; demand not to confuse other tests)
-k8udemvp1j2f7eg6jebps17vp3n8i58h.example.com. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en
-
-;; NSEC3 for *.uwild.example.com (will be added on demand not to confuse
-;; other tests)
-b4um86eghhds6nea196smvmlo4ors995.example.com. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
-;; NSEC3 for uwild.example.com. (will be added on demand)
-t644ebqk9bibcna874givr6joj62mlhv.example.com. 3600 IN NSEC3 1 1 12 aabbccdd r53bq7cc2uvmubfu5ocmm6pers9tk9en A RRSIG
-
-;; (Secure) delegation data; Delegation with DS record
-signed-delegation.example.com. 3600 IN NS ns.example.net.
-signed-delegation.example.com. 3600 IN DS 12345 8 2 764501411DE58E8618945054A3F620B36202E115D015A7773F4B78E0F952CECA
-
-;; (Secure) delegation data; Delegation without DS record (and both NSEC
-;; and NSEC3 denying its existence)
-unsigned-delegation.example.com. 3600 IN NS ns.example.net.
-unsigned-delegation.example.com. 3600 IN NSEC unsigned-delegation-optout.example.com. NS RRSIG NSEC
-;; This one will be added on demand
-q81r598950igr1eqvc60aedlq66425b5.example.com. 3600 IN NSEC3 1 1 12 aabbccdd 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom NS RRSIG
-
-;; Delegation without DS record, and no direct matching NSEC3 record
-unsigned-delegation-optout.example.com. 3600 IN NS ns.example.net.
-unsigned-delegation-optout.example.com. 3600 IN NSEC *.uwild.example.com. NS RRSIG NSEC
-
-;; (Secure) delegation data; Delegation where the DS lookup will raise an
-;; exception.
-bad-delegation.example.com. 3600 IN NS ns.example.net.
-
-;; Delegation from an unsigned parent.  There's no DS, and there's no NSEC
-;; or NSEC3 that proves it.
-nosec-delegation.example.com. 3600 IN NS ns.nosec.example.net.
diff --git a/src/bin/auth/tests/testdata/example.zone.in b/src/bin/auth/tests/testdata/example.zone.in
new file mode 100644
index 0000000..608c09f
--- /dev/null
+++ b/src/bin/auth/tests/testdata/example.zone.in
@@ -0,0 +1,6 @@
+;;
+;; This is a complete (but crafted and somewhat broken) zone file used
+;; in query tests, excluding NSEC3 records.
+;;
+
+$INCLUDE @abs_builddir@/example-base.zone
diff --git a/src/bin/bind10/bind10.xml b/src/bin/bind10/bind10.xml
index 2ce86ab..e32544a 100644
--- a/src/bin/bind10/bind10.xml
+++ b/src/bin/bind10/bind10.xml
@@ -160,7 +160,7 @@
 	    <citerefentry><refentrytitle>b10-msgq</refentrytitle><manvolnum>8</manvolnum></citerefentry>
             daemon to use.
             The default is
-            <filename>/usr/local/var/bind10-devel/msg_socket</filename>.
+            <filename>/usr/local/var/bind10/msg_socket</filename>.
 <!-- @localstatedir@/@PACKAGE_NAME@/msg_socket -->
            </para>
          </listitem>
diff --git a/src/bin/bind10/bind10_messages.mes b/src/bin/bind10/bind10_messages.mes
index aba8da5..36a4422 100644
--- a/src/bin/bind10/bind10_messages.mes
+++ b/src/bin/bind10/bind10_messages.mes
@@ -154,7 +154,7 @@ The boss module received the given signal.
 % BIND10_RESTART_COMPONENT_SKIPPED Skipped restarting a component %1
 The boss module tried to restart a component after it failed (crashed)
 unexpectedly, but the boss then found that the component had been removed
-from its local configuration of components to run.  This is an unusal
+from its local configuration of components to run.  This is an unusual
 situation but can happen if the administrator removes the component from
 the configuration after the component's crash and before the restart time.
 The boss module simply skipped restarting that module, and the whole system
@@ -262,7 +262,7 @@ indicated OS API function with given error.
 The boss forwards a request for a socket to the socket creator.
 
 % BIND10_STARTED_CC started configuration/command session
-Debug message given when BIND 10 has successfull started the object that
+Debug message given when BIND 10 has successfully started the object that
 handles configuration and commands.
 
 % BIND10_STARTED_PROCESS started %1
diff --git a/src/bin/cfgmgr/b10-cfgmgr.xml b/src/bin/cfgmgr/b10-cfgmgr.xml
index ff5706c..d8688f9 100644
--- a/src/bin/cfgmgr/b10-cfgmgr.xml
+++ b/src/bin/cfgmgr/b10-cfgmgr.xml
@@ -136,7 +136,7 @@
   <refsect1>
     <title>FILES</title>
 <!-- TODO: fix path -->
-    <para><filename>/usr/local/var/bind10-devel/b10-config.db</filename>
+    <para><filename>/usr/local/var/bind10/b10-config.db</filename>
       — Configuration storage file.
     </para>
   </refsect1>
diff --git a/src/bin/cmdctl/b10-certgen.xml b/src/bin/cmdctl/b10-certgen.xml
index 1e3c8e3..5ac8783 100644
--- a/src/bin/cmdctl/b10-certgen.xml
+++ b/src/bin/cmdctl/b10-certgen.xml
@@ -190,7 +190,7 @@
       To update an expired certificate in BIND 10 that has been installed to
       /usr/local:
       <screen>
-$> cd /usr/local/etc/bind10-devel/
+$> cd /usr/local/etc/bind10/
 
 $> b10-certgen
 cmdctl-certfile.pem failed to verify: certificate has expired
diff --git a/src/bin/cmdctl/b10-cmdctl.xml b/src/bin/cmdctl/b10-cmdctl.xml
index e01d5a2..4b1b32f 100644
--- a/src/bin/cmdctl/b10-cmdctl.xml
+++ b/src/bin/cmdctl/b10-cmdctl.xml
@@ -147,21 +147,21 @@
       <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>.
+      <filename>/usr/local/etc/bind10/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>.
+      <filename>/usr/local/etc/bind10/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>.
+      <filename>/usr/local/etc/bind10/cmdctl-keyfile.pem</filename>.
     </para>
 
 <!-- TODO: formating -->
@@ -187,17 +187,17 @@
 <!-- TODO: permissions -->
 <!-- TODO: what about multiple accounts? -->
 <!-- TODO: shouldn't the password file name say cmdctl in it? -->
-    <para><filename>/usr/local/etc/bind10-devel/cmdctl-accounts.csv</filename>
+    <para><filename>/usr/local/etc/bind10/cmdctl-accounts.csv</filename>
       — account database containing the name, hashed password,
       and the salt.
     </para>
 <!-- TODO: replace /usr/local -->
 <!-- TODO: permissions -->
 <!-- TODO: shouldn't have both in same file, will be configurable -->
-    <para><filename>/usr/local/etc/bind10-devel/cmdctl-keyfile.pem</filename>
+    <para><filename>/usr/local/etc/bind10/cmdctl-keyfile.pem</filename>
       — contains the Private key.
     </para>
-    <para><filename>/usr/local/etc/bind10-devel/cmdctl-certfile.pem</filename>
+    <para><filename>/usr/local/etc/bind10/cmdctl-certfile.pem</filename>
       — contains the Certificate.
     </para>
   </refsect1>
diff --git a/src/bin/dbutil/dbutil_messages.mes b/src/bin/dbutil/dbutil_messages.mes
index 90ede92..2fbd000 100644
--- a/src/bin/dbutil/dbutil_messages.mes
+++ b/src/bin/dbutil/dbutil_messages.mes
@@ -53,7 +53,7 @@ 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
+Debug message; the given SQL statement is executed.
 
 % DBUTIL_FILE Database file: %1
 The database file that is being checked.
@@ -67,7 +67,7 @@ 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
+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.
@@ -95,7 +95,7 @@ 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
+database schema. The error is shown in the message.
 
 % DBUTIL_UPGRADE_SUCCESFUL database upgrade successfully completed
 The database schema update was completed successfully.
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index c896591..28f08f7 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -58,6 +58,7 @@ b10_dhcp4_CXXFLAGS = -Wno-unused-parameter
 endif
 
 b10_dhcp4_LDADD  = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+b10_dhcp4_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
@@ -65,6 +66,5 @@ b10_dhcp4_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 
-
 b10_dhcp4dir = $(pkgdatadir)
 b10_dhcp4_DATA = dhcp4.spec
diff --git a/src/bin/dhcp4/config_parser.cc b/src/bin/dhcp4/config_parser.cc
index 08d16f8..76181c2 100644
--- a/src/bin/dhcp4/config_parser.cc
+++ b/src/bin/dhcp4/config_parser.cc
@@ -13,9 +13,12 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config/ccsession.h>
-#include <dhcpsrv/cfgmgr.h>
 #include <dhcp4/config_parser.h>
 #include <dhcp4/dhcp4_log.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option_definition.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <util/encode/hex.h>
 #include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/algorithm/string.hpp>
@@ -46,12 +49,20 @@ typedef std::map<std::string, ParserFactory*> FactoryMap;
 /// no subnet object created yet to store them.
 typedef std::vector<Pool4Ptr> PoolStorage;
 
+/// @brief Collection of option descriptors. This container allows searching for
+/// options using the option code or persistency flag. This is useful when merging
+/// existing options with newly configured options.
+typedef Subnet::OptionContainer OptionStorage;
+
 /// @brief Global uint32 parameters that will be used as defaults.
 Uint32Storage uint32_defaults;
 
 /// @brief global string parameters that will be used as defaults.
 StringStorage string_defaults;
 
+/// @brief Global storage for options that will be used as defaults.
+OptionStorage option_defaults;
+
 /// @brief a dummy configuration parser
 ///
 /// It is a debugging parser. It does not configure anything,
@@ -451,6 +462,344 @@ private:
     PoolStorage* pools_;
 };
 
+/// @brief Parser for option data value.
+///
+/// This parser parses configuration entries that specify value of
+/// a single option. These entries include option name, option code
+/// and data carried by the option. If parsing is successful then an
+/// instance of an option is created and added to the storage provided
+/// by the calling class.
+///
+/// @todo This class parses and validates the option name. However it is
+/// not used anywhere until support for option spaces is implemented
+/// (see tickets #2319, #2314). When option spaces are implemented
+/// there will be a way to reference the particular option using
+/// its type (code) or option name.
+class OptionDataParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Class constructor.
+    OptionDataParser(const std::string&)
+        : options_(NULL),
+          // initialize option to NULL ptr
+          option_descriptor_(false) { }
+
+    /// @brief Parses the single option data.
+    ///
+    /// This method parses the data of a single option from the configuration.
+    /// The option data includes option name, option code and data being
+    /// carried by this option. Eventually it creates the instance of the
+    /// option.
+    ///
+    /// @warning setStorage must be called with valid storage pointer prior
+    /// to calling this method.
+    ///
+    /// @param option_data_entries collection of entries that define value
+    /// for a particular option.
+    /// @throw Dhcp4ConfigError if invalid parameter specified in
+    /// the configuration.
+    /// @throw isc::InvalidOperation if failed to set storage prior to
+    /// calling build.
+    /// @throw isc::BadValue if option data storage is invalid.
+    virtual void build(ConstElementPtr option_data_entries) {
+        if (options_ == NULL) {
+            isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
+                      "parsing option data.");
+        }
+        BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
+            ParserPtr parser;
+            if (param.first == "name") {
+                boost::shared_ptr<StringParser>
+                    name_parser(dynamic_cast<StringParser*>(StringParser::Factory(param.first)));
+                if (name_parser) {
+                    name_parser->setStorage(&string_values_);
+                    parser = name_parser;
+                }
+            } else if (param.first == "code") {
+                boost::shared_ptr<Uint32Parser>
+                    code_parser(dynamic_cast<Uint32Parser*>(Uint32Parser::Factory(param.first)));
+                if (code_parser) {
+                    code_parser->setStorage(&uint32_values_);
+                    parser = code_parser;
+                }
+            } else if (param.first == "data") {
+                boost::shared_ptr<StringParser>
+                    value_parser(dynamic_cast<StringParser*>(StringParser::Factory(param.first)));
+                if (value_parser) {
+                    value_parser->setStorage(&string_values_);
+                    parser = value_parser;
+                }
+            } else {
+                isc_throw(Dhcp4ConfigError,
+                          "Parser error: option-data parameter not supported: "
+                          << param.first);
+            }
+            parser->build(param.second);
+        }
+        // Try to create the option instance.
+        createOption();
+    }
+
+    /// @brief Commits option value.
+    ///
+    /// This function adds a new option to the storage or replaces an existing option
+    /// with the same code.
+    ///
+    /// @throw isc::InvalidOperation if failed to set pointer to storage or failed
+    /// to call build() prior to commit. If that happens data in the storage
+    /// remain un-modified.
+    virtual void commit() {
+        if (options_ == NULL) {
+            isc_throw(isc::InvalidOperation, "Parser logic error: storage must be set before "
+                      "commiting option data.");
+        } else  if (!option_descriptor_.option) {
+            // Before we can commit the new option should be configured. If it is not
+            // than somebody must have called commit() before build().
+            isc_throw(isc::InvalidOperation, "Parser logic error: no option has been configured and"
+                      " thus there is nothing to commit. Has build() been called?");
+        }
+        uint16_t opt_type = option_descriptor_.option->getType();
+        Subnet::OptionContainerTypeIndex& idx = options_->get<1>();
+        // Try to find options with the particular option code in the main
+        // storage. If found, remove these options because they will be
+        // replaced with new one.
+        Subnet::OptionContainerTypeRange range =
+            idx.equal_range(opt_type);
+        if (std::distance(range.first, range.second) > 0) {
+            idx.erase(range.first, range.second);
+        }
+        // Append new option to the main storage.
+        options_->push_back(option_descriptor_);
+    }
+
+    /// @brief Set storage for the parser.
+    ///
+    /// Sets storage for the parser. This storage points to the
+    /// vector of options and is used by multiple instances of
+    /// OptionDataParser. Each instance creates exactly one object
+    /// of dhcp::Option or derived type and appends it to this
+    /// storage.
+    ///
+    /// @param storage pointer to the options storage
+    void setStorage(OptionStorage* storage) {
+        options_ = storage;
+    }
+
+private:
+
+    /// @brief Create option instance.
+    ///
+    /// Creates an instance of an option and adds it to the provided
+    /// options storage. If the option data parsed by \ref build function
+    /// are invalid or insufficient this function emits an exception.
+    ///
+    /// @warning this function does not check if options_ storage pointer
+    /// is intitialized but this check is not needed here because it is done
+    /// in the \ref build function.
+    ///
+    /// @throw Dhcp4ConfigError if parameters provided in the configuration
+    /// are invalid.
+    void createOption() {
+        // Option code is held in the uint32_t storage but is supposed to
+        // be uint16_t value. We need to check that value in the configuration
+        // does not exceed range of uint16_t and is not zero.
+        uint32_t option_code = getUint32Param("code");
+        if (option_code == 0) {
+            isc_throw(Dhcp4ConfigError, "Parser error: value of 'code' must not"
+                      << " be equal to zero. Option code '0' is reserved in"
+                      << " DHCPv4.");
+        } else if (option_code > std::numeric_limits<uint16_t>::max()) {
+            isc_throw(Dhcp4ConfigError, "Parser error: value of 'code' must not"
+                      << " exceed " << std::numeric_limits<uint16_t>::max());
+        }
+        // Check that the option name has been specified, is non-empty and does not
+        // contain spaces.
+        // @todo possibly some more restrictions apply here?
+        std::string option_name = getStringParam("name");
+        if (option_name.empty()) {
+            isc_throw(Dhcp4ConfigError, "Parser error: option name must not be"
+                      << " empty");
+        } else if (option_name.find(" ") != std::string::npos) {
+            isc_throw(Dhcp4ConfigError, "Parser error: option name must not contain"
+                      << " spaces");
+        }
+
+        // Get option data from the configuration database ('data' field).
+        // Option data is specified by the user as case insensitive string
+        // of hexadecimal digits for each option.
+        std::string option_data = getStringParam("data");
+        // Transform string of hexadecimal digits into binary format.
+        std::vector<uint8_t> binary;
+        try {
+            util::encode::decodeHex(option_data, binary);
+        } catch (...) {
+            isc_throw(Dhcp4ConfigError, "Parser error: option data is not a valid"
+                      << " string of hexadecimal digits: " << option_data);
+        }
+        // Get all existing DHCPv4 option definitions. The one that matches
+        // our option will be picked and used to create it.
+        OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V4);
+        // Get search index #1. It allows searching for options definitions
+        // using option type value.
+        const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+        // Get all option definitions matching option code we want to create.
+        const OptionDefContainerTypeRange& range = idx.equal_range(option_code);
+        size_t num_defs = std::distance(range.first, range.second);
+        OptionPtr option;
+        // Currently we do not allow duplicated definitions and if there are
+        // any duplicates we issue internal server error.
+        if (num_defs > 1) {
+            isc_throw(Dhcp4ConfigError, "Internal error: currently it is not"
+                      << " supported to initialize multiple option definitions"
+                      << " for the same option code. This will be supported once"
+                      << " there option spaces are implemented.");
+        } else if (num_defs == 0) {
+            // @todo We have a limited set of option definitions intiialized at the moment.
+            // In the future we want to initialize option definitions for all options.
+            // Consequently an error will be issued if an option definition does not exist
+            // for a particular option code. For now it is ok to create generic option
+            // if definition does not exist.
+            OptionPtr option(new Option(Option::V4, static_cast<uint16_t>(option_code),
+                                        binary));
+            // The created option is stored in option_descriptor_ class member until the
+            // commit stage when it is inserted into the main storage. If an option with the
+            // same code exists in main storage already the old option is replaced.
+            option_descriptor_.option = option;
+            option_descriptor_.persistent = false;
+        } else {
+            // We have exactly one option definition for the particular option code
+            // use it to create the option instance.
+            const OptionDefinitionPtr& def = *(range.first);
+            try {
+                OptionPtr option = def->optionFactory(Option::V4, option_code, binary);
+                Subnet::OptionDescriptor desc(option, false);
+                option_descriptor_.option = option;
+                option_descriptor_.persistent = false;
+            } catch (const isc::Exception& ex) {
+                isc_throw(Dhcp4ConfigError, "Parser error: option data does not match"
+                          << " option definition (code " << option_code << "): "
+                          << ex.what());
+            }
+        }
+    }
+
+    /// @brief Get a parameter from the strings storage.
+    ///
+    /// @param param_id parameter identifier.
+    /// @throw Dhcp4ConfigError if parameter has not been found.
+    std::string getStringParam(const std::string& param_id) const {
+        StringStorage::const_iterator param = string_values_.find(param_id);
+        if (param == string_values_.end()) {
+            isc_throw(Dhcp4ConfigError, "Parser error: option-data parameter"
+                      << " '" << param_id << "' not specified");
+        }
+        return (param->second);
+    }
+
+    /// @brief Get a parameter from the uint32 values storage.
+    ///
+    /// @param param_id parameter identifier.
+    /// @throw Dhcp4ConfigError if parameter has not been found.
+    uint32_t getUint32Param(const std::string& param_id) const {
+        Uint32Storage::const_iterator param = uint32_values_.find(param_id);
+        if (param == uint32_values_.end()) {
+            isc_throw(Dhcp4ConfigError, "Parser error: option-data parameter"
+                      << " '" << param_id << "' not specified");
+        }
+        return (param->second);
+    }
+
+    /// Storage for uint32 values (e.g. option code).
+    Uint32Storage uint32_values_;
+    /// Storage for string values (e.g. option name or data).
+    StringStorage string_values_;
+    /// Pointer to options storage. This storage is provided by
+    /// the calling class and is shared by all OptionDataParser objects.
+    OptionStorage* options_;
+    /// Option descriptor holds newly configured option.
+    Subnet::OptionDescriptor option_descriptor_;
+};
+
+/// @brief Parser for option data values within a subnet.
+///
+/// This parser iterates over all entries that define options
+/// data for a particular subnet and creates a collection of options.
+/// If parsing is successful, all these options are added to the Subnet
+/// object.
+class OptionDataListParser : public Dhcp4ConfigParser {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Unless otherwise specified, parsed options will be stored in
+    /// a global option container (option_default). That storage location
+    /// is overriden on a subnet basis.
+    OptionDataListParser(const std::string&)
+        : options_(&option_defaults), local_options_() { }
+
+    /// @brief Parses entries that define options' data for a subnet.
+    ///
+    /// This method iterates over all entries that define option data
+    /// for options within a single subnet and creates options' instances.
+    ///
+    /// @param option_data_list pointer to a list of options' data sets.
+    /// @throw Dhcp4ConfigError if option parsing failed.
+    void build(ConstElementPtr option_data_list) {
+        BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
+            boost::shared_ptr<OptionDataParser> parser(new OptionDataParser("option-data"));
+            // options_ member will hold instances of all options thus
+            // each OptionDataParser takes it as a storage.
+            parser->setStorage(&local_options_);
+            // Build the instance of a single option.
+            parser->build(option_value);
+            // Store a parser as it will be used to commit.
+            parsers_.push_back(parser);
+        }
+    }
+
+    /// @brief Set storage for option instances.
+    ///
+    /// @param storage pointer to options storage.
+    void setStorage(OptionStorage* storage) {
+        options_ = storage;
+    }
+
+
+    /// @brief Commit all option values.
+    ///
+    /// This function invokes commit for all option values.
+    void commit() {
+        BOOST_FOREACH(ParserPtr parser, parsers_) {
+            parser->commit();
+        }
+        // Parsing was successful and we have all configured
+        // options in local storage. We can now replace old values
+        // with new values.
+        std::swap(local_options_, *options_);
+    }
+
+    /// @brief Create DhcpDataListParser object
+    ///
+    /// @param param_name param name.
+    ///
+    /// @return DhcpConfigParser object.
+    static Dhcp4ConfigParser* Factory(const std::string& param_name) {
+        return (new OptionDataListParser(param_name));
+    }
+
+    /// Intermediate option storage. This storage is used by
+    /// lower level parsers to add new options.  Values held
+    /// in this storage are assigned to main storage (options_)
+    /// if overall parsing was successful.
+    OptionStorage local_options_;
+    /// Pointer to options instances storage.
+    OptionStorage* options_;
+    /// Collection of parsers;
+    ParserCollection parsers_;
+};
+
 /// @brief this class parses a single subnet
 ///
 /// This class parses the whole subnet definition. It creates parsers
@@ -470,35 +819,31 @@ public:
     void build(ConstElementPtr subnet) {
 
         BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
-
             ParserPtr parser(createSubnet4ConfigParser(param.first));
-
-            // if this is an Uint32 parser, tell it to store the values
-            // in values_, rather than in global storage
-            boost::shared_ptr<Uint32Parser> uint_parser =
-                boost::dynamic_pointer_cast<Uint32Parser>(parser);
-            if (uint_parser) {
-                uint_parser->setStorage(&uint32_values_);
-            } else {
-
-                boost::shared_ptr<StringParser> string_parser =
-                    boost::dynamic_pointer_cast<StringParser>(parser);
-                if (string_parser) {
-                    string_parser->setStorage(&string_values_);
-                } else {
-
-                    boost::shared_ptr<PoolParser> pool_parser =
-                        boost::dynamic_pointer_cast<PoolParser>(parser);
-                    if (pool_parser) {
-                        pool_parser->setStorage(&pools_);
-                    }
-                }
+            // The actual type of the parser is unknown here. We have to discover
+            // the parser type here to invoke the corresponding setStorage function
+            // on it.  We discover parser type by trying to cast the parser to various
+            // parser types and checking which one was successful. For this one
+            // a setStorage and build methods are invoked.
+
+            // Try uint32 type parser.
+            if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
+                                                          param.second) &&
+                // Try string type parser.
+                !buildParser<StringParser, StringStorage >(parser, string_values_,
+                                                           param.second) &&
+                // Try pool parser.
+                !buildParser<PoolParser, PoolStorage >(parser, pools_,
+                                                       param.second) &&
+                // Try option data parser.
+                !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
+                                                                   param.second)) {
+                // Appropriate parsers are created in the createSubnet6ConfigParser
+                // and they should be limited to those that we check here for. Thus,
+                // if we fail to find a matching parser here it is a programming error.
+                isc_throw(Dhcp4ConfigError, "failed to find suitable parser");
             }
-
-            parser->build(param.second);
-            parsers_.push_back(parser);
         }
-
         // Ok, we now have subnet parsed
     }
 
@@ -510,6 +855,10 @@ public:
     /// objects. Subnet4 are then added to DHCP CfgMgr.
     /// @throw Dhcp4ConfigError if there are any issues encountered during commit
     void commit() {
+        // Invoke commit on all sub-data parsers.
+        BOOST_FOREACH(ParserPtr parser, parsers_) {
+            parser->commit();
+        }
 
         StringStorage::const_iterator it = string_values_.find("subnet");
         if (it == string_values_.end()) {
@@ -545,11 +894,79 @@ public:
             subnet->addPool4(*it);
         }
 
+        const Subnet::OptionContainer& options = subnet->getOptions();
+        const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+        // Add subnet specific options.
+        BOOST_FOREACH(Subnet::OptionDescriptor desc, options_) {
+            Subnet::OptionContainerTypeRange range = idx.equal_range(desc.option->getType());
+            if (std::distance(range.first, range.second) > 0) {
+                LOG_WARN(dhcp4_logger, DHCP4_CONFIG_OPTION_DUPLICATE)
+                    .arg(desc.option->getType()).arg(addr.toText());
+            }
+            subnet->addOption(desc.option);
+        }
+
+        // Check all global options and add them to the subnet object if
+        // they have been configured in the global scope. If they have been
+        // configured in the subnet scope we don't add global option because
+        // the one configured in the subnet scope always takes precedence.
+        BOOST_FOREACH(Subnet::OptionDescriptor desc, option_defaults) {
+            // Get all options specified locally in the subnet and having
+            // code equal to global option's code.
+            Subnet::OptionContainerTypeRange range = idx.equal_range(desc.option->getType());
+            // @todo: In the future we will be searching for options using either
+            // an option code or namespace. Currently we have only the option
+            // code available so if there is at least one option found with the
+            // specific code we don't add the globally configured option.
+            // @todo with this code the first globally configured option
+            // with the given code will be added to a subnet. We may
+            // want to issue a warning about dropping the configuration of
+            // a global option if one already exsists.
+            if (std::distance(range.first, range.second) == 0) {
+                subnet->addOption(desc.option);
+            }
+        }
+
         CfgMgr::instance().addSubnet4(subnet);
     }
 
 private:
 
+    /// @brief Set storage for a parser and invoke build.
+    ///
+    /// This helper method casts the provided parser pointer to the specified
+    /// type. If the cast is successful it sets the corresponding storage for
+    /// this parser, invokes build on it and saves the parser.
+    ///
+    /// @tparam T parser type to which parser argument should be cast.
+    /// @tparam Y storage type for the specified parser type.
+    /// @param parser parser on which build must be invoked.
+    /// @param storage reference to a storage that will be set for a parser.
+    /// @param subnet subnet element read from the configuration and being parsed.
+    /// @return true if parser pointer was successfully cast to specialized
+    /// parser type provided as Y.
+    template<typename T, typename Y>
+    bool buildParser(const ParserPtr& parser, Y& storage, const ConstElementPtr& subnet) {
+        // We need to cast to T in order to set storage for the parser.
+        boost::shared_ptr<T> cast_parser = boost::dynamic_pointer_cast<T>(parser);
+        // It is common that this cast is not successful because we try to cast to all
+        // supported parser types as we don't know the type of a parser in advance.
+        if (cast_parser) {
+            // Cast, successful so we go ahead with setting storage and actual parse.
+            cast_parser->setStorage(&storage);
+            parser->build(subnet);
+            parsers_.push_back(parser);
+            // We indicate that cast was successful so as the calling function
+            // may skip attempts to cast to other parser types and proceed to
+            // next element.
+            return (true);
+        }
+        // It was not successful. Indicate that another parser type
+        // should be tried.
+        return (false);
+    }
+
     /// @brief creates parsers for entries in subnet definition
     ///
     /// @todo Add subnet-specific things here (e.g. subnet-specific options)
@@ -565,6 +982,7 @@ private:
         factories["rebind-timer"] = Uint32Parser::Factory;
         factories["subnet"] = StringParser::Factory;
         factories["pool"] = PoolParser::Factory;
+        factories["option-data"] = OptionDataListParser::Factory;
 
         FactoryMap::iterator f = factories.find(config_id);
         if (f == factories.end()) {
@@ -620,6 +1038,9 @@ private:
     /// storage for pools belonging to this subnet
     PoolStorage pools_;
 
+    /// storage for options belonging to this subnet
+    OptionStorage options_;
+
     /// parsers are stored here
     ParserCollection parsers_;
 };
@@ -650,7 +1071,6 @@ public:
         // used: Subnet4ConfigParser
 
         BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
-
             ParserPtr parser(new Subnet4ConfigParser("subnet"));
             parser->build(subnet);
             subnets_.push_back(parser);
@@ -702,6 +1122,7 @@ Dhcp4ConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id) {
     factories["rebind-timer"] = Uint32Parser::Factory;
     factories["interface"] = InterfaceListConfigParser::Factory;
     factories["subnet4"] = Subnets4ListConfigParser::Factory;
+    factories["option-data"] = OptionDataListParser::Factory;
     factories["version"] = StringParser::Factory;
 
     FactoryMap::iterator f = factories.find(config_id);
@@ -739,7 +1160,7 @@ configureDhcp4Server(Dhcpv4Srv& , ConstElementPtr config_set) {
         }
     } catch (const isc::Exception& ex) {
         ConstElementPtr answer = isc::config::createAnswer(1,
-                                 string("Configuration parsing failed:") + ex.what());
+                                 string("Configuration parsing failed: ") + ex.what());
         return (answer);
     } catch (...) {
         // for things like bad_cast in boost::lexical_cast
@@ -754,7 +1175,7 @@ configureDhcp4Server(Dhcpv4Srv& , ConstElementPtr config_set) {
     }
     catch (const isc::Exception& ex) {
         ConstElementPtr answer = isc::config::createAnswer(2,
-                                 string("Configuration commit failed:") + ex.what());
+                                 string("Configuration commit failed: ") + ex.what());
         return (answer);
     } catch (...) {
         // for things like bad_cast in boost::lexical_cast
diff --git a/src/bin/dhcp4/config_parser.h b/src/bin/dhcp4/config_parser.h
index 2ee9cf6..cc4c690 100644
--- a/src/bin/dhcp4/config_parser.h
+++ b/src/bin/dhcp4/config_parser.h
@@ -14,6 +14,7 @@
 
 #include <exceptions/exceptions.h>
 #include <cc/data.h>
+#include <stdint.h>
 #include <string>
 
 #ifndef DHCP4_CONFIG_PARSER_H
diff --git a/src/bin/dhcp4/dhcp4.spec b/src/bin/dhcp4/dhcp4.spec
index d5066e8..46192de 100644
--- a/src/bin/dhcp4/dhcp4.spec
+++ b/src/bin/dhcp4/dhcp4.spec
@@ -34,6 +34,37 @@
         "item_default": 4000
       },
 
+      { "item_name": "option-data",
+        "item_type": "list",
+        "item_optional": false,
+        "item_default": [],
+        "list_item_spec":
+        {
+          "item_name": "single-option-data",
+          "item_type": "map",
+          "item_optional": false,
+          "item_default": {},
+          "map_item_spec": [
+          {
+            "item_name": "name",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
+          },
+
+          { "item_name": "code",
+            "item_type": "integer",
+            "item_optional": false,
+            "item_default": 0
+          },
+          { "item_name": "data",
+            "item_type": "string",
+            "item_optional": false,
+            "item_default": ""
+          } ]
+        }
+      },
+
       { "item_name": "subnet4",
         "item_type": "list",
         "item_optional": false,
@@ -80,9 +111,40 @@
                         "item_optional": false,
                         "item_default": ""
                     }
-                }
-            ]
-        }
+                },
+
+                { "item_name": "option-data",
+                  "item_type": "list",
+                  "item_optional": false,
+                  "item_default": [],
+                  "list_item_spec":
+                  {
+                    "item_name": "single-option-data",
+                    "item_type": "map",
+                    "item_optional": false,
+                    "item_default": {},
+                    "map_item_spec": [
+                    {
+                      "item_name": "name",
+                      "item_type": "string",
+                      "item_optional": false,
+                      "item_default": ""
+                    },
+                    {
+                      "item_name": "code",
+                      "item_type": "integer",
+                      "item_optional": false,
+                      "item_default": 0
+                    },
+                    {
+                      "item_name": "data",
+                      "item_type": "string",
+                      "item_optional": false,
+                      "item_default": ""
+                    } ]
+                  }
+                } ]
+         }
       }
     ],
     "commands": [
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index 994f004..8724f8d 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -26,29 +26,34 @@ to establish a session with the BIND 10 control channel.
 A debug message listing the command (and possible arguments) received
 from the BIND 10 control system by the IPv4 DHCP server.
 
+% DHCP4_CONFIG_COMPLETE DHCPv4 server has completed configuration: %1
+This is an informational message announcing the successful processing of a
+new configuration. it is output during server startup, and when an updated
+configuration is committed by the administrator.  Additional information
+may be provided.
+
 % DHCP4_CONFIG_LOAD_FAIL failed to load configuration: %1
 This critical error message indicates that the initial DHCPv4
 configuration has failed. The server will start, but nothing will be
 served until the configuration has been corrected.
 
-% DHCP4_CONFIG_UPDATE updated configuration received: %1
-A debug message indicating that the IPv4 DHCP server has received an
-updated configuration from the BIND 10 configuration system.
+% DHCP4_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
+This is an informational message reporting that the configuration has
+been extended to include the specified IPv4 subnet.
 
 % DHCP4_CONFIG_START DHCPv4 server is processing the following configuration: %1
 This is a debug message that is issued every time the server receives a
 configuration. That happens at start up and also when a server configuration
 change is committed by the administrator.
 
-% DHCP4_CONFIG_NEW_SUBNET A new subnet has been added to configuration: %1
-This is an informational message reporting that the configuration has
-been extended to include the specified IPv4 subnet.
+% DHCP4_CONFIG_UPDATE updated configuration received: %1
+A debug message indicating that the IPv4 DHCP server has received an
+updated configuration from the BIND 10 configuration system.
 
-% DHCP4_CONFIG_COMPLETE DHCPv4 server has completed configuration: %1
-This is an informational message announcing the successful processing of a
-new configuration. it is output during server startup, and when an updated
-configuration is committed by the administrator.  Additional information
-may be provided.
+% DHCP4_CONFIG_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
+This warning message is issued on an attempt to configure multiple options with the
+same option code for the particular subnet. Adding multiple options is uncommon
+for DHCPv4, but it is not prohibited.
 
 % DHCP4_NOT_RUNNING IPv4 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
@@ -70,7 +75,7 @@ may well be a valid DHCP packet, just a type not expected by the server
 
 % DHCP4_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
 The IPv4 DHCP server tried to receive a packet but an error
-occured during this attempt. The reason for the error is included in
+occurred during this attempt. The reason for the error is included in
 the message.
 
 % DHCP4_PACKET_SEND_FAIL failed to send DHCPv4 packet: %1
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index e601919..c0ebcb9 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -66,13 +66,13 @@ dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp4_unittests_LDADD = $(GTEST_LDADD)
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 dhcp4_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
-dhcp4_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+dhcp4_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc
index a4ccabd..3dd75d7 100644
--- a/src/bin/dhcp4/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp4/tests/config_parser_unittest.cc
@@ -22,6 +22,7 @@
 #include <config/ccsession.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/cfgmgr.h>
+#include <boost/foreach.hpp>
 #include <iostream>
 #include <fstream>
 #include <sstream>
@@ -73,9 +74,188 @@ public:
     }
 
     ~Dhcp4ParserTest() {
+        resetConfiguration();
         delete srv_;
     };
 
+    /// @brief Create the simple configuration with single option.
+    ///
+    /// This function allows to set one of the parameters that configure
+    /// option value. These parameters are: "name", "code" and "data".
+    ///
+    /// @param param_value string holiding option parameter value to be
+    /// injected into the configuration string.
+    /// @param parameter name of the parameter to be configured with
+    /// param value.
+    /// @return configuration string containing custom values of parameters
+    /// describing an option.
+    std::string createConfigWithOption(const std::string& param_value,
+                                       const std::string& parameter) {
+        std::map<std::string, std::string> params;
+        if (parameter == "name") {
+            params["name"] = param_value;
+            params["code"] = "56";
+            params["data"] = "AB CDEF0105";
+        } else if (parameter == "code") {
+            params["name"] = "option_foo";
+            params["code"] = param_value;
+            params["data"] = "AB CDEF0105";
+        } else if (parameter == "data") {
+            params["name"] = "option_foo";
+            params["code"] = "56";
+            params["data"] = param_value;
+        }
+        return (createConfigWithOption(params));
+    }
+
+    /// @brief Create simple configuration with single option.
+    ///
+    /// This function creates a configuration for a single option with
+    /// custom values for all parameters that describe the option.
+    ///
+    /// @params params map holding parameters and their values.
+    /// @return configuration string containing custom values of parameters
+    /// describing an option.
+    std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
+        std::ostringstream stream;
+        stream << "{ \"interface\": [ \"all\" ],"
+            "\"rebind-timer\": 2000, "
+            "\"renew-timer\": 1000, "
+            "\"subnet4\": [ { "
+            "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+            "    \"subnet\": \"192.0.2.0/24\", "
+            "    \"option-data\": [ {";
+        bool first = true;
+        typedef std::pair<std::string, std::string> ParamPair;
+        BOOST_FOREACH(ParamPair param, params) {
+            if (!first) {
+                stream << ", ";
+            } else {
+                // cppcheck-suppress unreadVariable
+                first = false;
+            }
+            if (param.first == "name") {
+                stream << "\"name\": \"" << param.second << "\"";
+            } else if (param.first == "code") {
+                stream << "\"code\": " << param.second << "";
+            } else if (param.first == "data") {
+                stream << "\"data\": \"" << param.second << "\"";
+            }
+        }
+        stream <<
+            "        } ]"
+            " } ],"
+            "\"valid-lifetime\": 4000 }";
+        return (stream.str());
+    }
+
+    /// @brief Test invalid option parameter value.
+    ///
+    /// This test function constructs the simple configuration
+    /// string and injects invalid option configuration into it.
+    /// It expects that parser will fail with provided option code.
+    ///
+    /// @param param_value string holding invalid option parameter value
+    /// to be injected into configuration string.
+    /// @param parameter name of the parameter to be configured with
+    /// param_value (can be any of "name", "code", "data")
+    void testInvalidOptionParam(const std::string& param_value,
+                                const std::string& parameter) {
+        ConstElementPtr x;
+        std::string config = createConfigWithOption(param_value, parameter);
+        ElementPtr json = Element::fromJSON(config);
+        EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+        ASSERT_TRUE(x);
+        comment_ = parseAnswer(rcode_, x);
+        ASSERT_EQ(1, rcode_);
+    }
+
+    /// @brief Test option against given code and data.
+    ///
+    /// @param option_desc option descriptor that carries the option to
+    /// be tested.
+    /// @param expected_code expected code of the option.
+    /// @param expected_data expected data in the option.
+    /// @param expected_data_len length of the reference data.
+    /// @param extra_data if true extra data is allowed in an option
+    /// after tested data.
+    void testOption(const Subnet::OptionDescriptor& option_desc,
+                    uint16_t expected_code, const uint8_t* expected_data,
+                    size_t expected_data_len,
+                    bool extra_data = false) {
+        // Check if option descriptor contains valid option pointer.
+        ASSERT_TRUE(option_desc.option);
+        // Verify option type.
+        EXPECT_EQ(expected_code, option_desc.option->getType());
+        // We may have many different option types being created. Some of them
+        // have dedicated classes derived from Option class. In such case if
+        // we want to verify the option contents against expected_data we have
+        // to prepare raw buffer with the contents of the option. The easiest
+        // way is to call pack() which will prepare on-wire data.
+        util::OutputBuffer buf(option_desc.option->getData().size());
+        option_desc.option->pack(buf);
+        if (extra_data) {
+            // The length of the buffer must be at least equal to size of the
+            // reference data but it can sometimes be greater than that. This is
+            // because some options carry suboptions that increase the overall
+            // length.
+            ASSERT_GE(buf.getLength() - option_desc.option->getHeaderLen(),
+                      expected_data_len);
+        } else {
+            ASSERT_EQ(buf.getLength() - option_desc.option->getHeaderLen(),
+                      expected_data_len);
+        }
+        // Verify that the data is correct. Do not verify suboptions and a header.
+        const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
+        EXPECT_EQ(0, memcmp(expected_data, data + option_desc.option->getHeaderLen(),
+                            expected_data_len));
+    }
+
+    /// @brief Reset configuration database.
+    ///
+    /// This function resets configuration data base by
+    /// removing all subnets and option-data. Reset must
+    /// be performed after each test to make sure that
+    /// contents of the database do not affect result of
+    /// subsequent tests.
+    void resetConfiguration() {
+        ConstElementPtr status;
+
+        string config = "{ \"interface\": [ \"all\" ],"
+            "\"rebind-timer\": 2000, "
+            "\"renew-timer\": 1000, "
+            "\"valid-lifetime\": 4000, "
+            "\"subnet4\": [ ], "
+            "\"option-data\": [ ] }";
+
+        try {
+            ElementPtr json = Element::fromJSON(config);
+            status = configureDhcp4Server(*srv_, json);
+        } catch (const std::exception& ex) {
+            FAIL() << "Fatal error: unable to reset configuration database"
+                   << " after the test. The following configuration was used"
+                   << " to reset database: " << std::endl
+                   << config << std::endl
+                   << " and the following error message was returned:"
+                   << ex.what() << std::endl;
+        }
+
+        // status object must not be NULL
+        if (!status) {
+            FAIL() << "Fatal error: unable to reset configuration database"
+                   << " after the test. Configuration function returned"
+                   << " NULL pointer" << std::endl;
+        }
+
+        comment_ = parseAnswer(rcode_, status);
+        // returned value should be 0 (configuration success)
+        if (rcode_ != 0) {
+            FAIL() << "Fatal error: unable to reset configuration database"
+                   << " after the test. Configuration function returned"
+                   << " error code " << rcode_ << std::endl;
+        }
+    }
+
     Dhcpv4Srv* srv_;
 
     int rcode_;
@@ -248,6 +428,302 @@ TEST_F(Dhcp4ParserTest, poolPrefixLen) {
     EXPECT_EQ(4000, subnet->getValid());
 }
 
+// Goal of this test is to verify that global option
+// data is configured for the subnet if the subnet
+// configuration does not include options configuration.
+TEST_F(Dhcp4ParserTest, optionDataDefaults) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000,"
+        "\"renew-timer\": 1000,"
+        "\"option-data\": [ {"
+        "    \"name\": \"option_foo\","
+        "    \"code\": 56,"
+        "    \"data\": \"AB CDEF0105\""
+        " },"
+        " {"
+        "    \"name\": \"option_foo2\","
+        "    \"code\": 23,"
+        "    \"data\": \"01\""
+        " } ],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\""
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(2, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(56);
+    // Expect single option with the code equal to 56.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
+
+    range = idx.equal_range(23);
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // Do another round of testing with second option.
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
+}
+
+// Goal of this test is to verify options configuration
+// for a single subnet. In particular this test checks
+// that local options configuration overrides global
+// option setting.
+TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"option-data\": [ {"
+        "      \"name\": \"option_foo\","
+        "      \"code\": 56,"
+        "      \"data\": \"AB\""
+        " } ],"
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"option_foo\","
+        "          \"code\": 56,"
+        "          \"data\": \"AB CDEF0105\""
+        "        },"
+        "        {"
+        "          \"name\": \"option_foo2\","
+        "          \"code\": 23,"
+        "          \"data\": \"01\""
+        "        } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.24"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(2, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(56);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0xAB, 0xCD, 0xEF, 0x01, 0x05
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
+
+    range = idx.equal_range(23);
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    // Do another round of testing with second option.
+    const uint8_t foo2_expected[] = {
+        0x01
+    };
+    testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
+}
+
+// Goal of this test is to verify options configuration
+// for multiple subnets.
+TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
+    ConstElementPtr x;
+    string config = "{ \"interface\": [ \"all\" ],"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"option_foo\","
+        "          \"code\": 56,"
+        "          \"data\": \"0102030405060708090A\""
+        "        } ]"
+        " },"
+        " {"
+        "    \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
+        "    \"subnet\": \"192.0.3.0/24\", "
+        "    \"option-data\": [ {"
+        "          \"name\": \"option_foo2\","
+        "          \"code\": 23,"
+        "          \"data\": \"FF\""
+        "        } ]"
+        " } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet4Ptr subnet1 = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.100"));
+    ASSERT_TRUE(subnet1);
+    const Subnet::OptionContainer& options1 = subnet1->getOptions();
+    ASSERT_EQ(1, options1.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx1 = options1.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range1 =
+        idx1.equal_range(56);
+    // Expect single option with the code equal to 56.
+    ASSERT_EQ(1, std::distance(range1.first, range1.second));
+    const uint8_t foo_expected[] = {
+        0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x0A
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range1.first, 56, foo_expected, sizeof(foo_expected));
+
+    // Test another subnet in the same way.
+    Subnet4Ptr subnet2 = CfgMgr::instance().getSubnet4(IOAddress("192.0.3.102"));
+    ASSERT_TRUE(subnet2);
+    const Subnet::OptionContainer& options2 = subnet2->getOptions();
+    ASSERT_EQ(1, options2.size());
+
+    const Subnet::OptionContainerTypeIndex& idx2 = options2.get<1>();
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range2 =
+        idx2.equal_range(23);
+    ASSERT_EQ(1, std::distance(range2.first, range2.second));
+
+    const uint8_t foo2_expected[] = { 0xFF };
+    testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
+}
+
+// Verify that empty option name is rejected in the configuration.
+TEST_F(Dhcp4ParserTest, optionNameEmpty) {
+    // Empty option names not allowed.
+    testInvalidOptionParam("", "name");
+}
+
+// Verify that empty option name with spaces is rejected
+// in the configuration.
+TEST_F(Dhcp4ParserTest, optionNameSpaces) {
+    // Spaces in option names not allowed.
+    testInvalidOptionParam("option foo", "name");
+}
+
+// Verify that negative option code is rejected in the configuration.
+TEST_F(Dhcp4ParserTest, optionCodeNegative) {
+    // Check negative option code -4. This should fail too.
+    testInvalidOptionParam("-4", "code");
+}
+
+// Verify that out of bounds option code is rejected in the configuration.
+TEST_F(Dhcp4ParserTest, optionCodeNonUint8) {
+    // The valid option codes are uint16_t values so passing
+    // uint16_t maximum value incremented by 1 should result
+    // in failure.
+    testInvalidOptionParam("257", "code");
+}
+
+// Verify that zero option code is rejected in the configuration.
+TEST_F(Dhcp4ParserTest, optionCodeZero) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("0", "code");
+}
+
+// Verify that option data which contains non hexadecimal characters
+// is rejected by the configuration.
+TEST_F(Dhcp4ParserTest, optionDataInvalidChar) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("01020R", "data");
+}
+
+// Verify that option data containins '0x' prefix is rejected
+// by the configuration.
+TEST_F(Dhcp4ParserTest, optionDataUnexpectedPrefix) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("0x0102", "data");
+}
+
+// Verify that option data consisting od an odd number of
+// hexadecimal digits is rejected in the configuration.
+TEST_F(Dhcp4ParserTest, optionDataOddLength) {
+    // Option code 0 is reserved and should not be accepted
+    // by configuration parser.
+    testInvalidOptionParam("123", "data");
+}
+
+// Verify that either lower or upper case characters are allowed
+// to specify the option data.
+TEST_F(Dhcp4ParserTest, optionDataLowerCase) {
+    ConstElementPtr x;
+    std::string config = createConfigWithOption("0a0b0C0D", "data");
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
+    ASSERT_TRUE(x);
+    comment_ = parseAnswer(rcode_, x);
+    ASSERT_EQ(0, rcode_);
+
+    Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(IOAddress("192.0.2.5"));
+    ASSERT_TRUE(subnet);
+    const Subnet::OptionContainer& options = subnet->getOptions();
+    ASSERT_EQ(1, options.size());
+
+    // Get the search index. Index #1 is to search using option code.
+    const Subnet::OptionContainerTypeIndex& idx = options.get<1>();
+
+    // Get the options for specified index. Expecting one option to be
+    // returned but in theory we may have multiple options with the same
+    // code so we get the range.
+    std::pair<Subnet::OptionContainerTypeIndex::const_iterator,
+              Subnet::OptionContainerTypeIndex::const_iterator> range =
+        idx.equal_range(56);
+    // Expect single option with the code equal to 100.
+    ASSERT_EQ(1, std::distance(range.first, range.second));
+    const uint8_t foo_expected[] = {
+        0x0A, 0x0B, 0x0C, 0x0D
+    };
+    // Check if option is valid in terms of code and carried data.
+    testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
+}
+
 /// This test checks if Uint32Parser can really parse the whole range
 /// and properly err of out of range values. As we can't call Uint32Parser
 /// directly, we are exploiting the fact that it is used to parse global
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 1d9766f..decd986 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -59,14 +59,14 @@ if USE_CLANGPP
 b10_dhcp6_CXXFLAGS = -Wno-unused-parameter
 endif
 
-b10_dhcp6_LDADD  = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+b10_dhcp6_LDADD  = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
-b10_dhcp6_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 
 b10_dhcp6dir = $(pkgdatadir)
 b10_dhcp6_DATA = dhcp6.spec
diff --git a/src/bin/dhcp6/config_parser.cc b/src/bin/dhcp6/config_parser.cc
index 4c6fab1..944bb21 100644
--- a/src/bin/dhcp6/config_parser.cc
+++ b/src/bin/dhcp6/config_parser.cc
@@ -496,12 +496,12 @@ private:
 ///
 /// This parser parses configuration entries that specify value of
 /// a single option. These entries include option name, option code
-/// and data carried by the option. If parsing is successful than an
+/// and data carried by the option. If parsing is successful then an
 /// instance of an option is created and added to the storage provided
 /// by the calling class.
 ///
 /// @todo This class parses and validates the option name. However it is
-/// not used anywhere util support for option spaces is implemented
+/// not used anywhere until support for option spaces is implemented
 /// (see tickets #2319, #2314). When option spaces are implemented
 /// there will be a way to reference the particular option using
 /// its type (code) or option name.
@@ -857,26 +857,21 @@ public:
             // a setStorage and build methods are invoked.
 
             // Try uint32 type parser.
-            if (buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
-                                                          param.second)) {
-                // Storage set, build invoked on the parser, proceed with
-                // next configuration element.
-                continue;
-            }
-            // Try string type parser.
-            if (buildParser<StringParser, StringStorage >(parser, string_values_,
-                                                          param.second)) {
-                continue;
-            }
-            // Try pools parser.
-            if (buildParser<PoolParser, PoolStorage >(parser, pools_,
-                                                      param.second)) {
-                continue;
-            }
-            // Try option data parser.
-            if (buildParser<OptionDataListParser, OptionStorage >(parser, options_,
-                                                                  param.second)) {
-                continue;
+            if (!buildParser<Uint32Parser, Uint32Storage >(parser, uint32_values_,
+                                                           param.second) &&
+                // Try string type parser.
+                !buildParser<StringParser, StringStorage >(parser, string_values_,
+                                                           param.second) &&
+                // Try pool parser.
+                !buildParser<PoolParser, PoolStorage >(parser, pools_,
+                                                       param.second) &&
+                // Try option data parser.
+                !buildParser<OptionDataListParser, OptionStorage >(parser, options_,
+                                                                   param.second)) {
+                // Appropriate parsers are created in the createSubnet6ConfigParser
+                // and they should be limited to those that we check here for. Thus,
+                // if we fail to find a matching parser here it is a programming error.
+                isc_throw(Dhcp6ConfigError, "failed to find suitable parser");
             }
         }
         // Ok, we now have subnet parsed
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 6ab42b3..1913bb5 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -47,9 +47,9 @@ This is an informational message reporting that the configuration has
 been extended to include the specified subnet.
 
 % DHCP6_CONFIG_OPTION_DUPLICATE multiple options with the code: %1 added to the subnet: %2
-This warning message is issued on attempt to configure multiple options with the
+This warning message is issued on an attempt to configure multiple options with the
 same option code for the particular subnet. Adding multiple options is uncommon
-for DHCPv6, yet it is not prohibited.
+for DHCPv6, but it is not prohibited.
 
 % DHCP6_CONFIG_START DHCPv6 server is processing the following configuration: %1
 This is a debug message that is issued every time the server receives a
@@ -65,11 +65,18 @@ This informational message is printed every time DHCPv6 is started.
 It indicates what database backend type is being to store lease and
 other information.
 
+% DHCP6_LEASE_WITHOUT_DUID lease for address %1 does not have a DUID
+This error message indicates a database consistency failure. The lease
+database has an entry indicating that the given address is in use,
+but the lease does not contain any client identification. This is most
+likely due to a software error: please raise a bug report. As a temporary
+workaround, manually remove the lease entry from the database.
+
 % DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully advertised
-a lease. It is up to the client to choose one server out of othe advertised
-and continue allocation with that server. This is a normal behavior and
-indicates successful operation.
+a lease. It is up to the client to choose one server out of the
+advertised servers and continue allocation with that server. This
+is a normal behavior and indicates successful operation.
 
 % DHCP6_LEASE_ADVERT_FAIL failed to advertise a lease for client duid=%1, iaid=%2
 This message indicates that the server failed to advertise (in response to
@@ -79,19 +86,43 @@ such failure. Each specific failure is logged in a separate log entry.
 % DHCP6_LEASE_ALLOC lease %1 has been allocated (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully granted (in
 response to client's REQUEST message) a lease. This is a normal behavior
-and incicates successful operation.
+and indicates successful operation.
 
 % DHCP6_LEASE_ALLOC_FAIL failed to grant a lease for client duid=%1, iaid=%2
 This message indicates that the server failed to grant (in response to
 received REQUEST) a lease for a given client. There may be many reasons for
 such failure. Each specific failure is logged in a separate log entry.
 
-% DHCP6_REQUIRED_OPTIONS_CHECK_FAIL %1 message received from %2 failed the following check: %3
-This message indicates that received DHCPv6 packet is invalid.  This may be due
-to a number of reasons, e.g. the mandatory client-id option is missing,
-the server-id forbidden in that particular type of message is present,
-there is more than one instance of client-id or server-id present,
-etc. The exact reason for rejecting the packet is included in the message.
+% DHCP6_RELEASE address %1 belonging to client duid=%2, iaid=%3 was released properly.
+This debug message indicates that an address was released properly. It
+is a normal operation during client shutdown.
+
+% DHCP6_RELEASE_FAIL failed to remove lease for address %1 for duid=%2, iaid=%3
+This error message indicates that the software failed to remove a
+lease from the lease database.  It probably due to an error during a
+database operation: resolution will most likely require administrator
+intervention (e.g. check if DHCP process has sufficient privileges to
+update the database). It may also be triggered if a lease was manually
+removed from the database during RELEASE message processing.
+
+% DHCP6_RELEASE_FAIL_WRONG_DUID client (duid=%1) tried to release address %2, but it belongs to client (duid=%3)
+This warning message indicates that client tried to release an address
+that belongs to a different client. This should not happen in normal
+circumstances and may indicate a misconfiguration of the client.  However,
+since the client releasing the address will stop using it anyway, there
+is a good chance that the situation will correct itself.
+
+% DHCP6_RELEASE_FAIL_WRONG_IAID client (duid=%1) tried to release address %2, but it used wrong IAID (expected %3, but got %4)
+This warning message indicates that client tried to release an address
+that does belong to it, but the address was expected to be in a different
+IA (identity association) container. This probably means that the client's
+support for multiple addresses is flawed.
+
+% DHCP6_RELEASE_MISSING_CLIENTID client (address=%1) sent RELEASE message without mandatory client-id
+This warning message indicates that client sent RELEASE message without
+mandatory client-id option. This is most likely caused by a buggy client
+(or a relay that malformed forwarded message). This request will not be
+processed and a response with error status code will be sent back.
 
 % DHCP6_NOT_RUNNING IPv6 DHCP server is not running
 A warning message is issued when an attempt is made to shut down the
@@ -128,7 +159,7 @@ a received OFFER packet as UNKNOWN).
 
 % DHCP6_PACKET_RECEIVE_FAIL error on attempt to receive packet: %1
 The IPv6 DHCP server tried to receive a packet but an error
-occured during this attempt. The reason for the error is included in
+occurred during this attempt. The reason for the error is included in
 the message.
 
 % DHCP6_PACKET_SEND_FAIL failed to send DHCPv6 packet: %1
@@ -149,6 +180,13 @@ as a hint for possible requested address.
 % DHCP6_QUERY_DATA received packet length %1, data length %2, data is %3
 A debug message listing the data received from the client or relay.
 
+% DHCP6_REQUIRED_OPTIONS_CHECK_FAIL %1 message received from %2 failed the following check: %3
+This message indicates that received DHCPv6 packet is invalid.  This may be due
+to a number of reasons, e.g. the mandatory client-id option is missing,
+the server-id forbidden in that particular type of message is present,
+there is more than one instance of client-id or server-id present,
+etc. The exact reason for rejecting the packet is included in the message.
+
 % DHCP6_RESPONSE_DATA responding with packet type %1 data is %2
 A debug message listing the data returned to the client.
 
@@ -216,3 +254,8 @@ recently and does not recognize its well-behaving clients. This is more
 probable if you see many such messages. Clients will recover from this,
 but they will most likely get a different IP addresses and experience
 a brief service interruption.
+
+% DHCP6_UNKNOWN_RELEASE received RELEASE from unknown client (duid=%1, iaid=%2)
+This warning message is printed when client attempts to release a lease,
+but no such lease is known by the server. See DHCP6_UNKNOWN_RENEW for
+possible reasons for such behavior.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 2482833..ad8fb8c 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -23,8 +23,8 @@
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option_custom.h>
+#include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/dhcp6_srv.h>
@@ -331,8 +331,8 @@ void Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer)
 
     // Client requests some options using ORO option. Try to
     // get this option from client's message.
-    boost::shared_ptr<Option6IntArray<uint16_t> > option_oro =
-        boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(question->getOption(D6O_ORO));
+    boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
+        boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(question->getOption(D6O_ORO));
     // Option ORO not found. Don't do anything then.
     if (!option_oro) {
         return;
@@ -436,6 +436,8 @@ void Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer) {
 
     // We need to allocate addresses for all IA_NA options in the client's
     // question (i.e. SOLICIT or REQUEST) message.
+    // @todo add support for IA_TA
+    // @todo add support for IA_PD
 
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(question);
@@ -604,7 +606,7 @@ OptionPtr Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
         boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
 
         // Insert status code NoAddrsAvail.
-        ia_rsp->addOption(createStatusCode(STATUS_NoAddrsAvail,
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
                           "Sorry, no known leases for this duid/iaid."));
 
         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_UNKNOWN_RENEW)
@@ -640,6 +642,8 @@ void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
 
     // We need to renew addresses for all IA_NA options in the client's
     // RENEW message.
+    // @todo add support for IA_TA
+    // @todo add support for IA_PD
 
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(renew);
@@ -688,11 +692,176 @@ void Dhcpv6Srv::renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply) {
             break;
         }
     }
+}
+
+void Dhcpv6Srv::releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply) {
+
+    // We need to release addresses for all IA_NA options in the client's
+    // RELEASE message.
+    // @todo Add support for IA_TA
+    // @todo Add support for IA_PD
+    // @todo Consider supporting more than one address in a single IA_NA.
+    // That was envisaged by RFC3315, but it never happened. The only
+    // software that supports that is Dibbler, but its author seriously doubts
+    // if anyone is really using it. Clients that want more than one address
+    // just include more instances of IA_NA options.
+
+    // Let's find client's DUID. Client is supposed to include its client-id
+    // option almost all the time (the only exception is an anonymous inf-request,
+    // but that is mostly a theoretical case). Our allocation engine needs DUID
+    // and will refuse to allocate anything to anonymous clients.
+    OptionPtr opt_duid = release->getOption(D6O_CLIENTID);
+    if (!opt_duid) {
+        // This should not happen. We have checked this before.
+        // see sanityCheck() called from processRelease()
+        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_MISSING_CLIENTID)
+            .arg(release->getRemoteAddr().toText());
+
+        reply->addOption(createStatusCode(STATUS_UnspecFail,
+                         "You did not include mandatory client-id"));
+        return;
+    }
+    DuidPtr duid(new DUID(opt_duid->getData()));
+
+    int general_status = STATUS_Success;
+    for (Option::OptionCollection::iterator opt = release->options_.begin();
+         opt != release->options_.end(); ++opt) {
+        switch (opt->second->getType()) {
+        case D6O_IA_NA: {
+            OptionPtr answer_opt = releaseIA_NA(duid, release, general_status,
+                                   boost::dynamic_pointer_cast<Option6IA>(opt->second));
+            if (answer_opt) {
+                reply->addOption(answer_opt);
+            }
+            break;
+        }
+        // @todo: add support for IA_PD
+        // @todo: add support for IA_TA
+        default:
+            // remaining options are stateless and thus ignored in this context
+            ;
+        }
+    }
+
+    // To be pedantic, we should also include status code in the top-level
+    // scope, not just in each IA_NA. See RFC3315, section 18.2.6.
+    // This behavior will likely go away in RFC3315bis.
+    reply->addOption(createStatusCode(general_status,
+                     "Summary status for all processed IA_NAs"));
+}
+
+OptionPtr Dhcpv6Srv::releaseIA_NA(const DuidPtr& duid, Pkt6Ptr question,
+                                  int& general_status,
+                                  boost::shared_ptr<Option6IA> ia) {
+    // Release can be done in one of two ways:
+    // Approach 1: extract address from client's IA_NA and see if it belongs
+    // to this particular client.
+    // Approach 2: find a subnet for this client, get a lease for
+    // this subnet/duid/iaid and check if its content matches to what the
+    // client is asking us to release.
+    //
+    // This method implements approach 1.
+
+    // That's our response
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
+
+    boost::shared_ptr<Option6IAAddr> release_addr = boost::dynamic_pointer_cast<Option6IAAddr>
+        (ia->getOption(D6O_IAADDR));
+    if (!release_addr) {
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                                           "You did not include address in your RELEASE"));
+        general_status = STATUS_NoBinding;
+        return (ia_rsp);
+    }
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(release_addr->getAddress());
+
+    if (!lease) {
+        // client releasing a lease that we don't know about.
+
+        // Insert status code NoAddrsAvail.
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "Sorry, no known leases for this duid/iaid, can't release."));
+        general_status = STATUS_NoBinding;
+
+        LOG_INFO(dhcp6_logger, DHCP6_UNKNOWN_RELEASE)
+            .arg(duid->toText())
+            .arg(ia->getIAID());
+
+        return (ia_rsp);
+    }
+
+    if (!lease->duid_) {
+        // Something is gravely wrong here. We do have a lease, but it does not
+        // have mandatory DUID information attached. Someone was messing with our
+        // database.
+
+        LOG_ERROR(dhcp6_logger, DHCP6_LEASE_WITHOUT_DUID)
+            .arg(release_addr->getAddress().toText());
+
+        general_status = STATUS_UnspecFail;
+        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
+                          "Database consistency check failed when trying to RELEASE"));
+        return (ia_rsp);
+    }
+
+    if (*duid != *(lease->duid_)) {
+        // Sorry, it's not your address. You can't release it.
+
+        LOG_INFO(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_DUID)
+            .arg(duid->toText())
+            .arg(release_addr->getAddress().toText())
+            .arg(lease->duid_->toText());
+
+        general_status = STATUS_NoBinding;
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "This address does not belong to you, you can't release it"));
+        return (ia_rsp);
+    }
+
+    if (ia->getIAID() != lease->iaid_) {
+        // This address belongs to this client, but to a different IA
+        LOG_WARN(dhcp6_logger, DHCP6_RELEASE_FAIL_WRONG_IAID)
+            .arg(duid->toText())
+            .arg(release_addr->getAddress().toText())
+            .arg(lease->iaid_)
+            .arg(ia->getIAID());
+        ia_rsp->addOption(createStatusCode(STATUS_NoBinding,
+                          "This is your address, but you used wrong IAID"));
+        general_status = STATUS_NoBinding;
+        return (ia_rsp);
+    }
+
+    // It is not necessary to check if the address matches as we used
+    // getLease6(addr) method that is supposed to return a proper lease.
 
+    // Ok, we've passed all checks. Let's release this address.
 
+    if (!LeaseMgrFactory::instance().deleteLease(lease->addr_)) {
+        ia_rsp->addOption(createStatusCode(STATUS_UnspecFail,
+                          "Server failed to release a lease"));
+
+        LOG_ERROR(dhcp6_logger, DHCP6_RELEASE_FAIL)
+            .arg(lease->addr_.toText())
+            .arg(duid->toText())
+            .arg(lease->iaid_);
+        general_status = STATUS_UnspecFail;
+
+        return (ia_rsp);
+    } else {
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_RELEASE)
+            .arg(lease->addr_.toText())
+            .arg(duid->toText())
+            .arg(lease->iaid_);
 
+        ia_rsp->addOption(createStatusCode(STATUS_Success,
+                          "Lease released. Thank you, please come again."));
+
+        return (ia_rsp);
+    }
 }
 
+
 Pkt6Ptr Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
 
     sanityCheck(solicit, MANDATORY, FORBIDDEN);
@@ -751,8 +920,16 @@ Pkt6Ptr Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
 }
 
 Pkt6Ptr Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
-    /// @todo: Implement this
+
+    sanityCheck(release, MANDATORY, MANDATORY);
+
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
+
+    copyDefaultOptions(release, reply);
+    appendDefaultOptions(release, reply);
+
+    releaseLeases(release, reply);
+
     return reply;
 }
 
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index b6b0306..0f3e641 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -212,17 +212,39 @@ protected:
 
     /// @brief Renews specific IA_NA option
     ///
-    /// Generates response to IA_NA. This typically includes finding a lease that
-    /// corresponds to the received address. If no such lease is found, an IA_NA
-    /// response is generated with an appropriate status code.
+    /// Generates response to IA_NA in Renew. This typically includes finding a
+    /// lease that corresponds to the received address. If no such lease is
+    /// found, an IA_NA response is generated with an appropriate status code.
     ///
     /// @param subnet subnet the sender belongs to
     /// @param duid client's duid
     /// @param question client's message
     /// @param ia IA_NA option that is being renewed
+    /// @return IA_NA option (server's response)
     OptionPtr renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                          Pkt6Ptr question, boost::shared_ptr<Option6IA> ia);
 
+    /// @brief Releases specific IA_NA option
+    ///
+    /// Generates response to IA_NA in Release message. This covers finding and
+    /// removal of a lease that corresponds to the received address. If no such
+    /// lease is found, an IA_NA response is generated with an appropriate
+    /// status code.
+    ///
+    /// As RFC 3315 requires that a single status code be sent for the whole message,
+    /// this method may update the passed general_status: it is set to SUCCESS when
+    /// message processing begins, but may be updated to some error code if the
+    /// release process fails.
+    ///
+    /// @param duid client's duid
+    /// @param question client's message
+    /// @param general_status a global status (it may be updated in case of errors)
+    /// @param ia IA_NA option that is being renewed
+    /// @return IA_NA option (server's response)
+    OptionPtr releaseIA_NA(const DuidPtr& duid, Pkt6Ptr question,
+                           int& general_status,
+                           boost::shared_ptr<Option6IA> ia);
+
     /// @brief Copies required options from client message to server answer.
     ///
     /// Copies options that must appear in any server response (ADVERTISE, REPLY)
@@ -271,6 +293,17 @@ protected:
     /// @param reply server's response
     void renewLeases(const Pkt6Ptr& renew, Pkt6Ptr& reply);
 
+    /// @brief Attempts to release received addresses
+    ///
+    /// It iterates through received IA_NA options and attempts to release
+    /// received addresses. If no such leases are found, or the lease fails
+    /// proper checks (e.g. belongs to someone else), a proper status
+    /// code is added to reply message. Released addresses are not added
+    /// to REPLY packet, just its IA_NA containers.
+    /// @param release client's message asking to release
+    /// @param reply server's response
+    void releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply);
+
     /// @brief Sets server-identifier.
     ///
     /// This method attempts to set server-identifier DUID. It loads it
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index b01b877..d251df3 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -63,14 +63,13 @@ dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp6_unittests_LDADD = $(GTEST_LDADD)
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
-dhcp6_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
-
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index 4fd6baa..7083598 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -81,7 +81,9 @@ public:
         return (createConfigWithOption(params));
     }
 
-    std::string createConfigWithOption(const std::map<std::string, std::string>& params) {
+    std::string createConfigWithOption(const std::map<std::string,
+                                       std::string>& params)
+    {
         std::ostringstream stream;
         stream << "{ \"interface\": [ \"all\" ],"
             "\"preferred-lifetime\": 3000,"
@@ -97,6 +99,7 @@ public:
             if (!first) {
                 stream << ", ";
             } else {
+                // cppcheck-suppress unreadVariable
                 first = false;
             }
             if (param.first == "name") {
@@ -144,14 +147,14 @@ public:
                    << ex.what() << std::endl;
         }
 
-
-        // returned value should be 0 (configuration success)
+        // status object must not be NULL
         if (!status) {
             FAIL() << "Fatal error: unable to reset configuration database"
                    << " after the test. Configuration function returned"
                    << " NULL pointer" << std::endl;
         }
         comment_ = parseAnswer(rcode_, status);
+        // returned value should be 0 (configuration success)
         if (rcode_ != 0) {
             FAIL() << "Fatal error: unable to reset configuration database"
                    << " after the test. Configuration function returned"
@@ -215,9 +218,10 @@ public:
             ASSERT_EQ(buf.getLength() - option_desc.option->getHeaderLen(),
                       expected_data_len);
         }
-        // Verify that the data is correct. However do not verify suboptions.
+        // Verify that the data is correct. Do not verify suboptions and a header.
         const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
-        EXPECT_TRUE(memcmp(expected_data, data, expected_data_len));
+        EXPECT_EQ(0, memcmp(expected_data, data + option_desc.option->getHeaderLen(),
+                            expected_data_len));
     }
 
     Dhcpv6Srv srv_;
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index de0dc28..db93f9e 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -23,7 +23,7 @@
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int_array.h>
+#include <dhcp/option_int_array.h>
 #include <dhcp6/config_parser.h>
 #include <dhcp6/dhcp6_srv.h>
 #include <dhcpsrv/cfgmgr.h>
@@ -59,6 +59,7 @@ public:
     using Dhcpv6Srv::processSolicit;
     using Dhcpv6Srv::processRequest;
     using Dhcpv6Srv::processRenew;
+    using Dhcpv6Srv::processRelease;
     using Dhcpv6Srv::createStatusCode;
     using Dhcpv6Srv::selectSubnet;
     using Dhcpv6Srv::sanityCheck;
@@ -143,11 +144,14 @@ public:
     }
 
     // Checks that server rejected IA_NA, i.e. that it has no addresses and
-    // that expected status code really appears there.
+    // that expected status code really appears there. In some limited cases
+    // (reply to RELEASE) it may be used to verify positive case, where
+    // IA_NA response is expected to not include address.
+    //
     // Status code indicates type of error encountered (in theory it can also
     // indicate success, but servers typically don't send success status
     // as this is the default result and it saves bandwidth)
-    void checkRejectedIA_NA(const boost::shared_ptr<Option6IA>& ia,
+    void checkIA_NAStatusCode(const boost::shared_ptr<Option6IA>& ia,
                             uint16_t expected_status_code) {
         // Make sure there is no address assigned.
         EXPECT_FALSE(ia->getOption(D6O_IAADDR));
@@ -158,6 +162,12 @@ public:
 
         boost::shared_ptr<OptionCustom> status =
             boost::dynamic_pointer_cast<OptionCustom>(ia->getOption(D6O_STATUS_CODE));
+
+        // It is ok to not include status success as this is the default behavior
+        if (expected_status_code == STATUS_Success && !status) {
+            return;
+        }
+
         EXPECT_TRUE(status);
 
         if (status) {
@@ -169,6 +179,26 @@ public:
         }
     }
 
+
+    void checkMsgStatusCode(const Pkt6Ptr& msg, uint16_t expected_status) {
+        boost::shared_ptr<OptionCustom> status =
+            boost::dynamic_pointer_cast<OptionCustom>(msg->getOption(D6O_STATUS_CODE));
+
+        // It is ok to not include status success as this is the default behavior
+        if (expected_status == STATUS_Success && !status) {
+            return;
+        }
+
+        EXPECT_TRUE(status);
+        if (status) {
+            // We don't have dedicated class for status code, so let's just interpret
+            // first 2 bytes as status. Remainder of the status code option content is
+            // just a text explanation what went wrong.
+            EXPECT_EQ(static_cast<uint16_t>(expected_status),
+                      status->readInteger<uint16_t>(0));
+        }
+    }
+
     // Check that generated IAADDR option contains expected address.
     void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
                      const IOAddress& expected_addr,
@@ -353,10 +383,9 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
 
     ElementPtr json = Element::fromJSON(config);
 
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW(srv.reset(new NakedDhcpv6Srv(0)));
+    NakedDhcpv6Srv srv(0);
 
-    EXPECT_NO_THROW(x = configureDhcp6Server(*srv, json));
+    EXPECT_NO_THROW(x = configureDhcp6Server(srv, json));
     ASSERT_TRUE(x);
     comment_ = parseAnswer(rcode_, x);
 
@@ -369,7 +398,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
     sol->addOption(clientid);
 
     // Pass it to the server and get an advertise
-    boost::shared_ptr<Pkt6> adv = srv->processSolicit(sol);
+    boost::shared_ptr<Pkt6> adv = srv.processSolicit(sol);
 
     // check if we get response at all
     ASSERT_TRUE(adv);
@@ -381,8 +410,8 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
 
     // Let's now request option with code 1000.
     // We expect that server will include this option in its reply.
-    boost::shared_ptr<Option6IntArray<uint16_t> >
-        option_oro(new Option6IntArray<uint16_t>(D6O_ORO));
+    boost::shared_ptr<OptionIntArray<uint16_t> >
+        option_oro(new OptionIntArray<uint16_t>(Option::V6, D6O_ORO));
     // Create vector with two option codes.
     std::vector<uint16_t> codes(2);
     codes[0] = 1000;
@@ -393,7 +422,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
     sol->addOption(option_oro);
 
     // Need to process SOLICIT again after requesting new option.
-    adv = srv->processSolicit(sol);
+    adv = srv.processSolicit(sol);
     ASSERT_TRUE(adv);
 
     OptionPtr tmp = adv->getOption(D6O_NAME_SERVERS);
@@ -444,8 +473,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
 // - server-id
 // - IA that includes IAADDR
 TEST_F(Dhcpv6SrvTest, SolicitBasic) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
@@ -454,7 +482,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
     sol->addOption(clientid);
 
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply = srv->processSolicit(sol);
+    Pkt6Ptr reply = srv.processSolicit(sol);
 
     // check if we get response at all
     checkResponse(reply, DHCPV6_ADVERTISE, 1234);
@@ -467,7 +495,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
     checkIAAddr(addr, addr->getAddress(), subnet_->getPreferred(), subnet_->getValid());
 
     // check DUIDs
-    checkServerId(reply, srv->getServerID());
+    checkServerId(reply, srv.getServerID());
     checkClientId(reply, clientid);
 }
 
@@ -487,8 +515,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
 // - server-id
 // - IA that includes IAADDR
 TEST_F(Dhcpv6SrvTest, SolicitHint) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
@@ -505,7 +532,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
     sol->addOption(clientid);
 
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply = srv->processSolicit(sol);
+    Pkt6Ptr reply = srv.processSolicit(sol);
 
     // check if we get response at all
     checkResponse(reply, DHCPV6_ADVERTISE, 1234);
@@ -521,7 +548,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
     checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
 
     // check DUIDs
-    checkServerId(reply, srv->getServerID());
+    checkServerId(reply, srv.getServerID());
     checkClientId(reply, clientid);
 }
 
@@ -541,8 +568,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
 // - server-id
 // - IA that includes IAADDR
 TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
@@ -557,7 +583,7 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
     sol->addOption(clientid);
 
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply = srv->processSolicit(sol);
+    Pkt6Ptr reply = srv.processSolicit(sol);
 
     // check if we get response at all
     checkResponse(reply, DHCPV6_ADVERTISE, 1234);
@@ -571,10 +597,13 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
     EXPECT_TRUE(subnet_->inPool(addr->getAddress()));
 
     // check DUIDs
-    checkServerId(reply, srv->getServerID());
+    checkServerId(reply, srv.getServerID());
     checkClientId(reply, clientid);
 }
 
+/// @todo: Add a test that client sends hint that is in pool, but currently
+/// being used by a different client.
+
 // This test checks that the server is offering different addresses to different
 // clients in ADVERTISEs. Please note that ADVERTISE is not a guarantee that such
 // and address will be assigned. Had the pool was very small and contained only
@@ -583,8 +612,7 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
 // client. ADVERTISE is basically saying "if you send me a request, you will
 // probably get an address like this" (there are no guarantees).
 TEST_F(Dhcpv6SrvTest, ManySolicits) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     Pkt6Ptr sol1 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol2 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 2345));
@@ -608,9 +636,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     sol3->addOption(clientid3);
 
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply1 = srv->processSolicit(sol1);
-    Pkt6Ptr reply2 = srv->processSolicit(sol2);
-    Pkt6Ptr reply3 = srv->processSolicit(sol3);
+    Pkt6Ptr reply1 = srv.processSolicit(sol1);
+    Pkt6Ptr reply2 = srv.processSolicit(sol2);
+    Pkt6Ptr reply3 = srv.processSolicit(sol3);
 
     // check if we get response at all
     checkResponse(reply1, DHCPV6_ADVERTISE, 1234);
@@ -631,9 +659,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid());
 
     // check DUIDs
-    checkServerId(reply1, srv->getServerID());
-    checkServerId(reply2, srv->getServerID());
-    checkServerId(reply3, srv->getServerID());
+    checkServerId(reply1, srv.getServerID());
+    checkServerId(reply2, srv.getServerID());
+    checkServerId(reply3, srv.getServerID());
     checkClientId(reply1, clientid1);
     checkClientId(reply2, clientid2);
     checkClientId(reply3, clientid3);
@@ -663,8 +691,7 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
 // - server-id
 // - IA that includes IAADDR
 TEST_F(Dhcpv6SrvTest, RequestBasic) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
@@ -681,10 +708,10 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     req->addOption(clientid);
 
     // server-id is mandatory in REQUEST
-    req->addOption(srv->getServerID());
+    req->addOption(srv.getServerID());
 
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv->processRequest(req);
+    Pkt6Ptr reply = srv.processRequest(req);
 
     // check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -700,7 +727,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     checkIAAddr(addr, hint, subnet_->getPreferred(), subnet_->getValid());
 
     // check DUIDs
-    checkServerId(reply, srv->getServerID());
+    checkServerId(reply, srv.getServerID());
     checkClientId(reply, clientid);
 
     // check that the lease is really in the database
@@ -717,8 +744,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
 // client. ADVERTISE is basically saying "if you send me a request, you will
 // probably get an address like this" (there are no guarantees).
 TEST_F(Dhcpv6SrvTest, ManyRequests) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     Pkt6Ptr req1 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     Pkt6Ptr req2 = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 2345));
@@ -742,14 +768,14 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req3->addOption(clientid3);
 
     // server-id is mandatory in REQUEST
-    req1->addOption(srv->getServerID());
-    req2->addOption(srv->getServerID());
-    req3->addOption(srv->getServerID());
+    req1->addOption(srv.getServerID());
+    req2->addOption(srv.getServerID());
+    req3->addOption(srv.getServerID());
 
     // Pass it to the server and get an advertise
-    Pkt6Ptr reply1 = srv->processRequest(req1);
-    Pkt6Ptr reply2 = srv->processRequest(req2);
-    Pkt6Ptr reply3 = srv->processRequest(req3);
+    Pkt6Ptr reply1 = srv.processRequest(req1);
+    Pkt6Ptr reply2 = srv.processRequest(req2);
+    Pkt6Ptr reply3 = srv.processRequest(req3);
 
     // check if we get response at all
     checkResponse(reply1, DHCPV6_REPLY, 1234);
@@ -770,9 +796,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     checkIAAddr(addr3, addr3->getAddress(), subnet_->getPreferred(), subnet_->getValid());
 
     // check DUIDs
-    checkServerId(reply1, srv->getServerID());
-    checkServerId(reply2, srv->getServerID());
-    checkServerId(reply3, srv->getServerID());
+    checkServerId(reply1, srv.getServerID());
+    checkServerId(reply2, srv.getServerID());
+    checkServerId(reply3, srv.getServerID());
     checkClientId(reply1, clientid1);
     checkClientId(reply2, clientid2);
     checkClientId(reply3, clientid3);
@@ -796,8 +822,7 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
 // - returned REPLY message has IA that includes IAADDR
 // - lease is actually renewed in LeaseMgr
 TEST_F(Dhcpv6SrvTest, RenewBasic) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     const IOAddress addr("2001:db8:1:1::cafe:babe");
     const uint32_t iaid = 234;
@@ -838,10 +863,10 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
     req->addOption(clientid);
 
     // Server-id is mandatory in RENEW
-    req->addOption(srv->getServerID());
+    req->addOption(srv.getServerID());
 
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv->processRenew(req);
+    Pkt6Ptr reply = srv.processRenew(req);
 
     // Check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, 1234);
@@ -857,7 +882,7 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
     checkIAAddr(addr_opt, addr, subnet_->getPreferred(), subnet_->getValid());
 
     // Check DUIDs
-    checkServerId(reply, srv->getServerID());
+    checkServerId(reply, srv.getServerID());
     checkClientId(reply, clientid);
 
     // Check that the lease is really in the database
@@ -892,9 +917,7 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
 // - returned REPLY message has IA that includes STATUS-CODE
 // - No lease in LeaseMgr
 TEST_F(Dhcpv6SrvTest, RenewReject) {
-
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     const IOAddress addr("2001:db8:1:1::dead");
     const uint32_t transid = 1234;
@@ -922,12 +945,12 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     req->addOption(clientid);
 
     // Server-id is mandatory in RENEW
-    req->addOption(srv->getServerID());
+    req->addOption(srv.getServerID());
 
     // Case 1: No lease known to server
 
     // Pass it to the server and hope for a REPLY
-    Pkt6Ptr reply = srv->processRenew(req);
+    Pkt6Ptr reply = srv.processRenew(req);
 
     // Check if we get response at all
     checkResponse(reply, DHCPV6_REPLY, transid);
@@ -936,7 +959,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     // Check that IA_NA was returned and that there's an address included
     ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
     ASSERT_TRUE(ia);
-    checkRejectedIA_NA(ia, STATUS_NoAddrsAvail);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
 
     // Check that there is no lease added
     l = LeaseMgrFactory::instance().getLease6(addr);
@@ -953,14 +976,14 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
 
     // Pass it to the server and hope for a REPLY
-    reply = srv->processRenew(req);
+    reply = srv.processRenew(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
     // Check that IA_NA was returned and that there's an address included
     ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
     ASSERT_TRUE(ia);
-    checkRejectedIA_NA(ia, STATUS_NoAddrsAvail);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
 
     // There is a iaid mis-match, so server should respond that there is
     // no such address to renew.
@@ -972,14 +995,14 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     req->addOption(generateClientId(13)); // generate different DUID
                                           // (with length 13)
 
-    reply = srv->processRenew(req);
+    reply = srv.processRenew(req);
     checkResponse(reply, DHCPV6_REPLY, transid);
     tmp = reply->getOption(D6O_IA_NA);
     ASSERT_TRUE(tmp);
     // Check that IA_NA was returned and that there's an address included
     ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
     ASSERT_TRUE(ia);
-    checkRejectedIA_NA(ia, STATUS_NoAddrsAvail);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
 
     lease = LeaseMgrFactory::instance().getLease6(addr);
     ASSERT_TRUE(lease);
@@ -989,10 +1012,198 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
 }
 
+// This test verifies that incoming (positive) RELEASE can be handled properly,
+// that a REPLY is generated, that the response has status code and that the
+// lease is indeed removed from the database.
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA that does not include an IAADDR
+// - lease is actually removed from LeaseMgr
+TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
+    NakedDhcpv6Srv srv(0);
+
+    const IOAddress addr("2001:db8:1:1::cafe:babe");
+    const uint32_t iaid = 234;
+
+    // Generate client-id also duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the address we are about to use is indeed in pool
+    ASSERT_TRUE(subnet_->inPool(addr));
+
+    // Note that preferred, valid, T1 and T2 timers and CLTT are set to invalid
+    // value on purpose. They should be updated during RENEW.
+    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, iaid,
+                               501, 502, 503, 504, subnet_->getID(), 0));
+    lease->cltt_ = 1234;
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Check that the lease is really in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_TRUE(l);
+
+    // Let's create a RELEASE
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
+
+    // Server-id is mandatory in RELEASE
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(req);
+
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
+
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+
+    // Check that IA_NA was returned and that there's an address included
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    checkIA_NAStatusCode(ia, STATUS_Success);
+    checkMsgStatusCode(reply, STATUS_Success);
+
+    // There should be no address returned in RELEASE (see RFC3315, 18.2.6)
+    EXPECT_FALSE(tmp->getOption(D6O_IAADDR));
+
+    // Check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+
+    // Check that the lease is really gone in the database
+    // get lease by address
+    l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_FALSE(l);
+
+    // get lease by subnetid/duid/iaid combination
+    l = LeaseMgrFactory::instance().getLease6(*duid_, iaid, subnet_->getID());
+    ASSERT_FALSE(l);
+}
+
+// This test verifies that incoming (invalid) RELEASE can be handled properly.
+//
+// This test checks 3 scenarios:
+// 1. there is no such lease at all
+// 2. there is such a lease, but it is assigned to a different IAID
+// 3. there is such a lease, but it belongs to a different client
+//
+// expected:
+// - returned REPLY message has copy of client-id
+// - returned REPLY message has server-id
+// - returned REPLY message has IA that includes STATUS-CODE
+// - No lease in LeaseMgr
+TEST_F(Dhcpv6SrvTest, ReleaseReject) {
+
+    NakedDhcpv6Srv srv(0);
+
+    const IOAddress addr("2001:db8:1:1::dead");
+    const uint32_t transid = 1234;
+    const uint32_t valid_iaid = 234;
+    const uint32_t bogus_iaid = 456;
+
+    // Quick sanity check that the address we're about to use is ok
+    ASSERT_TRUE(subnet_->inPool(addr));
+
+    // GenerateClientId() also sets duid_
+    OptionPtr clientid = generateClientId();
+
+    // Check that the lease is NOT in the database
+    Lease6Ptr l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_FALSE(l);
+
+    // Let's create a RELEASE
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, transid));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
+
+    OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
+    ia->addOption(released_addr_opt);
+    req->addOption(ia);
+    req->addOption(clientid);
+
+    // Server-id is mandatory in RENEW
+    req->addOption(srv.getServerID());
+
+    // Case 1: No lease known to server
+    SCOPED_TRACE("CASE 1: No lease known to server");
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRelease(req);
+
+    // Check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    OptionPtr tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+    // Check that IA_NA was returned and that there's an address included
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+    checkMsgStatusCode(reply, STATUS_NoBinding);
+
+    // Check that the lease is not there
+    l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_FALSE(l);
+
+    // CASE 2: Lease is known and belongs to this client, but to a different IAID
+    SCOPED_TRACE("CASE 2: Lease is known and belongs to this client, but to a different IAID");
+
+    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, duid_, valid_iaid,
+                               501, 502, 503, 504, subnet_->getID(), 0));
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // Pass it to the server and hope for a REPLY
+    reply = srv.processRelease(req);
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+    // Check that IA_NA was returned and that there's an address included
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+    checkMsgStatusCode(reply, STATUS_NoBinding);
+
+    // Check that the lease is still there
+    l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_TRUE(l);
+
+    // CASE 3: Lease belongs to a client with different client-id
+    SCOPED_TRACE("CASE 3: Lease belongs to a client with different client-id");
+
+    req->delOption(D6O_CLIENTID);
+    ia = boost::dynamic_pointer_cast<Option6IA>(req->getOption(D6O_IA_NA));
+    ia->setIAID(valid_iaid); // Now iaid in renew matches that in leasemgr
+    req->addOption(generateClientId(13)); // generate different DUID
+                                          // (with length 13)
+
+    reply = srv.processRelease(req);
+    checkResponse(reply, DHCPV6_REPLY, transid);
+    tmp = reply->getOption(D6O_IA_NA);
+    ASSERT_TRUE(tmp);
+    // Check that IA_NA was returned and that there's an address included
+    ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    ASSERT_TRUE(ia);
+    checkIA_NAStatusCode(ia, STATUS_NoBinding);
+    checkMsgStatusCode(reply, STATUS_NoBinding);
+
+    // Check that the lease is still there
+    l = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_TRUE(l);
+
+    // Finally, let's cleanup the database
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(addr));
+}
+
 // This test verifies if the status code option is generated properly.
 TEST_F(Dhcpv6SrvTest, StatusCode) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     // a dummy content for client-id
     uint8_t expected[] = {
@@ -1002,7 +1213,7 @@ TEST_F(Dhcpv6SrvTest, StatusCode) {
         0x41, 0x42, 0x43, 0x44, 0x45 // string value ABCDE
     };
     // Create the option.
-    OptionPtr status = srv->createStatusCode(3, "ABCDE");
+    OptionPtr status = srv.createStatusCode(3, "ABCDE");
     // Allocate an output buffer. We will store the option
     // in wire format here.
     OutputBuffer buf(sizeof(expected));
@@ -1016,34 +1227,34 @@ TEST_F(Dhcpv6SrvTest, StatusCode) {
 
 // This test verifies if the sanityCheck() really checks options presence.
 TEST_F(Dhcpv6SrvTest, sanityCheck) {
-    boost::scoped_ptr<NakedDhcpv6Srv> srv;
-    ASSERT_NO_THROW( srv.reset(new NakedDhcpv6Srv(0)) );
+    NakedDhcpv6Srv srv(0);
 
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
 
-    // check that the packets originating from local addresses can be
+    // Set link-local sender address, so appropriate subnet can be
+    // selected for this packet.
     pkt->setRemoteAddr(IOAddress("fe80::abcd"));
 
     // client-id is optional for information-request, so
-    EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL));
+    EXPECT_NO_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL));
 
     // empty packet, no client-id, no server-id
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN),
                  RFCViolation);
 
     // This doesn't make much sense, but let's check it for completeness
-    EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::FORBIDDEN, Dhcpv6Srv::FORBIDDEN));
+    EXPECT_NO_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::FORBIDDEN, Dhcpv6Srv::FORBIDDEN));
 
     OptionPtr clientid = generateClientId();
     pkt->addOption(clientid);
 
     // client-id is mandatory, server-id is forbidden (as in SOLICIT or REBIND)
-    EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN));
+    EXPECT_NO_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN));
 
-    pkt->addOption(srv->getServerID());
+    pkt->addOption(srv.getServerID());
 
     // both client-id and server-id are mandatory (as in REQUEST, RENEW, RELEASE, DECLINE)
-    EXPECT_NO_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY));
+    EXPECT_NO_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY));
 
     // sane section ends here, let's do some negative tests as well
 
@@ -1051,13 +1262,13 @@ TEST_F(Dhcpv6SrvTest, sanityCheck) {
     pkt->addOption(clientid);
 
     // with more than one client-id it should throw, no matter what
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL),
                  RFCViolation);
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::OPTIONAL),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::OPTIONAL),
                  RFCViolation);
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::MANDATORY),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::MANDATORY),
                  RFCViolation);
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
                  RFCViolation);
 
     pkt->delOption(D6O_CLIENTID);
@@ -1066,20 +1277,21 @@ TEST_F(Dhcpv6SrvTest, sanityCheck) {
     // again we have only one client-id
 
     // let's try different type of insanity - several server-ids
-    pkt->addOption(srv->getServerID());
-    pkt->addOption(srv->getServerID());
+    pkt->addOption(srv.getServerID());
+    pkt->addOption(srv.getServerID());
 
     // with more than one server-id it should throw, no matter what
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::OPTIONAL),
                  RFCViolation);
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::OPTIONAL),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::OPTIONAL),
                  RFCViolation);
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::MANDATORY),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::OPTIONAL, Dhcpv6Srv::MANDATORY),
                  RFCViolation);
-    EXPECT_THROW(srv->sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
+    EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
                  RFCViolation);
-
-
 }
 
+/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
+/// to call processX() methods.
+
 }   // end of anonymous namespace
diff --git a/src/bin/loadzone/.gitignore b/src/bin/loadzone/.gitignore
index 59f0bb5..41e280a 100644
--- a/src/bin/loadzone/.gitignore
+++ b/src/bin/loadzone/.gitignore
@@ -1,4 +1,5 @@
 /b10-loadzone
 /b10-loadzone.py
+/loadzone.py
 /run_loadzone.sh
 /b10-loadzone.8
diff --git a/src/bin/loadzone/Makefile.am b/src/bin/loadzone/Makefile.am
index 790f757..13b1501 100644
--- a/src/bin/loadzone/Makefile.am
+++ b/src/bin/loadzone/Makefile.am
@@ -1,12 +1,17 @@
-SUBDIRS = . tests/correct tests/error
+SUBDIRS = . tests
 bin_SCRIPTS = b10-loadzone
 noinst_SCRIPTS = run_loadzone.sh
 
-CLEANFILES = b10-loadzone
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/loadzone_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+
+CLEANFILES = b10-loadzone loadzone.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/loadzone_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/loadzone_messages.pyc
 
 man_MANS = b10-loadzone.8
 DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) b10-loadzone.xml
+EXTRA_DIST = $(man_MANS) b10-loadzone.xml loadzone_messages.mes
 
 if GENERATE_DOCS
 
@@ -21,10 +26,13 @@ $(man_MANS):
 
 endif
 
-b10-loadzone: b10-loadzone.py
-	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" \
-	       -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" \
-	       -e "s|@@LIBEXECDIR@@|$(pkglibexecdir)|" b10-loadzone.py >$@
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/loadzone_messages.py : loadzone_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/loadzone_messages.mes
+
+b10-loadzone: loadzone.py $(PYTHON_LOGMSGPKG_DIR)/work/loadzone_messages.py
+	$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" loadzone.py >$@
 	chmod a+x $@
 
 EXTRA_DIST += tests/normal/README
@@ -48,6 +56,7 @@ EXTRA_DIST += tests/normal/sql1.example.com.signed
 EXTRA_DIST += tests/normal/sql2.example.com
 EXTRA_DIST += tests/normal/sql2.example.com.signed
 
-pytest:
-	$(SHELL) tests/correct/correct_test.sh
-	$(SHELL) tests/error/error_test.sh
+CLEANDIRS = __pycache__
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/src/bin/loadzone/TODO b/src/bin/loadzone/TODO
index d8d5f24..a33385d 100644
--- a/src/bin/loadzone/TODO
+++ b/src/bin/loadzone/TODO
@@ -1,16 +1,3 @@
-Support optional origin in $INCLUDE:
-$INCLUDE filename origin
-
-Support optional comment in $INCLUDE:
-$INCLUDE filename origin comment
-
-Support optional comment in $TTL (RFC 2308):
-$TTL number comment
-
-Do not assume "." is origin if origin is not set and sees a @ or
-a label without a ".". It should probably fail.  (Don't assume a
-mistake means it is a root level label.)
-
 Add verbose option to show what it is adding, not necessarily
 in master file format, but in the context of the data source.
 
diff --git a/src/bin/loadzone/b10-loadzone.py.in b/src/bin/loadzone/b10-loadzone.py.in
deleted file mode 100644
index 83654f5..0000000
--- a/src/bin/loadzone/b10-loadzone.py.in
+++ /dev/null
@@ -1,94 +0,0 @@
-#!@PYTHON@
-
-# 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.
-
-import sys; sys.path.append ('@@PYTHONPATH@@')
-import re, getopt
-import isc.datasrc
-import isc.util.process
-from isc.datasrc.master import MasterFile
-import time
-import os
-
-isc.util.process.rename()
-
-#########################################################################
-# usage: print usage note and exit
-#########################################################################
-def usage():
-    print("Usage: %s [-d <database>] [-o <origin>] <file>" % sys.argv[0], \
-          file=sys.stderr)
-    exit(1)
-
-#########################################################################
-# main
-#########################################################################
-def main():
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], "d:o:h", \
-                                                ["dbfile", "origin", "help"])
-    except getopt.GetoptError as e:
-        print(str(e))
-        usage()
-        exit(2)
-
-    dbfile = '@@LOCALSTATEDIR@@/@PACKAGE@/zone.sqlite3'
-    initial_origin = ''
-    for o, a in opts:
-        if o in ("-d", "--dbfile"):
-            dbfile = a
-        elif o in ("-o", "--origin"):
-            if a[-1] != '.':
-                a += '.'
-            initial_origin = a
-        elif o in ("-h", "--help"):
-            usage()
-        else:
-            assert False, "unhandled option"
-
-    if len(args) != 1:
-        usage()
-    zonefile = args[0]
-    verbose = os.isatty(sys.stdout.fileno())
-    try:
-        master = MasterFile(zonefile, initial_origin, verbose)
-    except Exception as e:
-        sys.stderr.write("Error reading zone file: %s\n" % str(e))
-        exit(1)
-
-    try:
-        zone = master.zonename()
-        if verbose:
-            sys.stdout.write("Using SQLite3 database file %s\n" % dbfile)
-            sys.stdout.write("Zone name is %s\n" % zone)
-            sys.stdout.write("Loading file \"%s\"\n" % zonefile)
-    except Exception as e:
-        sys.stdout.write("\n")
-        sys.stderr.write("Error reading zone file: %s\n" % str(e))
-        exit(1)
-
-    try:
-        isc.datasrc.sqlite3_ds.load(dbfile, zone, master.zonedata)
-        if verbose:
-            master.closeverbose()
-            sys.stdout.write("\nDone.\n")
-    except Exception as e:
-        sys.stdout.write("\n")
-        sys.stderr.write("Error loading database: %s\n"% str(e))
-        exit(1)
-
-if __name__ == "__main__":
-    main()
diff --git a/src/bin/loadzone/b10-loadzone.xml b/src/bin/loadzone/b10-loadzone.xml
index 8c41e54..8c55f9b 100644
--- a/src/bin/loadzone/b10-loadzone.xml
+++ b/src/bin/loadzone/b10-loadzone.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) 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 26, 2012</date>
+    <date>December 15, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -36,7 +36,7 @@
 
   <docinfo>
     <copyright>
-      <year>2010</year>
+      <year>2012</year>
       <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
     </copyright>
   </docinfo>
@@ -44,9 +44,13 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <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 choice="req">filename</arg>
+      <arg><option>-c <replaceable class="parameter">datasrc_config</replaceable></option></arg>
+      <arg><option>-d <replaceable class="parameter">debug_level</replaceable></option></arg>
+      <arg><option>-i <replaceable class="parameter">report_interval</replaceable></option></arg>
+      <arg><option>-t <replaceable class="parameter">datasrc_type</replaceable></option></arg>
+      <arg><option>-C <replaceable class="parameter">zone_class</replaceable></option></arg>
+      <arg choice="req">zone name</arg>
+      <arg choice="req">zone file</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -63,11 +67,9 @@
 
     <para>
     Some control entries (aka directives) are supported.
-    $ORIGIN is followed by a domain name, and sets the the origin
+    $ORIGIN is followed by a domain name, and sets the origin
     that will be used for relative domain names in subsequent records.
     $INCLUDE is followed by a filename to load.
-<!-- TODO: and optionally a
-    domain name used to set the relative domain name origin. -->
     The previous origin is restored after the file is included.
 <!-- the current domain name is also restored -->
     $TTL is followed by a time-to-live value which is used
@@ -75,11 +77,31 @@
     </para>
 
     <para>
+      If the specified zone does not exist in the specified data
+      source, <command>b10-loadzone</command> will first create a
+      new empty zone in the data source, then fill it with the RRs
+      given in the specified master zone file.  In this case, if
+      loading fails for some reason, the creation of the new zone
+      is also canceled.
+      <note><simpara>
+	Due to an implementation limitation, the current version
+	does not make the zone creation and subsequent loading an
+	atomic operation; an empty zone will be visible and used by
+	other application (e.g., the <command>b10-auth</command>
+	authoritative server) while loading.  If this is an issue,
+	make sure the initial loading of a new zone is done before
+	starting other BIND 10 applications.
+      </simpara></note>
+    </para>
+
+    <para>
       When re-loading an existing zone, the prior version is completely
       removed.  While the new version of the zone is being loaded, the old
       version remains accessible to queries.  After the new version is
       completely loaded, the old version is swapped out and replaced
-      with the new one in a single operation.
+      with the new one in a single operation.  If loading fails for
+      some reason, the loaded RRs will be effectively deleted, and the
+      old version will still remain accessible for other applications.
     </para>
 
   </refsect1>
@@ -88,21 +110,82 @@
     <title>ARGUMENTS</title>
 
     <variablelist>
+      <varlistentry>
+        <term>-c <replaceable class="parameter">datasrc_config</replaceable></term>
+        <listitem><para>
+          Specifies configuration of the data source in the JSON
+          format.  The configuration contents depend on the type of
+	  the data source, and that's the same as what would be
+	  specified for the BIND 10 servers (see the data source
+          configuration section of the BIND 10 guide).  For example,
+	  for an SQLite3 data source, it would look like
+	  '{"database_file": "path-to-sqlite3-db-file"}'.
+	  <note>
+	    <simpara>For SQLite3 data source with the default DB file,
+	      this option can be omitted; in other cases including
+	      for any other types of data sources when supported,
+	      this option is currently mandatory in practice.
+	      In a future version it will be possible to retrieve the
+	      configuration from the BIND 10 server configuration (if
+	      it exists).
+	  </simpara></note>
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-d <replaceable class="parameter">debug_level</replaceable> </term>
+        <listitem><para>
+	    Enable dumping debug level logging with the specified
+	    level.  By default, only log messages at the severity of
+	    informational or higher levels will be produced.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>-i <replaceable class="parameter">report_interval</replaceable></term>
+        <listitem><para>
+          Specifies the interval of status update by the number of RRs
+	  loaded in the interval.
+	  The <command>b10-loadzone</command> tool periodically
+          reports the progress of loading with the total number of
+          loaded RRs and elapsed time.  This option specifies the
+	  interval of the reports.  If set to 0, status reports will
+          be suppressed.  The default is 10,000.
+        </para></listitem>
+      </varlistentry>
 
       <varlistentry>
-        <term>-d <replaceable class="parameter">database</replaceable> </term>
+        <term>-t <replaceable class="parameter">datasrc_type</replaceable></term>
         <listitem><para>
-          Defines the filename for the database.
-	  The default is
-	  <filename>/usr/local/var/bind10-devel/zone.sqlite3</filename>.
-<!-- TODO: fix filename -->
+          Specifies the type of data source to store the zone.
+	  Currently, only the "sqlite3" type is supported (which is
+          the default of this option), which means the SQLite3 data
+          source.
         </para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>-o <replaceable class="parameter">origin</replaceable></term>
+        <term>-C <replaceable class="parameter">zone_class</replaceable></term>
         <listitem><para>
-          Defines the default origin for the zone file records.
+          Specifies the RR class of the zone.
+	  Currently, only class IN is supported (which is the default
+          of this option) due to limitation of the underlying data
+          source implementation.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><replaceable class="parameter">zone name</replaceable></term>
+        <listitem><para>
+          The name of the zone to create or update.  This must be a valid DNS
+	  domain name.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><replaceable class="parameter">zone file</replaceable></term>
+        <listitem><para>
+          A path to the master zone file to be loaded.
         </para></listitem>
       </varlistentry>
 
@@ -131,8 +214,31 @@
   <refsect1>
     <title>AUTHORS</title>
     <para>
-      The <command>b10-loadzone</command> tool was initial written
-      by Evan Hunt of ISC.
+      A prior version of the <command>b10-loadzone</command> tool was
+      written by Evan Hunt of ISC.
+      The new version that this manual refers to was rewritten from
+      the scratch by the BIND 10 development team in around December 2012.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>BUGS</title>
+    <para>
+      As of the initial implementation, the underlying library that
+      this tool uses does not fully validate the loaded zone; for
+      example, loading will succeed even if it doesn't have the SOA or
+      NS record at its origin name.  Such checks will be implemented
+      in a near future version, but until then, the
+      <command>b10-loadzone</command> performs the existence of the
+      SOA and NS records by itself.  However, <command>b10-loadzone</command>
+      only warns about it, and does not cancel the load itself.
+      If this warning message is produced, it's the user's
+      responsibility to fix the errors and reload it.  When the
+      library is updated with the post load checks, it will be more
+      sophisticated and the such zone won't be successfully loaded.
+    </para>
+    <para>
+      There are some other issues noted in the DESCRIPTION section.
     </para>
   </refsect1>
 </refentry><!--
diff --git a/src/bin/loadzone/loadzone.py.in b/src/bin/loadzone/loadzone.py.in
new file mode 100755
index 0000000..294df55
--- /dev/null
+++ b/src/bin/loadzone/loadzone.py.in
@@ -0,0 +1,342 @@
+#!@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.
+
+import sys
+sys.path.append('@@PYTHONPATH@@')
+import time
+import signal
+from optparse import OptionParser
+from isc.dns import *
+from isc.datasrc import *
+import isc.util.process
+import isc.log
+from isc.log_messages.loadzone_messages import *
+
+isc.util.process.rename()
+
+# These are needed for logger settings
+import bind10_config
+import json
+from isc.config import module_spec_from_file
+from isc.config.ccsession import path_search
+
+isc.log.init("b10-loadzone")
+logger = isc.log.Logger("loadzone")
+
+# The default value for the interval of progress report in terms of the
+# number of RRs loaded in that interval.  Arbitrary choice, but intended to
+# be reasonably small to handle emergency exit.
+LOAD_INTERVAL_DEFAULT = 10000
+
+class BadArgument(Exception):
+    '''An exception indicating an error in command line argument.
+
+    '''
+    pass
+
+class LoadFailure(Exception):
+    '''An exception indicating failure in loading operation.
+
+    '''
+    pass
+
+def set_cmd_options(parser):
+    '''Helper function to set command-line options.
+
+    '''
+    parser.add_option("-c", "--datasrc-conf", dest="conf", action="store",
+                      help="""configuration of datasrc to load the zone in.
+Example: '{"database_file": "/path/to/dbfile/db.sqlite3"}'""",
+                      metavar='CONFIG')
+    parser.add_option("-d", "--debug", dest="debug_level",
+                      type='int', action="store", default=None,
+                      help="enable debug logs with the specified level [0-99]")
+    parser.add_option("-i", "--report-interval", dest="report_interval",
+                      type='int', action="store",
+                      default=LOAD_INTERVAL_DEFAULT,
+                      help="""report logs progress per specified number of RRs
+(specify 0 to suppress report) [default: %default]""")
+    parser.add_option("-t", "--datasrc-type", dest="datasrc_type",
+                      action="store", default='sqlite3',
+                      help="""type of data source (e.g., 'sqlite3')\n
+[default: %default]""")
+    parser.add_option("-C", "--class", dest="zone_class", action="store",
+                      default='IN',
+                      help="""RR class of the zone; currently must be 'IN'
+[default: %default]""")
+
+class LoadZoneRunner:
+    '''Main logic for the loadzone.
+
+    This is implemented as a class mainly for the convenience of tests.
+
+    '''
+    def __init__(self, command_args):
+        self.__command_args = command_args
+        self.__loaded_rrs = 0
+        self.__interrupted = False # will be set to True on receiving signal
+
+        # system-wide log configuration.  We need to configure logging this
+        # way so that the logging policy applies to underlying libraries, too.
+        self.__log_spec = json.dumps(isc.config.module_spec_from_file(
+                path_search('logging.spec', bind10_config.PLUGIN_PATHS)).
+                                     get_full_spec())
+        # "severity" and "debuglevel" are the tunable parameters, which will
+        # be set in _config_log().
+        self.__log_conf_base = {"loggers":
+                                    [{"name": "*",
+                                      "output_options":
+                                          [{"output": "stderr",
+                                            "destination": "console"}]}]}
+
+        # These are essentially private, and defined as "protected" for the
+        # convenience of tests inspecting them
+        self._zone_class = None
+        self._zone_name = None
+        self._zone_file = None
+        self._datasrc_config = None
+        self._datasrc_type = None
+        self._log_severity = 'INFO'
+        self._log_debuglevel = 0
+        self._report_interval = LOAD_INTERVAL_DEFAULT
+
+        self._config_log()
+
+    def _config_log(self):
+        '''Configure logging policy.
+
+        This is essentially private, but defined as "protected" for tests.
+
+        '''
+        self.__log_conf_base['loggers'][0]['severity'] = self._log_severity
+        self.__log_conf_base['loggers'][0]['debuglevel'] = self._log_debuglevel
+        isc.log.log_config_update(json.dumps(self.__log_conf_base),
+                                  self.__log_spec)
+
+    def _parse_args(self):
+        '''Parse command line options and other arguments.
+
+        This is essentially private, but defined as "protected" for tests.
+
+        '''
+
+        usage_txt = \
+            'usage: %prog [options] -c datasrc_config zonename zonefile'
+        parser = OptionParser(usage=usage_txt)
+        set_cmd_options(parser)
+        (options, args) = parser.parse_args(args=self.__command_args)
+
+        # Configure logging policy as early as possible
+        if options.debug_level is not None:
+            self._log_severity = 'DEBUG'
+            # optparse performs type check
+            self._log_debuglevel = int(options.debug_level)
+            if self._log_debuglevel < 0:
+                raise BadArgument(
+                    'Invalid debug level (must be non negative): %d' %
+                    self._log_debuglevel)
+        self._config_log()
+
+        self._datasrc_type = options.datasrc_type
+        self._datasrc_config = options.conf
+        if options.conf is None:
+            self._datasrc_config = self._get_datasrc_config(self._datasrc_type)
+        try:
+            self._zone_class = RRClass(options.zone_class)
+        except isc.dns.InvalidRRClass as ex:
+            raise BadArgument('Invalid zone class: ' + str(ex))
+        if self._zone_class != RRClass.IN():
+            raise BadArgument("RR class is not supported: " +
+                              str(self._zone_class))
+
+        self._report_interval = int(options.report_interval)
+        if self._report_interval < 0:
+            raise BadArgument(
+                'Invalid report interval (must be non negative): %d' %
+                self._report_interval)
+
+        if len(args) != 2:
+            raise BadArgument('Unexpected number of arguments: %d (must be 2)'
+                              % (len(args)))
+        try:
+            self._zone_name = Name(args[0])
+        except Exception as ex: # too broad, but there's no better granurality
+            raise BadArgument("Invalid zone name '" + args[0] + "': " +
+                              str(ex))
+        self._zone_file = args[1]
+
+    def _get_datasrc_config(self, datasrc_type):
+        ''''Return the default data source configuration of given type.
+
+        Right now, it only supports SQLite3, and hardcodes the syntax
+        of the default configuration.  It's a kind of workaround to balance
+        convenience of users and minimizing hardcoding of data source
+        specific logic in the entire tool.  In future this should be
+        more sophisticated.
+
+        This is essentially a private helper method for _parse_arg(),
+        but defined as "protected" so tests can use it directly.
+
+        '''
+        if datasrc_type != 'sqlite3':
+            raise BadArgument('default config is not available for ' +
+                              datasrc_type)
+
+        default_db_file = bind10_config.DATA_PATH + '/zone.sqlite3'
+        logger.info(LOADZONE_SQLITE3_USING_DEFAULT_CONFIG, default_db_file)
+        return '{"database_file": "' + default_db_file + '"}'
+
+    def __cancel_create(self):
+        '''sqlite3-only hack: delete the zone just created on load failure.
+
+        This should eventually be done via generic datasrc API, but right now
+        we don't have that interface.  Leaving the zone in this situation
+        is too bad, so we handle it with a workaround.
+
+        '''
+        if self._datasrc_type is not 'sqlite3':
+            return
+
+        import sqlite3          # we need the module only here
+        import json
+
+        # If we are here, the following should basically succeed; since
+        # this is considered a temporary workaround we don't bother to catch
+        # and recover rare failure cases.
+        dbfile = json.loads(self._datasrc_config)['database_file']
+        with sqlite3.connect(dbfile) as conn:
+            cur = conn.cursor()
+            cur.execute("DELETE FROM zones WHERE name = ?",
+                        [self._zone_name.to_text()])
+
+    def _report_progress(self, loaded_rrs):
+        '''Dump the current progress report to stdout.
+
+        This is essentially private, but defined as "protected" for tests.
+
+        '''
+        elapsed = time.time() - self.__start_time
+        sys.stdout.write("\r" + (80 * " "))
+        sys.stdout.write("\r%d RRs loaded in %.2f seconds" %
+                         (loaded_rrs, elapsed))
+
+    def _do_load(self):
+        '''Main part of the load logic.
+
+        This is essentially private, but defined as "protected" for tests.
+
+        '''
+        created = False
+        try:
+            datasrc_client = DataSourceClient(self._datasrc_type,
+                                              self._datasrc_config)
+            created = datasrc_client.create_zone(self._zone_name)
+            if created:
+                logger.info(LOADZONE_ZONE_CREATED, self._zone_name,
+                            self._zone_class)
+            else:
+                logger.info(LOADZONE_ZONE_UPDATING, self._zone_name,
+                            self._zone_class)
+            loader = ZoneLoader(datasrc_client, self._zone_name,
+                                self._zone_file)
+            self.__start_time = time.time()
+            if self._report_interval > 0:
+                limit = self._report_interval
+            else:
+                # Even if progress report is suppressed, we still load
+                # incrementally so we won't delay catching signals too long.
+                limit = LOAD_INTERVAL_DEFAULT
+            while (not self.__interrupted and
+                   not loader.load_incremental(limit)):
+                self.__loaded_rrs += self._report_interval
+                if self._report_interval > 0:
+                    self._report_progress(self.__loaded_rrs)
+            if self.__interrupted:
+                raise LoadFailure('loading interrupted by signal')
+
+            # On successful completion, add final '\n' to the progress
+            # report output (on failure don't bother to make it prettier).
+            if (self._report_interval > 0 and
+                self.__loaded_rrs >= self._report_interval):
+                sys.stdout.write('\n')
+        except Exception as ex:
+            # release any remaining lock held in the client/loader
+            loader, datasrc_client = None, None
+            if created:
+                self.__cancel_create()
+                logger.error(LOADZONE_CANCEL_CREATE_ZONE, self._zone_name,
+                             self._zone_class)
+            raise LoadFailure(str(ex))
+
+    def _post_load_checks(self):
+        '''Perform minimal validity checks on the loaded zone.
+
+        We do this ourselves because the underlying library currently
+        doesn't do any checks.  Once the library support post-load validation
+        this check should be removed.
+
+        '''
+        datasrc_client = DataSourceClient(self._datasrc_type,
+                                          self._datasrc_config)
+        _, finder = datasrc_client.find_zone(self._zone_name) # should succeed
+        result = finder.find(self._zone_name, RRType.SOA())[0]
+        if result is not finder.SUCCESS:
+            self._post_load_warning('zone has no SOA')
+        result = finder.find(self._zone_name, RRType.NS())[0]
+        if result is not finder.SUCCESS:
+            self._post_load_warning('zone has no NS')
+
+    def _post_load_warning(self, msg):
+        logger.warn(LOADZONE_POSTLOAD_ISSUE, self._zone_name,
+                    self._zone_class, msg)
+
+    def _set_signal_handlers(self):
+        signal.signal(signal.SIGINT, self._interrupt_handler)
+        signal.signal(signal.SIGTERM, self._interrupt_handler)
+
+    def _interrupt_handler(self, signal, frame):
+        self.__interrupted = True
+
+    def run(self):
+        '''Top-level method, simply calling other helpers'''
+
+        try:
+            self._set_signal_handlers()
+            self._parse_args()
+            self._do_load()
+            total_elapsed_txt = "%.2f" % (time.time() - self.__start_time)
+            logger.info(LOADZONE_DONE, self.__loaded_rrs, self._zone_name,
+                        self._zone_class, total_elapsed_txt)
+            self._post_load_checks()
+            return 0
+        except BadArgument as ex:
+            logger.error(LOADZONE_ARGUMENT_ERROR, ex)
+        except LoadFailure as ex:
+            logger.error(LOADZONE_LOAD_ERROR, self._zone_name,
+                         self._zone_class, ex)
+        except Exception as ex:
+            logger.error(LOADZONE_UNEXPECTED_FAILURE, ex)
+        return 1
+
+if '__main__' == __name__:
+    runner = LoadZoneRunner(sys.argv[1:])
+    ret = runner.run()
+    sys.exit(ret)
+
+## Local Variables:
+## mode: python
+## End:
diff --git a/src/bin/loadzone/loadzone_messages.mes b/src/bin/loadzone/loadzone_messages.mes
new file mode 100644
index 0000000..db79269
--- /dev/null
+++ b/src/bin/loadzone/loadzone_messages.mes
@@ -0,0 +1,81 @@
+# 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.
+
+# 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.
+
+% LOADZONE_ARGUMENT_ERROR Error in command line arguments: %1
+Some semantics error in command line arguments or options to b10-loadzone
+is detected.  b10-loadzone does effectively nothing and immediately
+terminates.
+
+% LOADZONE_CANCEL_CREATE_ZONE Creation of new zone %1/%2 was canceled
+b10-loadzone has created a new zone in the data source (see
+LOADZONE_ZONE_CREATED), but the loading operation has subsequently
+failed.  The newly created zone has been removed from the data source,
+so that the data source will go back to the original state.
+
+% LOADZONE_DONE Loaded (at least) %1 RRs into zone %2/%3 in %4 seconds
+b10-loadzone has successfully loaded the specified zone.  If there was
+an old version of the zone in the data source, it is now deleted.
+It also prints (a lower bound of) the number of RRs that have been loaded
+and the time spent for the loading.  Due to a limitation of the
+current implementation of the underlying library however, it cannot show the
+exact number of the loaded RRs; it's counted for every N-th RR where N
+is the value of the -i command line option.  So, for smaller zones that
+don't even contain N RRs, the reported value will be 0.  This will be
+improved in a future version.
+
+% LOADZONE_LOAD_ERROR Failed to load zone %1/%2: %3
+Loading a zone by b10-loadzone fails for some reason in the middle of
+the loading.  This is most likely due to an error in the specified
+arguments to b10-loadzone (such as non-existent zone file) or an error
+in the zone file.  When this happens, the RRs loaded so far are
+effectively deleted from the zone, and the old version (if exists)
+will still remain valid for operations.
+
+% LOADZONE_POSTLOAD_ISSUE New version of zone %1/%2 has an issue: %3
+b10-loadzone detected a problem after a successful load of zone:
+either or both of SOA and NS records are missing at the zone origin.
+In the current implementation the load will not be canceled for such
+problems.  The operator will need to fix the issues and reload the
+zone; otherwise applications (such as b10-auth) that use this data
+source will not work as expected.
+
+% LOADZONE_SQLITE3_USING_DEFAULT_CONFIG Using default configuration with SQLite3 DB file %1
+The SQLite3 data source is specified as the data source type without a
+data source configuration.  b10-loadzone uses the default
+configuration with the default DB file for the BIND 10 system.
+
+% LOADZONE_UNEXPECTED_FAILURE Unexpected exception: %1
+b10-loadzone encounters an unexpected failure and terminates itself.
+This is generally a bug of b10-loadzone itself or the underlying
+data source library, so it's advisable to submit a bug report if
+this message is logged.  The incomplete attempt of loading should
+have been cleanly canceled in this case, too.
+
+% LOADZONE_ZONE_CREATED Zone %1/%2 does not exist in the data source, newly created
+The specified zone to b10-loadzone to load does not exist in the
+specified data source.  b10-loadzone has created a new empty zone
+in the data source.
+
+% LOADZONE_ZONE_UPDATING Started updating zone %1/%2 with removing old data (this can take a while)
+b10-loadzone started loading a new version of the zone as specified,
+beginning with removing the current contents of the zone (in a
+transaction, so the removal won't take effect until and unless the entire
+load is completed successfully).  If the old version of the zone is large,
+this can take time, such as a few minutes or more, without any visible
+feedback.  This is not a problem as long as the b10-loadzone process
+is working at a moderate load.
diff --git a/src/bin/loadzone/run_loadzone.sh.in b/src/bin/loadzone/run_loadzone.sh.in
index 43b7920..b3d61d3 100755
--- a/src/bin/loadzone/run_loadzone.sh.in
+++ b/src/bin/loadzone/run_loadzone.sh.in
@@ -18,7 +18,7 @@
 PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs
 export PYTHONPATH
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
@@ -32,5 +32,13 @@ fi
 BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
 export BIND10_MSGQ_SOCKET_FILE
 
+# For bind10_config
+B10_FROM_SOURCE=@abs_top_srcdir@
+export B10_FROM_SOURCE
+
+# For data source loadable modules
+B10_FROM_BUILD=@abs_top_builddir@
+export B10_FROM_BUILD
+
 LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
 exec ${LOADZONE_PATH}/b10-loadzone "$@"
diff --git a/src/bin/loadzone/tests/Makefile.am b/src/bin/loadzone/tests/Makefile.am
new file mode 100644
index 0000000..8459f83
--- /dev/null
+++ b/src/bin/loadzone/tests/Makefile.am
@@ -0,0 +1,37 @@
+SUBDIRS = . correct
+
+PYCOVERAGE_RUN=@PYCOVERAGE_RUN@
+PYTESTS = loadzone_test.py
+
+EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST += testdata/example.org.zone
+EXTRA_DIST += testdata/broken-example.org.zone
+EXTRA_DIST += testdata/example-nosoa.org.zone
+EXTRA_DIST += testdata/example-nons.org.zone
+
+# 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/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$(abs_top_builddir)/src/lib/acl/.libs:$$$(ENV_LIBRARY_PATH)
+endif
+
+# test using command-line arguments, so use check-local target instead of TESTS
+# We need to define B10_FROM_BUILD for datasrc loadable modules
+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 ; \
+	B10_FROM_SOURCE=$(abs_top_srcdir) \
+	B10_FROM_BUILD=$(abs_top_builddir) \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	TESTDATA_PATH=$(abs_top_srcdir)/src/lib/testutils/testdata \
+	LOCAL_TESTDATA_PATH=$(srcdir)/testdata \
+	TESTDATA_WRITE_PATH=$(builddir) \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/loadzone:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
+	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
+	done
diff --git a/src/bin/loadzone/tests/correct/Makefile.am b/src/bin/loadzone/tests/correct/Makefile.am
index a3c67d4..7ed500d 100644
--- a/src/bin/loadzone/tests/correct/Makefile.am
+++ b/src/bin/loadzone/tests/correct/Makefile.am
@@ -26,5 +26,8 @@ endif
 # TODO: maybe use TESTS?
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
-	echo Running test: correct_test.sh 
+	echo Running test: correct_test.sh
+	B10_FROM_SOURCE=$(abs_top_srcdir) \
+	B10_FROM_BUILD=$(abs_top_builddir) \
+	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/loadzone:$(abs_top_builddir)/src/lib/dns/python/.libs \
 	$(LIBRARY_PATH_PLACEHOLDER) $(SHELL) $(abs_builddir)/correct_test.sh
diff --git a/src/bin/loadzone/tests/correct/correct_test.sh.in b/src/bin/loadzone/tests/correct/correct_test.sh.in
index e3f6a84..9b90d13 100755
--- a/src/bin/loadzone/tests/correct/correct_test.sh.in
+++ b/src/bin/loadzone/tests/correct/correct_test.sh.in
@@ -18,7 +18,7 @@
 PYTHON_EXEC=${PYTHON_EXEC:- at PYTHON@}
 export PYTHON_EXEC
 
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python:$PYTHONPATH
 export PYTHONPATH
 
 LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
@@ -28,28 +28,28 @@ TEST_OUTPUT_PATH=@abs_top_builddir@/src/bin/loadzone//tests/correct
 status=0
 echo "Loadzone include. from include.db file"
 cd ${TEST_FILE_PATH}
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 include.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' include. include.db >> /dev/null
 
 echo "loadzone  ttl1. from ttl1.db file"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 ttl1.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' ttl1. ttl1.db >> /dev/null
 
 echo "loadzone ttl2. from ttl2.db file"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 ttl2.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' ttl2. ttl2.db >> /dev/null
 
 echo "loadzone mix1. from mix1.db"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 mix1.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' mix1. mix1.db >> /dev/null
 
 echo "loadzone mix2. from mix2.db"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 mix2.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' mix2. mix2.db >> /dev/null
 
 echo "loadzone ttlext. from ttlext.db"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 ttlext.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' ttlext. ttlext.db >> /dev/null
 
 echo "loadzone example.com. from example.db"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 example.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' example.com. example.db >> /dev/null
 
 echo "loadzone comment.example.com. from comment.db"
-${LOADZONE_PATH}/b10-loadzone -d ${TEST_OUTPUT_PATH}/zone.sqlite3 comment.db >> /dev/null
+${LOADZONE_PATH}/b10-loadzone -c '{"database_file": "'${TEST_OUTPUT_PATH}/zone.sqlite3'"}' comment.example.com. comment.db >> /dev/null
 
 echo "I:test master file \$INCLUDE semantics"
 echo "I:test master file BIND 8 compatibility TTL and \$TTL semantics"
diff --git a/src/bin/loadzone/tests/correct/example.db b/src/bin/loadzone/tests/correct/example.db
index fe012cf..38d1329 100644
--- a/src/bin/loadzone/tests/correct/example.db
+++ b/src/bin/loadzone/tests/correct/example.db
@@ -2,11 +2,17 @@
 $ORIGIN example.com.
 $TTL 60
 @    IN SOA   ns1.example.com. hostmaster.example.com. (1 43200 900 1814400 7200)
-     IN     20      NS  ns1
-                    NS  ns2
+; these need #2390
+;     IN     20      NS  ns1
+;                    NS  ns2
+     IN     20      NS  ns1.example.com.
+                    NS  ns2.example.com.
 ns1  IN     30      A   192.168.1.102
-            70      NS  ns3
-     IN             NS  ns4
+; these need #2390
+;            70      NS  ns3
+;     IN             NS  ns4
+            70      NS  ns3.example.com.
+     IN             NS  ns4.example.com.
      10     IN      MX  10  mail.example.com.
 ns2         80      A   1.1.1.1
 ns3  IN             A   2.2.2.2
diff --git a/src/bin/loadzone/tests/correct/include.db b/src/bin/loadzone/tests/correct/include.db
index f60a240..a9eeca3 100644
--- a/src/bin/loadzone/tests/correct/include.db
+++ b/src/bin/loadzone/tests/correct/include.db
@@ -1,13 +1,17 @@
 $ORIGIN include.   ; initialize origin
 $TTL 300
-@			IN SOA	ns hostmaster (
+; this needs #2500
+;@			IN SOA	ns hostmaster (
+@			IN SOA	ns.include. hostmaster.include. (
 				1        ; serial
 				3600
 				1800
 				1814400
 				3600
 				)
-			NS	ns
+; this needs #2390
+;			NS	ns
+			NS	ns.include.
 
 ns			A	127.0.0.1
 
diff --git a/src/bin/loadzone/tests/correct/known.test.out b/src/bin/loadzone/tests/correct/known.test.out
index eec692e..377158d 100644
--- a/src/bin/loadzone/tests/correct/known.test.out
+++ b/src/bin/loadzone/tests/correct/known.test.out
@@ -80,6 +80,6 @@ ns5.example.com.		90	IN	A	4.4.4.4
 comment.example.com.		60	IN	SOA	ns1.example.com. hostmaster.example.com. 1 43200 900 1814400 7200
 comment.example.com.		60	IN	NS	ns1.example.com.
 comment.example.com.		60	IN	TXT	"Simple text"
-comment.example.com.		60	IN	TXT	"; No comment"
+comment.example.com.		60	IN	TXT	"\; No comment"
 comment.example.com.		60	IN	TXT	"Also no comment here"
-comment.example.com.		60	IN	TXT	"A combination ; see?"
+comment.example.com.		60	IN	TXT	"A combination \; see?"
diff --git a/src/bin/loadzone/tests/correct/mix1.db b/src/bin/loadzone/tests/correct/mix1.db
index a9d58a8..5bc0a95 100644
--- a/src/bin/loadzone/tests/correct/mix1.db
+++ b/src/bin/loadzone/tests/correct/mix1.db
@@ -1,12 +1,16 @@
 $ORIGIN mix1.
-@			IN SOA	ns hostmaster (
+; this needs #2500
+;@			IN SOA	ns hostmaster (
+@			IN SOA	ns.mix1. hostmaster.mix1. (
 				1        ; serial
 				3600
 				1800
 				1814400
 				3
 				)
-			NS	ns
+; this needs #2390
+;			NS	ns
+			NS	ns.mix1.
 ns			A	10.53.0.1
 a			TXT	"soa minttl 3"
 b		2	TXT	"explicit ttl 2"
diff --git a/src/bin/loadzone/tests/correct/mix2.db b/src/bin/loadzone/tests/correct/mix2.db
index 2c8153d..e43b943 100644
--- a/src/bin/loadzone/tests/correct/mix2.db
+++ b/src/bin/loadzone/tests/correct/mix2.db
@@ -1,12 +1,16 @@
 $ORIGIN mix2.
-@		1	IN SOA	ns hostmaster (
+; this needs #2500
+;@		1	IN SOA	ns hostmaster (
+@		1	IN SOA	ns.mix2. hostmaster.mix2. (
 				1        ; serial
 				3600
 				1800
 				1814400
 				3
 				)
-			NS	ns
+; this needs #2390
+;			NS	ns
+			NS	ns.mix2.
 ns			A	10.53.0.1
 a			TXT	"inherited ttl 1"
 $INCLUDE mix2sub1.txt
diff --git a/src/bin/loadzone/tests/correct/mix2sub2.txt b/src/bin/loadzone/tests/correct/mix2sub2.txt
index 96d53c1..7e4292a 100644
--- a/src/bin/loadzone/tests/correct/mix2sub2.txt
+++ b/src/bin/loadzone/tests/correct/mix2sub2.txt
@@ -1,3 +1,3 @@
-f                       TXT     "default  ttl 3"
+f                       TXT     "default ttl 3"
 $TTL 5
-g                       TXT     "default  ttl 5"
+g                       TXT     "default ttl 5"
diff --git a/src/bin/loadzone/tests/correct/ttl1.db b/src/bin/loadzone/tests/correct/ttl1.db
index aa6e2bb..fec0813 100644
--- a/src/bin/loadzone/tests/correct/ttl1.db
+++ b/src/bin/loadzone/tests/correct/ttl1.db
@@ -1,12 +1,16 @@
 $ORIGIN ttl1.
-@			IN SOA	ns hostmaster (
+; this needs #2500
+;@			IN SOA	ns hostmaster (
+@			IN SOA	ns.ttl1. hostmaster.ttl1. (
 				1        ; serial
 				3600
 				1800
 				1814400
 				3
 				)
-			NS	ns
+; this needs #2390
+;			NS	ns
+			NS	ns.ttl1.
 ns			A	10.53.0.1
 a			TXT	"soa minttl 3"
 b		2	TXT	"explicit ttl 2"
diff --git a/src/bin/loadzone/tests/correct/ttl2.db b/src/bin/loadzone/tests/correct/ttl2.db
index f7f6eee..4705978 100644
--- a/src/bin/loadzone/tests/correct/ttl2.db
+++ b/src/bin/loadzone/tests/correct/ttl2.db
@@ -1,12 +1,16 @@
 $ORIGIN ttl2.
-@		1	IN SOA	ns hostmaster (
+; this needs #2500
+;@		1	IN SOA	ns hostmaster (
+@		1	IN SOA	ns.ttl2. hostmaster.ttl2 (
 				1        ; serial
 				3600
 				1800
 				1814400
 				3
 				)
-			NS	ns
+; this needs #2390
+;			NS	ns
+			NS	ns.ttl2.
 ns			A	10.53.0.1
 a			TXT	"inherited ttl 1"
 b		2	TXT	"explicit ttl 2"
diff --git a/src/bin/loadzone/tests/correct/ttlext.db b/src/bin/loadzone/tests/correct/ttlext.db
index f8b96ea..f8c83b0 100644
--- a/src/bin/loadzone/tests/correct/ttlext.db
+++ b/src/bin/loadzone/tests/correct/ttlext.db
@@ -1,12 +1,16 @@
 $ORIGIN ttlext.
-@			IN SOA	ns hostmaster (
+; this needs #2500
+;@			IN SOA	ns hostmaster (
+@			IN SOA	ns.ttlext. hostmaster.ttlext. (
 				1        ; serial
 				3600
 				1800
 				1814400
 				3
 				)
-			NS	ns
+; this needs #2390
+;			NS	ns
+			NS	ns.ttlext.
 ns			A	10.53.0.1
 a			TXT	"soa minttl 3"
 b		2S	TXT	"explicit ttl 2"
diff --git a/src/bin/loadzone/tests/error/.gitignore b/src/bin/loadzone/tests/error/.gitignore
deleted file mode 100644
index 5d20adb..0000000
--- a/src/bin/loadzone/tests/error/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/error_test.sh
diff --git a/src/bin/loadzone/tests/error/Makefile.am b/src/bin/loadzone/tests/error/Makefile.am
deleted file mode 100644
index 03263b7..0000000
--- a/src/bin/loadzone/tests/error/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-EXTRA_DIST = error.known
-EXTRA_DIST += formerr1.db 
-EXTRA_DIST += formerr2.db
-EXTRA_DIST += formerr3.db
-EXTRA_DIST += formerr4.db
-EXTRA_DIST += formerr5.db
-EXTRA_DIST += include.txt
-EXTRA_DIST += keyerror1.db
-EXTRA_DIST += keyerror2.db
-EXTRA_DIST += keyerror3.db
-#EXTRA_DIST += nofilenane.db
-EXTRA_DIST += originerr1.db
-EXTRA_DIST += originerr2.db
-
-noinst_SCRIPTS = error_test.sh
-
-# 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/util/io/.libs:$(abs_top_builddir)/src/lib/datasrc/.libs:$$$(ENV_LIBRARY_PATH)
-endif
-
-# TODO: use TESTS ?
-# test using command-line arguments, so use check-local target instead of TESTS
-check-local:
-	echo Running test: error_test.sh
-	$(LIBRARY_PATH_PLACEHOLDER) $(SHELL) $(abs_builddir)/error_test.sh
diff --git a/src/bin/loadzone/tests/error/error.known b/src/bin/loadzone/tests/error/error.known
deleted file mode 100644
index cdbbed2..0000000
--- a/src/bin/loadzone/tests/error/error.known
+++ /dev/null
@@ -1,11 +0,0 @@
-Error reading zone file: Cannot parse RR, No $ORIGIN: @ IN SOA ns hostmaster 1 3600 1800 1814400 3600
-Error reading zone file: $ORIGIN is not absolute in record: $ORIGIN com
-Error reading zone file: Cannot parse RR: $TL 300
-Error reading zone file: Cannot parse RR: $OIGIN com.
-Error loading database: Error while loading com.: Cannot parse RR: $INLUDE file.txt
-Error loading database: Error while loading com.: Invalid $include format
-Error loading database: Error while loading com.: Cannot parse RR, No $ORIGIN:  include.txt sub
-Error reading zone file: Invalid TTL: ""
-Error reading zone file: Invalid TTL: "M"
-Error loading database: Error while loading com.: Cannot parse RR: b "no type error!"
-Error reading zone file: Could not open bogusfile
diff --git a/src/bin/loadzone/tests/error/error_test.sh.in b/src/bin/loadzone/tests/error/error_test.sh.in
deleted file mode 100755
index 94c5edb..0000000
--- a/src/bin/loadzone/tests/error/error_test.sh.in
+++ /dev/null
@@ -1,82 +0,0 @@
-#! /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
-
-PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-LOADZONE_PATH=@abs_top_builddir@/src/bin/loadzone
-TEST_OUTPUT_PATH=@abs_top_builddir@/src/bin/loadzone/tests/error
-TEST_FILE_PATH=@abs_top_srcdir@/src/bin/loadzone/tests/error
-
-cd ${LOADZONE_PATH}/tests/error
-
-export LOADZONE_PATH
-status=0
-
-echo "PYTHON PATH: $PYTHONPATH"
-
-echo "Test no \$ORIGIN error in zone file"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/originerr1.db 1> /dev/null 2> error.out
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/originerr2.db 1> /dev/null 2>> error.out
-
-echo "Test: key word TTL spell error"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/keyerror1.db 1> /dev/null 2>> error.out
-
-echo "Test: key word ORIGIN spell error"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/keyerror2.db 1> /dev/null 2>> error.out
-
-echo "Test: key INCLUDE spell error"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/keyerror3.db 1> /dev/null 2>> error.out
-
-echo "Test: include formal error, miss filename"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/formerr1.db 1> /dev/null 2>>error.out
-
-echo "Test: include form error, domain is not absolute"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/formerr2.db 1> /dev/null 2>> error.out
-
-echo "Test: TTL form error, no ttl value"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/formerr3.db 1> /dev/null 2>> error.out
-
-echo "Test: TTL form error, ttl value error"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/formerr4.db 1> /dev/null 2>> error.out
-
-echo "Test: rr form error, no type"
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  ${TEST_FILE_PATH}/formerr5.db 1> /dev/null 2>> error.out
-
-echo "Test: zone file is bogus"
-# since bogusfile doesn't exist anyway, we *don't* specify the directory
-${LOADZONE_PATH}/b10-loadzone -d zone.sqlite3  bogusfile 1> /dev/null 2>> error.out
-
-diff error.out ${TEST_FILE_PATH}/error.known || status=1
-
-echo "Clean tmp file."
-rm -f error.out
-rm -f zone.sqlite3
-
-echo "I:exit status:$status"
-echo "-----------------------------------------------------------------------------"
-echo "Ran 11 test files"
-echo ""
-if [ "$status" -eq 1 ];then
-    echo "ERROR"
-else 
-    echo "OK"
-fi
-exit $status
diff --git a/src/bin/loadzone/tests/error/formerr1.db b/src/bin/loadzone/tests/error/formerr1.db
deleted file mode 100644
index 9bab49f..0000000
--- a/src/bin/loadzone/tests/error/formerr1.db
+++ /dev/null
@@ -1,13 +0,0 @@
-$TTL 300
-$ORIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-$INCLUDE
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/formerr2.db b/src/bin/loadzone/tests/error/formerr2.db
deleted file mode 100644
index 3d7dd48..0000000
--- a/src/bin/loadzone/tests/error/formerr2.db
+++ /dev/null
@@ -1,12 +0,0 @@
-$TTL 300
-com.			IN SOA	ns.com. hostmaster.com. (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns.example.com.
-ns.com.			A	127.0.0.1
-$INCLUDE include.txt sub
-a.com.			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/formerr3.db b/src/bin/loadzone/tests/error/formerr3.db
deleted file mode 100644
index c1c3975..0000000
--- a/src/bin/loadzone/tests/error/formerr3.db
+++ /dev/null
@@ -1,12 +0,0 @@
-$TTL 
-$ORIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/formerr4.db b/src/bin/loadzone/tests/error/formerr4.db
deleted file mode 100644
index d37515f..0000000
--- a/src/bin/loadzone/tests/error/formerr4.db
+++ /dev/null
@@ -1,12 +0,0 @@
-$TTL M
-$ORIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/formerr5.db b/src/bin/loadzone/tests/error/formerr5.db
deleted file mode 100644
index fa5983f..0000000
--- a/src/bin/loadzone/tests/error/formerr5.db
+++ /dev/null
@@ -1,13 +0,0 @@
-$TTL 2M
-$ORIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1 ; ip value
-b               "no type error!"
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/include.txt b/src/bin/loadzone/tests/error/include.txt
deleted file mode 100644
index 9b4c57c..0000000
--- a/src/bin/loadzone/tests/error/include.txt
+++ /dev/null
@@ -1 +0,0 @@
-a  300 A 127.0.0.1
diff --git a/src/bin/loadzone/tests/error/keyerror1.db b/src/bin/loadzone/tests/error/keyerror1.db
deleted file mode 100644
index 7384362..0000000
--- a/src/bin/loadzone/tests/error/keyerror1.db
+++ /dev/null
@@ -1,12 +0,0 @@
-$TL 300
- at ORIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/keyerror2.db b/src/bin/loadzone/tests/error/keyerror2.db
deleted file mode 100644
index 5c97e4e..0000000
--- a/src/bin/loadzone/tests/error/keyerror2.db
+++ /dev/null
@@ -1,12 +0,0 @@
-$TTL 300
-$OIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/keyerror3.db b/src/bin/loadzone/tests/error/keyerror3.db
deleted file mode 100644
index eebb0aa..0000000
--- a/src/bin/loadzone/tests/error/keyerror3.db
+++ /dev/null
@@ -1,13 +0,0 @@
-$TTL 300
-$ORIGIN com.
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-$INLUDE file.txt
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/originerr1.db b/src/bin/loadzone/tests/error/originerr1.db
deleted file mode 100644
index fc20edc..0000000
--- a/src/bin/loadzone/tests/error/originerr1.db
+++ /dev/null
@@ -1,11 +0,0 @@
-$TTL 300
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/error/originerr2.db b/src/bin/loadzone/tests/error/originerr2.db
deleted file mode 100644
index 2cb90eb..0000000
--- a/src/bin/loadzone/tests/error/originerr2.db
+++ /dev/null
@@ -1,12 +0,0 @@
-$TTL 300
-$ORIGIN com
-@			IN SOA	ns hostmaster (
-				1        ; serial
-				3600
-				1800
-				1814400
-				3600
-				)
-			NS	ns
-ns			A	127.0.0.1
-a			A	10.0.0.1
diff --git a/src/bin/loadzone/tests/loadzone_test.py b/src/bin/loadzone/tests/loadzone_test.py
new file mode 100755
index 0000000..4f13c5d
--- /dev/null
+++ b/src/bin/loadzone/tests/loadzone_test.py
@@ -0,0 +1,342 @@
+# 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.
+
+'''Tests for the loadzone module'''
+
+import unittest
+from loadzone import *
+from isc.dns import *
+from isc.datasrc import *
+import isc.log
+import bind10_config
+import os
+import shutil
+
+# Some common test parameters
+TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
+READ_ZONE_DB_FILE = TESTDATA_PATH + "rwtest.sqlite3" # original, to be copied
+LOCAL_TESTDATA_PATH = os.environ['LOCAL_TESTDATA_PATH'] + os.sep
+READ_ZONE_DB_FILE = TESTDATA_PATH + "rwtest.sqlite3" # original, to be copied
+NEW_ZONE_TXT_FILE = LOCAL_TESTDATA_PATH + "example.org.zone"
+ALT_NEW_ZONE_TXT_FILE = TESTDATA_PATH + "example.com.zone"
+TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH'] + os.sep
+WRITE_ZONE_DB_FILE = TESTDATA_WRITE_PATH + "rwtest.sqlite3.copied"
+TEST_ZONE_NAME = Name('example.org')
+DATASRC_CONFIG = '{"database_file": "' + WRITE_ZONE_DB_FILE + '"}'
+
+# before/after SOAs: different in mname and serial
+ORIG_SOA_TXT = 'example.org. 3600 IN SOA ns1.example.org. ' +\
+    'admin.example.org. 1234 3600 1800 2419200 7200\n'
+NEW_SOA_TXT = 'example.org. 3600 IN SOA ns.example.org. ' +\
+    'admin.example.org. 1235 3600 1800 2419200 7200\n'
+# This is the brandnew SOA for a newly created zone
+ALT_NEW_SOA_TXT = 'example.com. 3600 IN SOA ns.example.com. ' +\
+    'admin.example.com. 1234 3600 1800 2419200 7200\n'
+
+class TestLoadZoneRunner(unittest.TestCase):
+    def setUp(self):
+        shutil.copyfile(READ_ZONE_DB_FILE, WRITE_ZONE_DB_FILE)
+
+        # default command line arguments
+        self.__args = ['-c', DATASRC_CONFIG, 'example.org', NEW_ZONE_TXT_FILE]
+        self.__runner = LoadZoneRunner(self.__args)
+
+    def tearDown(self):
+        # Delete the used DB file; if some of the tests unexpectedly fail
+        # unexpectedly in the middle of updating the DB, a lock could stay
+        # there and would affect the other tests that would otherwise succeed.
+        os.unlink(WRITE_ZONE_DB_FILE)
+
+    def test_init(self):
+        '''
+        Checks initial class attributes
+        '''
+        self.assertIsNone(self.__runner._zone_class)
+        self.assertIsNone(self.__runner._zone_name)
+        self.assertIsNone(self.__runner._zone_file)
+        self.assertIsNone(self.__runner._datasrc_config)
+        self.assertIsNone(self.__runner._datasrc_type)
+        self.assertEqual(10000, self.__runner._report_interval)
+        self.assertEqual('INFO', self.__runner._log_severity)
+        self.assertEqual(0, self.__runner._log_debuglevel)
+
+    def test_parse_args(self):
+        self.__runner._parse_args()
+        self.assertEqual(TEST_ZONE_NAME, self.__runner._zone_name)
+        self.assertEqual(NEW_ZONE_TXT_FILE, self.__runner._zone_file)
+        self.assertEqual(DATASRC_CONFIG, self.__runner._datasrc_config)
+        self.assertEqual('sqlite3', self.__runner._datasrc_type) # default
+        self.assertEqual(10000, self.__runner._report_interval) # default
+        self.assertEqual(RRClass.IN(), self.__runner._zone_class) # default
+        self.assertEqual('INFO', self.__runner._log_severity) # default
+        self.assertEqual(0, self.__runner._log_debuglevel)
+
+    def test_set_loglevel(self):
+        runner = LoadZoneRunner(['-d', '1'] + self.__args)
+        runner._parse_args()
+        self.assertEqual('DEBUG', runner._log_severity)
+        self.assertEqual(1, runner._log_debuglevel)
+
+    def test_parse_bad_args(self):
+        # There must be exactly 2 non-option arguments: zone name and zone file
+        self.assertRaises(BadArgument, LoadZoneRunner([])._parse_args)
+        self.assertRaises(BadArgument, LoadZoneRunner(['example']).
+                          _parse_args)
+        self.assertRaises(BadArgument, LoadZoneRunner(self.__args + ['0']).
+                          _parse_args)
+
+        # Bad zone name
+        args = ['example.org', 'example.zone'] # otherwise valid args
+        self.assertRaises(BadArgument,
+                          LoadZoneRunner(['bad..name', 'example.zone'] + args).
+                          _parse_args)
+
+        # Bad class name
+        self.assertRaises(BadArgument,
+                          LoadZoneRunner(['-C', 'badclass'] + args).
+                          _parse_args)
+        # Unsupported class
+        self.assertRaises(BadArgument,
+                          LoadZoneRunner(['-C', 'CH'] + args)._parse_args)
+
+        # bad debug level
+        self.assertRaises(BadArgument,
+                          LoadZoneRunner(['-d', '-10'] + args)._parse_args)
+
+        # bad report interval
+        self.assertRaises(BadArgument,
+                          LoadZoneRunner(['-i', '-5'] + args)._parse_args)
+
+        # -c cannot be omitted unless it's type sqlite3 (right now)
+        self.assertRaises(BadArgument,
+                          LoadZoneRunner(['-t', 'memory'] + args)._parse_args)
+
+    def test_get_datasrc_config(self):
+        # For sqlite3, we use the config with the well-known DB file.
+        expected_conf = \
+            '{"database_file": "' + bind10_config.DATA_PATH + '/zone.sqlite3"}'
+        self.assertEqual(expected_conf,
+                         self.__runner._get_datasrc_config('sqlite3'))
+
+        # For other types, config must be given by hand for now
+        self.assertRaises(BadArgument, self.__runner._get_datasrc_config,
+                          'memory')
+
+    def __common_load_setup(self):
+        self.__runner._zone_class = RRClass.IN()
+        self.__runner._zone_name = TEST_ZONE_NAME
+        self.__runner._zone_file = NEW_ZONE_TXT_FILE
+        self.__runner._datasrc_type = 'sqlite3'
+        self.__runner._datasrc_config = DATASRC_CONFIG
+        self.__runner._report_interval = 1
+        self.__reports = []
+        self.__runner._report_progress = lambda x: self.__reports.append(x)
+
+    def __check_zone_soa(self, soa_txt, zone_name=TEST_ZONE_NAME):
+        """Check that the given SOA RR exists and matches the expected string
+
+        If soa_txt is None, the zone is expected to be non-existent.
+        Otherwise, if soa_txt is False, the zone should exist but SOA is
+        expected to be missing.
+
+        """
+
+        client = DataSourceClient('sqlite3', DATASRC_CONFIG)
+        result, finder = client.find_zone(zone_name)
+        if soa_txt is None:
+            self.assertEqual(client.NOTFOUND, result)
+            return
+        self.assertEqual(client.SUCCESS, result)
+        result, rrset, _ = finder.find(zone_name, RRType.SOA())
+        if soa_txt:
+            self.assertEqual(finder.SUCCESS, result)
+            self.assertEqual(soa_txt, rrset.to_text())
+        else:
+            self.assertEqual(finder.NXRRSET, result)
+
+    def test_load_update(self):
+        '''successful case to loading new contents to an existing zone.'''
+        self.__common_load_setup()
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.__runner._do_load()
+        # In this test setup every loaded RR will be reported, and there will
+        # be 3 RRs
+        self.assertEqual([1, 2, 3], self.__reports)
+        self.__check_zone_soa(NEW_SOA_TXT)
+
+    def test_load_update_skipped_report(self):
+        '''successful loading, with reports for every 2 RRs'''
+        self.__common_load_setup()
+        self.__runner._report_interval = 2
+        self.__runner._do_load()
+        self.assertEqual([2], self.__reports)
+
+    def test_load_update_no_report(self):
+        '''successful loading, without progress reports'''
+        self.__common_load_setup()
+        self.__runner._report_interval = 0
+        self.__runner._do_load()
+        self.assertEqual([], self.__reports) # no report
+        self.__check_zone_soa(NEW_SOA_TXT)   # but load is completed
+
+    def test_create_and_load(self):
+        '''successful case to loading contents to a new zone (created).'''
+        self.__common_load_setup()
+        self.__runner._zone_name = Name('example.com')
+        self.__runner._zone_file = ALT_NEW_ZONE_TXT_FILE
+        self.__check_zone_soa(None, zone_name=Name('example.com'))
+        self.__runner._do_load()
+        self.__check_zone_soa(ALT_NEW_SOA_TXT, zone_name=Name('example.com'))
+
+    def test_load_fail_badconfig(self):
+        '''Load attempt fails due to broken datasrc config.'''
+        self.__common_load_setup()
+        self.__runner._datasrc_config = "invalid config"
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.assertRaises(LoadFailure, self.__runner._do_load)
+        self.__check_zone_soa(ORIG_SOA_TXT) # no change to the zone
+
+    def test_load_fail_badzone(self):
+        '''Load attempt fails due to broken zone file.'''
+        self.__common_load_setup()
+        self.__runner._zone_file = \
+            LOCAL_TESTDATA_PATH + '/broken-example.org.zone'
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.assertRaises(LoadFailure, self.__runner._do_load)
+        self.__check_zone_soa(ORIG_SOA_TXT)
+
+    def test_load_fail_noloader(self):
+        '''Load attempt fails because loading isn't supported'''
+        self.__common_load_setup()
+        self.__runner._datasrc_type = 'memory'
+        self.__runner._datasrc_config = '{"type": "memory"}'
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.assertRaises(LoadFailure, self.__runner._do_load)
+        self.__check_zone_soa(ORIG_SOA_TXT)
+
+    def test_load_fail_create_cancel(self):
+        '''Load attempt fails and new creation of zone is canceled'''
+        self.__common_load_setup()
+        self.__runner._zone_name = Name('example.com')
+        self.__runner._zone_file = 'no-such-file'
+        self.__check_zone_soa(None, zone_name=Name('example.com'))
+        self.assertRaises(LoadFailure, self.__runner._do_load)
+        # _do_load() should have once created the zone but then canceled it.
+        self.__check_zone_soa(None, zone_name=Name('example.com'))
+
+    def __common_post_load_setup(self, zone_file):
+        '''Common setup procedure for post load tests.'''
+        # replace the LoadZoneRunner's original _post_load_warning() for
+        # inspection
+        self.__warnings = []
+        self.__runner._post_load_warning = \
+            lambda msg: self.__warnings.append(msg)
+
+        # perform load and invoke checks
+        self.__common_load_setup()
+        self.__runner._zone_file = zone_file
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.__runner._do_load()
+        self.__runner._post_load_checks()
+
+    def test_load_post_check_fail_soa(self):
+        '''Load succeeds but warns about missing SOA, should cause warn'''
+        self.__common_load_setup()
+        self.__common_post_load_setup(LOCAL_TESTDATA_PATH +
+                                      '/example-nosoa.org.zone')
+        self.__check_zone_soa(False)
+        self.assertEqual(1, len(self.__warnings))
+        self.assertEqual('zone has no SOA', self.__warnings[0])
+
+    def test_load_post_check_fail_ns(self):
+        '''Load succeeds but warns about missing NS, should cause warn'''
+        self.__common_load_setup()
+        self.__common_post_load_setup(LOCAL_TESTDATA_PATH +
+                                      '/example-nons.org.zone')
+        self.__check_zone_soa(NEW_SOA_TXT)
+        self.assertEqual(1, len(self.__warnings))
+        self.assertEqual('zone has no NS', self.__warnings[0])
+
+    def __interrupt_progress(self, loaded_rrs):
+        '''A helper emulating a signal in the middle of loading.
+
+        On the second progress report, it internally invokes the signal
+        handler to see if it stops the loading.
+
+        '''
+        self.__reports.append(loaded_rrs)
+        if len(self.__reports) == 2:
+            self.__runner._interrupt_handler()
+
+    def test_load_interrupted(self):
+        '''Load attempt fails due to signal interruption'''
+        self.__common_load_setup()
+        self.__runner._report_progress = lambda x: self.__interrupt_progress(x)
+        # The interrupting _report_progress() will terminate the loading
+        # in the middle.  the number of reports is smaller, and the zone
+        # won't be changed.
+        self.assertRaises(LoadFailure, self.__runner._do_load)
+        self.assertEqual([1, 2], self.__reports)
+        self.__check_zone_soa(ORIG_SOA_TXT)
+
+    def test_load_interrupted_create_cancel(self):
+        '''Load attempt for a new zone fails due to signal interruption
+
+        It cancels the zone creation.
+
+        '''
+        self.__common_load_setup()
+        self.__runner._report_progress = lambda x: self.__interrupt_progress(x)
+        self.__runner._zone_name = Name('example.com')
+        self.__runner._zone_file = ALT_NEW_ZONE_TXT_FILE
+        self.__check_zone_soa(None, zone_name=Name('example.com'))
+        self.assertRaises(LoadFailure, self.__runner._do_load)
+        self.assertEqual([1, 2], self.__reports)
+        self.__check_zone_soa(None, zone_name=Name('example.com'))
+
+    def test_run_success(self):
+        '''Check for the top-level method.
+
+        Detailed behavior is tested in other tests.  We only check the
+        return value of run(), and the zone is successfully loaded.
+
+        '''
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.assertEqual(0, self.__runner.run())
+        self.__check_zone_soa(NEW_SOA_TXT)
+
+    def test_run_fail(self):
+        '''Check for the top-level method, failure case.
+
+        Similar to the success test, but loading will fail, and return
+        value should be 1.
+
+        '''
+        runner = LoadZoneRunner(['-c', DATASRC_CONFIG, 'example.org',
+                                 LOCAL_TESTDATA_PATH +
+                                 '/broken-example.org.zone'])
+        self.__check_zone_soa(ORIG_SOA_TXT)
+        self.assertEqual(1, runner.run())
+        self.__check_zone_soa(ORIG_SOA_TXT)
+
+if __name__== "__main__":
+    isc.log.resetUnitTestRootLogger()
+    # Disable the internal logging setup so the test output won't be too
+    # verbose by default.
+    LoadZoneRunner._config_log = lambda x: None
+
+    # Cancel signal handlers so we can stop tests when they hang
+    LoadZoneRunner._set_signal_handlers = lambda x: None
+    unittest.main()
diff --git a/src/bin/loadzone/tests/testdata/broken-example.org.zone b/src/bin/loadzone/tests/testdata/broken-example.org.zone
new file mode 100644
index 0000000..004d617
--- /dev/null
+++ b/src/bin/loadzone/tests/testdata/broken-example.org.zone
@@ -0,0 +1,11 @@
+example.org.    3600    IN  SOA (
+		ns.example.org.
+		admin.example.org.
+		1235
+		3600		;1H
+		1800		;30M
+		2419200
+		7200)
+example.org.    3600    IN  NS ns.example.org.
+ns.example.org.	3600    IN  A 192.0.2.1
+bad..name.example.org. 3600 IN AAAA 2001:db8::1
diff --git a/src/bin/loadzone/tests/testdata/example-nons.org.zone b/src/bin/loadzone/tests/testdata/example-nons.org.zone
new file mode 100644
index 0000000..f37fa28
--- /dev/null
+++ b/src/bin/loadzone/tests/testdata/example-nons.org.zone
@@ -0,0 +1,10 @@
+;; Intentionally missing SOA for testing post-load checks
+example.org.    3600    IN  SOA (
+		ns.example.org.
+		admin.example.org.
+		1235
+		3600		;1H
+		1800		;30M
+		2419200
+		7200)
+ns.example.org.	3600    IN  A 192.0.2.1
diff --git a/src/bin/loadzone/tests/testdata/example-nosoa.org.zone b/src/bin/loadzone/tests/testdata/example-nosoa.org.zone
new file mode 100644
index 0000000..3bf0d0e
--- /dev/null
+++ b/src/bin/loadzone/tests/testdata/example-nosoa.org.zone
@@ -0,0 +1,3 @@
+;; Intentionally missing SOA for testing post-load checks
+example.org.    3600    IN  NS ns.example.org.
+ns.example.org.	3600    IN  A 192.0.2.1
diff --git a/src/bin/loadzone/tests/testdata/example.org.zone b/src/bin/loadzone/tests/testdata/example.org.zone
new file mode 100644
index 0000000..fc44b36
--- /dev/null
+++ b/src/bin/loadzone/tests/testdata/example.org.zone
@@ -0,0 +1,10 @@
+example.org.    3600    IN  SOA (
+		ns.example.org.
+		admin.example.org.
+		1235
+		3600		;1H
+		1800		;30M
+		2419200
+		7200)
+example.org.    3600    IN  NS ns.example.org.
+ns.example.org.	3600    IN  A 192.0.2.1
diff --git a/src/bin/msgq/Makefile.am b/src/bin/msgq/Makefile.am
index 4244d07..5f377b1 100644
--- a/src/bin/msgq/Makefile.am
+++ b/src/bin/msgq/Makefile.am
@@ -5,10 +5,16 @@ pkglibexecdir = $(libexecdir)/@PACKAGE@
 pkglibexec_SCRIPTS = b10-msgq
 
 CLEANFILES = b10-msgq msgq.pyc
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
+CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.pyc
 
 man_MANS = b10-msgq.8
 DISTCLEANFILES = $(man_MANS)
-EXTRA_DIST = $(man_MANS) msgq.xml
+EXTRA_DIST = $(man_MANS) msgq.xml msgq_messages.mes
+
+nodist_pylogmessage_PYTHON = $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
+pylogmessagedir = $(pyexecdir)/isc/log_messages/
+BUILT_SOURCES = $(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py
 
 if GENERATE_DOCS
 
@@ -23,6 +29,11 @@ $(man_MANS):
 
 endif
 
+# Define rule to build logging source files from message file
+$(PYTHON_LOGMSGPKG_DIR)/work/msgq_messages.py : msgq_messages.mes
+	$(top_builddir)/src/lib/log/compiler/message \
+	-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/msgq_messages.mes
+
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
 b10-msgq: msgq.py
 	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" msgq.py >$@
diff --git a/src/bin/msgq/msgq.py.in b/src/bin/msgq/msgq.py.in
index bd13a1c..6937600 100755
--- a/src/bin/msgq/msgq.py.in
+++ b/src/bin/msgq/msgq.py.in
@@ -31,10 +31,16 @@ import select
 import random
 from optparse import OptionParser, OptionValueError
 import isc.util.process
+import isc.log
+from isc.log_messages.msgq_messages import *
 
 import isc.cc
 
 isc.util.process.rename()
+logger = isc.log.Logger("msgq")
+TRACE_START = logger.DBGLVL_START_SHUT
+TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
+TRACE_DETAIL = logger.DBGLVL_TRACE_DETAIL
 
 # This is the version that gets displayed to the user.
 # The VERSION string consists of the module name, the module version
@@ -51,11 +57,11 @@ class SubscriptionManager:
         """Add a subscription."""
         target = ( group, instance )
         if target in self.subscriptions:
-            print("[b10-msgq] Appending to existing target")
+            logger.debug(TRACE_BASIC, MSGQ_SUBS_APPEND_TARGET, group, instance)
             if socket not in self.subscriptions[target]:
                 self.subscriptions[target].append(socket)
         else:
-            print("[b10-msgq] Creating new target")
+            logger.debug(TRACE_BASIC, MSGQ_SUBS_NEW_TARGET, group, instance)
             self.subscriptions[target] = [ socket ]
 
     def unsubscribe(self, group, instance, socket):
@@ -162,9 +168,7 @@ class MsgQ:
 
     def setup_listener(self):
         """Set up the listener socket.  Internal function."""
-        if self.verbose:
-            sys.stdout.write("[b10-msgq] Setting up socket at %s\n" %
-                             self.socket_file)
+        logger.debug(TRACE_BASIC, MSGQ_LISTENER_SETUP, self.socket_file)
 
         self.listen_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 
@@ -179,8 +183,7 @@ class MsgQ:
             if os.path.exists(self.socket_file):
                 os.remove(self.socket_file)
             self.listen_socket.close()
-            sys.stderr.write("[b10-msgq] failed to setup listener on %s: %s\n"
-                             % (self.socket_file, str(e)))
+            logger.fatal(MSGQ_LISTENER_FAILED, self.socket_file, e)
             raise e
 
         if self.poller:
@@ -197,8 +200,7 @@ class MsgQ:
         self.setup_poller()
         self.setup_listener()
 
-        if self.verbose:
-            sys.stdout.write("[b10-msgq] Listening\n")
+        logger.debug(TRACE_START, MSGQ_LISTENER_STARTED);
 
         self.runnable = True
 
@@ -226,7 +228,7 @@ class MsgQ:
     def process_socket(self, fd):
         """Process a read on a socket."""
         if not fd in self.sockets:
-            sys.stderr.write("[b10-msgq] Got read on Strange Socket fd %d\n" % fd)
+            logger.error(MSGQ_READ_UNKNOWN_FD, fd)
             return
         sock = self.sockets[fd]
 #        sys.stderr.write("[b10-msgq] Got read on fd %d\n" %fd)
@@ -243,7 +245,7 @@ class MsgQ:
         del self.sockets[fd]
         if fd in self.sendbuffs:
             del self.sendbuffs[fd]
-        sys.stderr.write("[b10-msgq] Closing socket fd %d\n" % fd)
+        logger.debug(TRACE_BASIC, MSGQ_SOCK_CLOSE, fd)
 
     def getbytes(self, fd, sock, length):
         """Get exactly the requested bytes, or raise an exception if
@@ -285,15 +287,15 @@ class MsgQ:
         try:
             routing, data = self.read_packet(fd, sock)
         except MsgQReceiveError as err:
+            logger.error(MSGQ_RECV_ERR, fd, err)
             self.kill_socket(fd, sock)
-            sys.stderr.write("[b10-msgq] Receive error: %s\n" % err)
             return
 
         try:
             routingmsg = isc.cc.message.from_wire(routing)
         except DecodeError as err:
             self.kill_socket(fd, sock)
-            sys.stderr.write("[b10-msgq] Routing decode error: %s\n" % err)
+            logger.error(MSGQ_HDR_DECODE_ERR, fd, err)
             return
 
         self.process_command(fd, sock, routingmsg, data)
@@ -301,9 +303,7 @@ class MsgQ:
     def process_command(self, fd, sock, routing, data):
         """Process a single command.  This will split out into one of the
            other functions."""
-        # TODO: A print statement got removed here (one that prints the
-        # routing envelope). When we have logging with multiple levels,
-        # we might want to re-add that on a high debug verbosity.
+        logger.debug(TRACE_DETAIL, MSGQ_RECV_HDR, routing)
         cmd = routing["type"]
         if cmd == 'send':
             self.process_command_send(sock, routing, data)
@@ -319,7 +319,7 @@ class MsgQ:
         elif cmd == 'stop':
             self.stop()
         else:
-            sys.stderr.write("[b10-msgq] Invalid command: %s\n" % cmd)
+            logger.error(MSGQ_INVALID_CMD, cmd)
 
     def preparemsg(self, env, msg = None):
         if type(env) == dict:
@@ -363,8 +363,8 @@ class MsgQ:
             elif e.errno in [ errno.EPIPE,
                               errno.ECONNRESET,
                               errno.ENOBUFS ]:
-                print("[b10-msgq] " + errno.errorcode[e.errno] +
-                      " on send, dropping message and closing connection")
+                logger.error(MSGQ_SEND_ERR, sock.fileno(),
+                             errno.errorcode[e.errno])
                 self.kill_socket(sock.fileno(), sock)
                 return None
             else:
@@ -491,7 +491,7 @@ class MsgQ:
                 if err.args[0] == errno.EINTR:
                     events = []
                 else:
-                    sys.stderr.write("[b10-msgq] Error with poll(): %s\n" % err)
+                    logger.fatal(MSGQ_POLL_ERR, err)
                     break
             for (fd, event) in events:
                 if fd == self.listen_socket.fileno():
@@ -502,7 +502,7 @@ class MsgQ:
                     elif event & select.POLLIN:
                         self.process_socket(fd)
                     else:
-                        print("[b10-msgq] Error: Unknown even in run_poller()")
+                        logger.error(MSGQ_POLL_UNKNOWN_EVENT, fd, event)
 
     def run_kqueue(self):
         while self.running:
@@ -563,18 +563,25 @@ if __name__ == "__main__":
                       help="UNIX domain socket file the msgq daemon will use")
     (options, args) = parser.parse_args()
 
+    # Init logging, according to the parameters.
+    # FIXME: Do proper logger configuration, this is just a hack
+    # This is #2582
+    sev = 'INFO'
+    if options.verbose:
+        sev = 'DEBUG'
+    isc.log.init("b10-msgq", buffer=False, severity=sev, debuglevel=99)
+
     signal.signal(signal.SIGTERM, signal_handler)
 
     # Announce startup.
-    if options.verbose:
-        sys.stdout.write("[b10-msgq] %s\n" % VERSION)
+    logger.debug(TRACE_START, MSGQ_START, VERSION)
 
     msgq = MsgQ(options.msgq_socket_file, options.verbose)
 
     try:
         msgq.setup()
     except Exception as e:
-        sys.stderr.write("[b10-msgq] Error on startup: %s\n" % str(e))
+        logger.fatal(MSGQ_START_FAIL, e)
         sys.exit(1)
 
     try:
diff --git a/src/bin/msgq/msgq.xml b/src/bin/msgq/msgq.xml
index fed4c27..43c72a4 100644
--- a/src/bin/msgq/msgq.xml
+++ b/src/bin/msgq/msgq.xml
@@ -111,7 +111,7 @@
         <listitem><para>
           The UNIX domain socket file this daemon will use.
           The default is
-          <filename>/usr/local/var/bind10-devel/msg_socket</filename>.
+          <filename>/usr/local/var/bind10/msg_socket</filename>.
 <!-- @localstatedir@/@PACKAGE_NAME@/msg_socket -->
           </para></listitem>
       </varlistentry>
diff --git a/src/bin/msgq/msgq_messages.mes b/src/bin/msgq/msgq_messages.mes
new file mode 100644
index 0000000..21c5aa8
--- /dev/null
+++ b/src/bin/msgq/msgq_messages.mes
@@ -0,0 +1,88 @@
+# 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.
+
+% MSGQ_HDR_DECODE_ERR Error decoding header received from socket %1: %2
+The socket with mentioned file descriptor sent a packet. However, it was not
+possible to decode the routing header of the packet. The packet is ignored.
+This may be caused by a programmer error (one of the components sending invalid
+data) or possibly by incompatible version of msgq and the component (but that's
+unlikely, as the protocol is not changed often).
+
+% MSGQ_LISTENER_FAILED Failed to initialize listener on socket file '%1': %2
+The message queue daemon tried to listen on a file socket (the path is in the
+message), but it failed. The error from the operating system is logged.
+
+% MSGQ_LISTENER_SETUP Starting to listen on socket file '%1'
+Debug message. The listener is trying to open a listening socket.
+
+% MSGQ_LISTENER_STARTED Successfully started to listen
+Debug message. The message queue successfully opened a listening socket and
+waits for incoming connections.
+
+% MSGQ_POLL_ERR Error while polling for events: %1
+A low-level error happened when waiting for events, the error is logged. The
+reason for this varies, but it usually means the system is short on some
+resources.
+
+% MSGQ_POLL_UNKNOWN_EVENT Got an unknown event from the poller for fd %1: %2
+An unknown event got out from the poll() system call. This should generally not
+happen and it is either a programmer error or OS bug. The event is ignored. The
+number noted as the event is the raw encoded value, which might be useful to
+the authors when figuring the problem out.
+
+% MSGQ_READ_UNKNOWN_FD Got read on strange socket %1
+The OS reported a file descriptor is ready to read. But the daemon doesn't know
+the mentioned file descriptor, which is either a programmer error or OS bug.
+The read event is ignored.
+
+% MSGQ_RECV_ERR Error reading from socket %1: %2
+There was a low-level error when reading from a socket. The error is logged and
+the corresponding socket is dropped.
+
+% MSGQ_RECV_HDR Received header: %1
+Debug message. This message includes the whole routing header of a packet.
+
+% MSGQ_INVALID_CMD Received invalid command: %1
+An unknown command listed in the log has been received. It is ignored. This
+indicates either a programmer error (eg. a typo in the command name) or
+incompatible version of a module and message queue daemon.
+
+% MSGQ_SEND_ERR Error while sending to socket %1: %2
+There was a low-level error when sending data to a socket. The error is logged
+and the corresponding socket is dropped.
+
+% MSGQ_SOCK_CLOSE Closing socket fd %1
+Debug message. Closing the mentioned socket.
+
+% MSGQ_START Msgq version %1 starting
+Debug message. The message queue is starting up.
+
+% MSGQ_START_FAIL Error during startup: %1
+There was an error during early startup of the daemon. More concrete error is
+in the log. The daemon terminates as a result.
+
+% MSGQ_SUBS_APPEND_TARGET Appending to existing target for subscription to group '%1' for instance '%2'
+Debug message. Creating a new subscription by appending it to already existing
+data structure.
+
+% MSGQ_SUBS_NEW_TARGET Creating new target for subscription to group '%1' for instance '%2'
+Debug message. Creating a new subscription. Also creating a new data structure
+to hold it.
diff --git a/src/bin/msgq/run_msgq.sh.in b/src/bin/msgq/run_msgq.sh.in
index 3e464be..3ab4024 100644
--- a/src/bin/msgq/run_msgq.sh.in
+++ b/src/bin/msgq/run_msgq.sh.in
@@ -20,9 +20,25 @@ export PYTHON_EXEC
 
 MYPATH_PATH=@abs_top_builddir@/src/bin/msgq
 
-PYTHONPATH=@abs_top_srcdir@/src/lib/python
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/log/.libs
 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/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
+    export @ENV_LIBRARY_PATH@
+fi
+
+B10_FROM_SOURCE=@abs_top_srcdir@
+export B10_FROM_SOURCE
+# TODO: We need to do this feature based (ie. no general from_source)
+# But right now we need a second one because some spec files are
+# generated and hence end up under builddir
+B10_FROM_BUILD=@abs_top_builddir@
+export B10_FROM_BUILD
+
 BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
 export BIND10_MSGQ_SOCKET_FILE
 
diff --git a/src/bin/msgq/tests/Makefile.am b/src/bin/msgq/tests/Makefile.am
index 50b218b..c9ef5d3 100644
--- a/src/bin/msgq/tests/Makefile.am
+++ b/src/bin/msgq/tests/Makefile.am
@@ -21,6 +21,8 @@ endif
 	$(LIBRARY_PATH_PLACEHOLDER) \
 	PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/msgq \
 	BIND10_TEST_SOCKET_FILE=$(builddir)/test_msgq_socket.sock \
+	B10_FROM_SOURCE=$(abs_top_srcdir) \
+	B10_FROM_BUILD=$(abs_top_builddir) \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
 
diff --git a/src/bin/msgq/tests/msgq_test.in b/src/bin/msgq/tests/msgq_test.in
deleted file mode 100755
index e3aae89..0000000
--- a/src/bin/msgq/tests/msgq_test.in
+++ /dev/null
@@ -1,28 +0,0 @@
-#! /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
-
-MYPATH_PATH=@abs_top_srcdir@/src/bin/msgq/tests
-
-PYTHONPATH=@abs_top_srcdir@/src/bin/msgq:@abs_top_srcdir@/src/lib/python
-
-export PYTHONPATH
-
-cd ${MYPATH_PATH}
-exec ${PYTHON_EXEC} -O msgq_test.py $*
diff --git a/src/bin/msgq/tests/msgq_test.py b/src/bin/msgq/tests/msgq_test.py
index 4060190..417418f 100644
--- a/src/bin/msgq/tests/msgq_test.py
+++ b/src/bin/msgq/tests/msgq_test.py
@@ -10,6 +10,7 @@ import errno
 import threading
 import isc.cc
 import collections
+import isc.log
 
 #
 # Currently only the subscription part and some sending is implemented...
@@ -457,4 +458,6 @@ class SendNonblock(unittest.TestCase):
 
 
 if __name__ == '__main__':
+    isc.log.init("b10-msgq")
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
diff --git a/src/bin/resolver/b10-resolver.xml b/src/bin/resolver/b10-resolver.xml
index aca8fb2..485d022 100644
--- a/src/bin/resolver/b10-resolver.xml
+++ b/src/bin/resolver/b10-resolver.xml
@@ -20,7 +20,7 @@
 <refentry>
 
   <refentryinfo>
-    <date>February 28, 2012</date>
+    <date>August 16, 2012</date>
   </refentryinfo>
 
   <refmeta>
@@ -148,6 +148,8 @@ once that is merged you can for instance do 'config add Resolver/forward_address
       address or special keyword.
       The <varname>key</varname> is a TSIG key name.
       The default configuration accepts queries from 127.0.0.1 and ::1.
+      The default action is REJECT for newly added
+      <varname>query_acl</varname> items.
     </para>
 
     <para>
diff --git a/src/bin/stats/b10-stats-httpd.xml b/src/bin/stats/b10-stats-httpd.xml
index 17b7671..28d6ac9 100644
--- a/src/bin/stats/b10-stats-httpd.xml
+++ b/src/bin/stats/b10-stats-httpd.xml
@@ -103,7 +103,7 @@
   <refsect1>
     <title>FILES</title>
     <para>
-      <filename>/usr/local/share/bind10-devel/stats-httpd.spec</filename>
+      <filename>/usr/local/share/bind10/stats-httpd.spec</filename>
       <!--TODO: The filename should be computed from prefix-->
       — the spec file of <command>b10-stats-httpd</command>. This file
       contains configurable settings
@@ -115,17 +115,17 @@
       how to configure the settings.
     </para>
     <para>
-      <filename>/usr/local/share/bind10-devel/stats-httpd-xml.tpl</filename>
+      <filename>/usr/local/share/bind10/stats-httpd-xml.tpl</filename>
       <!--TODO: The filename should be computed from prefix-->
       — the template file of XML document.
     </para>
     <para>
-      <filename>/usr/local/share/bind10-devel/stats-httpd-xsd.tpl</filename>
+      <filename>/usr/local/share/bind10/stats-httpd-xsd.tpl</filename>
       <!--TODO: The filename should be computed from prefix-->
       — the template file of XSD document.
     </para>
     <para>
-      <filename>/usr/local/share/bind10-devel/stats-httpd-xsl.tpl</filename>
+      <filename>/usr/local/share/bind10/stats-httpd-xsl.tpl</filename>
       <!--TODO: The filename should be computed from prefix-->
       — the template file of XSL document.
     </para>
diff --git a/src/bin/stats/b10-stats.xml b/src/bin/stats/b10-stats.xml
index 88a01ed..ee89ad2 100644
--- a/src/bin/stats/b10-stats.xml
+++ b/src/bin/stats/b10-stats.xml
@@ -210,7 +210,7 @@
 
   <refsect1>
     <title>FILES</title>
-    <para><filename>/usr/local/share/bind10-devel/stats.spec</filename>
+    <para><filename>/usr/local/share/bind10/stats.spec</filename>
       <!--TODO: The filename should be computed from prefix-->
       — This is a spec file for <command>b10-stats</command>. It
       contains commands for <command>b10-stats</command>. They can be
diff --git a/src/bin/xfrin/xfrin_messages.mes b/src/bin/xfrin/xfrin_messages.mes
index 837cafa..770a8b2 100644
--- a/src/bin/xfrin/xfrin_messages.mes
+++ b/src/bin/xfrin/xfrin_messages.mes
@@ -24,7 +24,7 @@ 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:
+about this at the IETF dnsext working group:
 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
@@ -61,10 +61,10 @@ 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
+In an attempt of IXFR processing, the beginning 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.
+and really got 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,
@@ -149,16 +149,16 @@ daemon will now shut down.
 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
+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
+run time: Time (in seconds) the complete axfr took.
 
-bytes/second: Transfer speed
+bytes/second: Transfer speed.
 
 % XFRIN_TSIG_KEY_NOT_FOUND TSIG key not found in key ring: %1
 An attempt to start a transfer with TSIG was made, but the configured TSIG
diff --git a/src/bin/xfrout/xfrout_messages.mes b/src/bin/xfrout/xfrout_messages.mes
index 97b7a31..d48aa24 100644
--- a/src/bin/xfrout/xfrout_messages.mes
+++ b/src/bin/xfrout/xfrout_messages.mes
@@ -70,7 +70,7 @@ AXFR-style IXFR.
 
 % XFROUT_IXFR_NO_ZONE IXFR client %1, %2: zone not found with journal
 The requested zone in IXFR was not found in the data source
-even though the xfrout daemon sucessfully found the SOA RR of the zone
+even though the xfrout daemon successfully found the SOA RR of the zone
 in the data source.  This can happen if the administrator removed the
 zone from the data source within the small duration between these
 operations, but it's more likely to be a bug or broken data source.
@@ -84,9 +84,6 @@ NOTAUTH.
 An IXFR request was received, but the client's SOA version is the same as
 or newer than that of the server.  The xfrout server responds to the
 request with the answer section being just one SOA of that version.
-Note: as of this wrting the 'newer version' cannot be identified due to
-the lack of support for the serial number arithmetic.  This will soon
-be implemented.
 
 % XFROUT_MODULECC_SESSION_ERROR error encountered by configuration/command module: %1
 There was a problem in the lower level module handling configuration and
@@ -206,7 +203,7 @@ xfrout daemon process is still running. This xfrout daemon (the one
 printing this message) will not start.
 
 % XFROUT_XFR_TRANSFER_CHECK_ERROR %1 client %2: check for transfer of %3 failed: %4
-Pre-response check for an incomding XFR request failed unexpectedly.
+Pre-response check for an incoming XFR request failed unexpectedly.
 The most likely cause of this is that some low level error in the data
 source, but it may also be other general (more unlikely) errors such
 as memory shortage.  Some detail of the error is also included in the
diff --git a/src/lib/asiodns/asiodns_messages.mes b/src/lib/asiodns/asiodns_messages.mes
index 8fbafdd..db2902e 100644
--- a/src/lib/asiodns/asiodns_messages.mes
+++ b/src/lib/asiodns/asiodns_messages.mes
@@ -53,6 +53,22 @@ The asynchronous I/O code encountered an error when trying to send data to
 the specified address on the given protocol.  The number of the system
 error that caused the problem is given in the message.
 
+% ASIODNS_UDP_ASYNC_SEND_FAIL Error sending UDP packet to %1: %2
+The low-level ASIO library reported an error when trying to send a UDP
+packet in asynchronous UDP mode. This can be any error reported by
+send_to(), and can indicate problems such as too high a load on the network,
+or a problem in the underlying library or system.
+This packet is dropped and will not be sent, but service should resume
+normally.
+If you see a single occurrence of this message, it probably does not
+indicate any significant problem, but if it is logged often, it is probably
+a good idea to inspect your network traffic.
+
+% ASIODNS_UDP_SYNC_SEND_FAIL Error sending UDP packet to %1: %2
+The low-level ASIO library reported an error when trying to send a UDP
+packet in synchronous UDP mode. See ASIODNS_UDP_ASYNC_SEND_FAIL for
+more information.
+
 % ASIODNS_UNKNOWN_ORIGIN unknown origin for ASIO error code %1 (protocol: %2, address %3)
 An internal consistency check on the origin of a message from the
 asynchronous I/O module failed. This may indicate an internal error;
diff --git a/src/lib/asiodns/sync_udp_server.cc b/src/lib/asiodns/sync_udp_server.cc
index 0c577f2..95e1c72 100644
--- a/src/lib/asiodns/sync_udp_server.cc
+++ b/src/lib/asiodns/sync_udp_server.cc
@@ -148,9 +148,15 @@ SyncUDPServer::handleRead(const asio::error_code& ec, const size_t length) {
             return;
         }
 
+        asio::error_code ec;
         socket_->send_to(asio::buffer(output_buffer_->getData(),
                                       output_buffer_->getLength()),
-                         sender_);
+                         sender_, 0, ec);
+        if (ec) {
+            LOG_ERROR(logger, ASIODNS_UDP_SYNC_SEND_FAIL).
+                      arg(sender_.address().to_string()).
+                      arg(ec.message());
+        }
     }
 
     // And schedule handling another socket.
diff --git a/src/lib/asiodns/udp_server.cc b/src/lib/asiodns/udp_server.cc
index 0f5456b..bdf79a7 100644
--- a/src/lib/asiodns/udp_server.cc
+++ b/src/lib/asiodns/udp_server.cc
@@ -299,10 +299,16 @@ UDPServer::operator()(asio::error_code ec, size_t length) {
         // Begin an asynchronous send, and then yield.  When the
         // send completes, we will resume immediately after this point
         // (though we have nothing further to do, so the coroutine
-        // will simply exit at that time).
+        // will simply exit at that time, after reporting an error if
+        // there was one).
         CORO_YIELD data_->socket_->async_send_to(
             buffer(data_->respbuf_->getData(), data_->respbuf_->getLength()),
             *data_->sender_, *this);
+        if (ec) {
+            LOG_ERROR(logger, ASIODNS_UDP_ASYNC_SEND_FAIL).
+                      arg(data_->sender_->address().to_string()).
+                      arg(ec.message());
+        }
     }
 }
 
diff --git a/src/lib/cc/data.cc b/src/lib/cc/data.cc
index 8d9b87d..4a37087 100644
--- a/src/lib/cc/data.cc
+++ b/src/lib/cc/data.cc
@@ -308,7 +308,7 @@ std::string
 str_from_stringstream(std::istream &in, const std::string& file, const int line,
                       int& pos) throw (JSONError)
 {
-    char c = 0;
+    char c;
     std::stringstream ss;
     c = in.get();
     ++pos;
@@ -390,7 +390,7 @@ number_from_stringstream(std::istream &in, int& pos) {
 // value is larger than an int can handle)
 ElementPtr
 from_stringstream_number(std::istream &in, int &pos) {
-    long int i = 0;
+    long int i;
     double d = 0.0;
     bool is_double = false;
     char *endptr;
diff --git a/src/lib/datasrc/database.cc b/src/lib/datasrc/database.cc
index b65bc08..f377ffe 100644
--- a/src/lib/datasrc/database.cc
+++ b/src/lib/datasrc/database.cc
@@ -920,7 +920,7 @@ DatabaseClient::Finder::findOnNameResult(const Name& name,
             } else {
                 LOG_DEBUG(logger, DBG_TRACE_DETAILED,
                           DATASRC_DATABASE_FOUND_RRSET).
-                    arg(accessor_->getDBName()).arg(wti->second);
+                    arg(accessor_->getDBName()).arg(*wti->second);
             }
         }
         // Found an RR matching the query, so return it.  (Note that this
@@ -1745,7 +1745,7 @@ public:
                 arg(zone_).arg(rrclass_).arg(accessor_->getDBName());
             return (rrset);
         } catch (const Exception& ex) {
-            LOG_ERROR(logger, DATASRC_DATABASE_JOURNALREADR_BADDATA).
+            LOG_ERROR(logger, DATASRC_DATABASE_JOURNALREADER_BADDATA).
                 arg(zone_).arg(rrclass_).arg(accessor_->getDBName()).
                 arg(begin_).arg(end_).arg(ex.what());
             isc_throw(DataSourceError, "Failed to create RRset from diff on "
diff --git a/src/lib/datasrc/datasrc_messages.mes b/src/lib/datasrc/datasrc_messages.mes
index e9b4c90..6983459 100644
--- a/src/lib/datasrc/datasrc_messages.mes
+++ b/src/lib/datasrc/datasrc_messages.mes
@@ -144,7 +144,7 @@ instead.
 % DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL empty non-terminal %2 in %1
 The domain name does not have any RRs associated with it, so it doesn't
 exist in the database.  However, it has a subdomain, so it does exist
-in the DNS address space. This type of domain is known an an "empty
+in the DNS address space. This type of domain is known as an "empty
 non-terminal" and so we return NXRRSET instead of NXDOMAIN.
 
 % DATASRC_DATABASE_FOUND_NXDOMAIN search in datasource %1 resulted in NXDOMAIN for %2/%3/%4
@@ -202,7 +202,7 @@ a zone's difference sequences from a database-based data source.  The
 zone's name and class, database name, and the start and end serials
 are shown in the message.
 
-% DATASRC_DATABASE_JOURNALREADR_BADDATA failed to convert a diff to RRset in %1/%2 on %3 between %4 and %5: %6
+% DATASRC_DATABASE_JOURNALREADER_BADDATA failed to convert a diff to RRset in %1/%2 on %3 between %4 and %5: %6
 This is an error message indicating that a zone's diff is broken and
 the data source library failed to convert it to a valid RRset.  The
 most likely cause of this is that someone has manually modified the
@@ -287,7 +287,7 @@ matching the name.  This is returned as the result of the search.
 The given wildcard matches the name being sough but it as an empty
 nonterminal (e.g. there's nothing at *.example.com but something like
 subdomain.*.example.org, do exist: so *.example.org exists in the
-namespace but has no RRs assopciated with it). This will produce NXRRSET.
+namespace but has no RRs associated with it). This will produce NXRRSET.
 
 % DATASRC_DATABASE_WILDCARD_MATCH search in datasource %1 resulted in wildcard match at %2 with RRset %3
 The database doesn't contain directly matching name.  When searching
@@ -490,7 +490,7 @@ destroyed.
 % DATASRC_MEM_WILDCARD_CANCEL wildcard match canceled for '%1'
 Debug information. A domain above wildcard was reached, but there's something
 below the requested domain. Therefore the wildcard doesn't apply here.  This
-behaviour is specified by RFC 1034, section 4.3.3
+behaviour is specified by RFC 1034, section 4.3.3.
 
 % DATASRC_MEM_WILDCARD_DNAME DNAME record in wildcard domain '%1'
 The software refuses to load DNAME records into a wildcard domain.  It isn't
diff --git a/src/lib/datasrc/memory/zone_data_loader.cc b/src/lib/datasrc/memory/zone_data_loader.cc
index 051acc3..e224224 100644
--- a/src/lib/datasrc/memory/zone_data_loader.cc
+++ b/src/lib/datasrc/memory/zone_data_loader.cc
@@ -12,15 +12,17 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <datasrc/master_loader_callbacks.h>
 #include <datasrc/memory/zone_data_loader.h>
 #include <datasrc/memory/zone_data_updater.h>
 #include <datasrc/memory/logger.h>
 #include <datasrc/memory/segment_object_holder.h>
 #include <datasrc/memory/util_internal.h>
 
+#include <dns/master_loader.h>
+#include <dns/rrcollator.h>
 #include <dns/rdataclass.h>
 #include <dns/rrset.h>
-#include <dns/masterload.h>
 
 #include <boost/foreach.hpp>
 #include <boost/bind.hpp>
@@ -181,18 +183,25 @@ loadZoneDataInternal(util::MemorySegment& mem_sgmt,
     return (holder.release());
 }
 
-// A wrapper for dns::masterLoad used by loadZoneData() below.  Essentially it
-// converts the two callback types.  Note the mostly redundant wrapper of
+// A wrapper for dns::MasterLoader used by loadZoneData() 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
+// function<void(RRsetPtr)> (MasterLoader 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)
+masterLoaderWrapper(const char* const filename, const Name& origin,
+                    const RRClass& zone_class, LoadCallback callback)
 {
+    bool load_ok = false;       // (we don't use it)
+    dns::RRCollator collator(boost::bind(callback, _1));
+
     try {
-        masterLoad(filename, origin, zone_class, boost::bind(callback, _1));
-    } catch (MasterLoadError& e) {
+        dns::MasterLoader(filename, origin, zone_class,
+                          createMasterLoaderCallbacks(origin, zone_class,
+                                                      &load_ok),
+                          collator.getCallback()).load();
+        collator.flush();
+    } catch (const dns::MasterLoaderError& e) {
         isc_throw(ZoneLoaderException, e.what());
     }
 }
@@ -215,7 +224,7 @@ loadZoneData(util::MemorySegment& mem_sgmt,
              const std::string& zone_file)
 {
      return (loadZoneDataInternal(mem_sgmt, rrclass, zone_name,
-                                 boost::bind(masterLoadWrapper,
+                                 boost::bind(masterLoaderWrapper,
                                              zone_file.c_str(),
                                              zone_name, rrclass,
                                              _1)));
diff --git a/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc b/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
index 58c6cb1..4f82f7b 100644
--- a/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
+++ b/src/lib/datasrc/tests/memory/rdata_serialization_unittest.cc
@@ -409,6 +409,7 @@ public:
         reader.nextSig();
         reader.rewind();
         // Do the actual rendering
+        // cppcheck-suppress unreadVariable
         current = &renderer;
         reader.iterate();
         renderer.writeName(dummyName2());
@@ -484,6 +485,8 @@ public:
         current = NULL;
         reader.iterateAllSigs();
         // Now return the renderer and render the rest of the data
+        // cppcheck-suppress redundantAssignment
+        // cppcheck-suppress unreadVariable
         current = &renderer;
         reader.iterate();
         // Now, this should not break anything and should be valid, but should
diff --git a/src/lib/datasrc/tests/zone_finder_context_unittest.cc b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
index 6844712..85b167e 100644
--- a/src/lib/datasrc/tests/zone_finder_context_unittest.cc
+++ b/src/lib/datasrc/tests/zone_finder_context_unittest.cc
@@ -81,7 +81,7 @@ addRRset(ZoneUpdaterPtr updater, ConstRRsetPtr rrset) {
 }
 
 DataSourceClientPtr
-createSQLite3Client(RRClass zclass, const Name& zname) {
+createSQLite3Client(RRClass zclass, const Name& zname, stringstream& ss) {
     // 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.
@@ -93,7 +93,6 @@ createSQLite3Client(RRClass zclass, const Name& zname) {
     // 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();
@@ -101,6 +100,12 @@ createSQLite3Client(RRClass zclass, const Name& zname) {
     return (client);
 }
 
+DataSourceClientPtr
+createSQLite3ClientWithNS(RRClass zclass, const Name& zname) {
+    stringstream ss("ns.example.com. 3600 IN A 192.0.2.7");
+    return (createSQLite3Client(zclass, zname, ss));
+}
+
 // The test class.  Its parameterized so we can share the test scnearios
 // for any concrete data source implementaitons.
 class ZoneFinderContextTest :
@@ -134,7 +139,7 @@ protected:
 // We test the in-memory and SQLite3 data source implementations.
 INSTANTIATE_TEST_CASE_P(, ZoneFinderContextTest,
                         ::testing::Values(createInMemoryClient,
-                                          createSQLite3Client));
+                                          createSQLite3ClientWithNS));
 
 TEST_P(ZoneFinderContextTest, getAdditionalAuthNS) {
     ZoneFinderContextPtr ctx = finder_->find(qzone_, RRType::NS());
@@ -430,4 +435,25 @@ TEST_P(ZoneFinderContextTest, getAdditionalWithRRSIGOnly) {
                 result_sets_.begin(), result_sets_.end());
 }
 
+TEST(ZoneFinderContextSQLite3Test, escapedText) {
+    // This test checks that TXTLike data, when written to a database,
+    // is escaped correctly before stored in the database. The actual
+    // escaping is done in the toText() method of TXTLike objects, but
+    // we check anyway if this also carries over to the ZoneUpdater.
+    RRClass zclass(RRClass::IN());
+    Name zname("example.org");
+    stringstream ss("escaped.example.org. 3600 IN TXT Hello~World\\;\\\"");
+    DataSourceClientPtr client = createSQLite3Client(zclass, zname, ss);
+    ZoneFinderPtr finder = client->findZone(zname).zone_finder;
+
+    // If there is no escaping, the following will throw an exception
+    // when it tries to construct a TXT RRset using the data from the
+    // database.
+    ZoneFinderContextPtr ctx = finder->find(Name("escaped.example.org"),
+                                            RRType::TXT());
+    EXPECT_EQ(ZoneFinder::SUCCESS, ctx->code);
+    EXPECT_EQ("escaped.example.org. 3600 IN TXT \"Hello~World\\;\\\"\"\n",
+              ctx->rrset->toText());
+}
+
 }
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index f41ce53..571f11d 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -28,8 +28,8 @@ libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
 libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
 libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
 libb10_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h
-libb10_dhcp___la_SOURCES += option6_int.h
-libb10_dhcp___la_SOURCES += option6_int_array.h
+libb10_dhcp___la_SOURCES += option_int.h
+libb10_dhcp___la_SOURCES += option_int_array.h
 libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
 libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index adf1731..a4f4659 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -212,8 +212,8 @@ bool IfaceMgr::openSockets4(const uint16_t port) {
              addr != addrs.end();
              ++addr) {
 
-            // Skip IPv6 addresses
-            if (addr->getFamily() != AF_INET) {
+            // Skip all but V4 addresses.
+            if (!addr->isV4()) {
                 continue;
             }
 
@@ -247,8 +247,8 @@ bool IfaceMgr::openSockets6(const uint16_t port) {
              addr != addrs.end();
              ++addr) {
 
-            // skip IPv4 addresses
-            if (addr->getFamily() != AF_INET6) {
+            // Skip all but V6 addresses.
+            if (!addr->isV6()) {
                 continue;
             }
 
@@ -356,12 +356,13 @@ int IfaceMgr::openSocket(const std::string& ifname, const IOAddress& addr,
     if (!iface) {
         isc_throw(BadValue, "There is no " << ifname << " interface present.");
     }
-    switch (addr.getFamily()) {
-    case AF_INET:
+    if (addr.isV4()) {
         return openSocket4(*iface, addr, port);
-    case AF_INET6:
+
+    } else if (addr.isV6()) {
         return openSocket6(*iface, addr, port);
-    default:
+
+    } else {
         isc_throw(BadValue, "Failed to detect family of address: "
                   << addr.toText());
     }
@@ -469,7 +470,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
     asio::error_code err_code;
     // If remote address is broadcast address we have to
     // allow this on the socket.
-    if (remote_addr.getAddress().is_v4() &&
+    if (remote_addr.isV4() &&
         (remote_addr == IOAddress(DHCP_IPV4_BROADCAST_ADDRESS))) {
         // Socket has to be open prior to setting the broadcast
         // option. Otherwise set_option will complain about
@@ -556,9 +557,7 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
         addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
     }
 
-    memcpy(&addr6.sin6_addr,
-           addr.getAddress().to_v6().to_bytes().data(),
-           sizeof(addr6.sin6_addr));
+    memcpy(&addr6.sin6_addr, &addr.toBytes()[0], sizeof(addr6.sin6_addr));
 #ifdef HAVE_SA_LEN
     addr6.sin6_len = sizeof(addr6);
 #endif
@@ -660,7 +659,7 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
     to.sin6_family = AF_INET6;
     to.sin6_port = htons(pkt->getRemotePort());
     memcpy(&to.sin6_addr,
-           pkt->getRemoteAddr().getAddress().to_v6().to_bytes().data(),
+           &pkt->getRemoteAddr().toBytes()[0],
            16);
     to.sin6_scope_id = pkt->getIndex();
 
@@ -798,7 +797,7 @@ IfaceMgr::receive4(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */) {
              s != socket_collection.end(); ++s) {
 
             // Only deal with IPv4 addresses.
-            if (s->addr_.getFamily() == AF_INET) {
+            if (s->addr_.isV4()) {
                 names << s->sockfd_ << "(" << iface->getName() << ") ";
 
                 // Add this socket to listening set
@@ -950,8 +949,8 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout_sec, uint32_t timeout_usec /* = 0 */
         for (SocketCollection::const_iterator s = socket_collection.begin();
              s != socket_collection.end(); ++s) {
 
-            // Only deal with IPv4 addresses.
-            if (s->addr_.getFamily() == AF_INET6) {
+            // Only deal with IPv6 addresses.
+            if (s->addr_.isV6()) {
                 names << s->sockfd_ << "(" << iface->getName() << ") ";
 
                 // Add this socket to listening set
diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc
index 2773d09..b19ed21 100644
--- a/src/lib/dhcp/libdhcp++.cc
+++ b/src/lib/dhcp/libdhcp++.cc
@@ -20,8 +20,8 @@
 #include <dhcp/option.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option_definition.h>
+#include <dhcp/option_int_array.h>
 #include <dhcp/std_option_defs.h>
 #include <exceptions/exceptions.h>
 #include <util/buffer.h>
@@ -49,10 +49,12 @@ const OptionDefContainer&
 LibDHCP::getOptionDefs(const Option::Universe u) {
     switch (u) {
     case Option::V4:
-        initStdOptionDefs4();
+        if (v4option_defs_.empty()) {
+            initStdOptionDefs4();
+        }
         return (v4option_defs_);
     case Option::V6:
-        if (v6option_defs_.size() == 0) {
+        if (v6option_defs_.empty()) {
             initStdOptionDefs6();
         }
         return (v6option_defs_);
@@ -100,31 +102,35 @@ LibDHCP::optionFactory(Option::Universe u,
 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];
+    size_t length = buf.size();
+
+    // Get the list of stdandard option definitions.
+    const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V6);
+    // Get the search index #1. It allows to search for option definitions
+    // using option code.
+    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+
+    // The buffer being read comprises a set of options, each starting with
+    // a two-byte type code and a two-byte length field.
+    while (offset + 4 <= length) {
+        uint16_t opt_type = isc::util::readUint16(&buf[offset]);
         offset += 2;
-        uint16_t opt_len = buf[offset] * 256 + buf[offset + 1];
+        uint16_t opt_len = isc::util::readUint16(&buf[offset]);
         offset += 2;
 
-        if (offset + opt_len > end) {
+        if (offset + opt_len > length) {
             // @todo: consider throwing exception here.
             return (offset);
         }
 
-        // Get the list of stdandard option definitions.
-        OptionDefContainer option_defs = LibDHCP::getOptionDefs(Option::V6);
-        // Get the search index #1. It allows to search for option definitions
-        // using option code.
-        const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
-        // Get all options with the particular option code. Note that option code
-        // is non-unique within this container however at this point we expect
-        // to get one option definition with the particular code. If more are
-        // returned we report an error.
+        // Get all definitions with the particular option code. Note that option
+        // code is non-unique within this container however at this point we
+        // expect to get one option definition with the particular code. If more
+        // are returned we report an error.
         const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
         // Get the number of returned option definitions for the option code.
         size_t num_defs = distance(range.first, range.second);
+
         OptionPtr opt;
         if (num_defs > 1) {
             // Multiple options of the same code are not supported right now!
@@ -162,7 +168,14 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
                                  isc::dhcp::Option::OptionCollection& options) {
     size_t offset = 0;
 
-    // 2 byte - header of DHCPv4 option
+    // Get the list of stdandard option definitions.
+    const OptionDefContainer& option_defs = LibDHCP::getOptionDefs(Option::V4);
+    // Get the search index #1. It allows to search for option definitions
+    // using option code.
+    const OptionDefContainerTypeIndex& idx = option_defs.get<1>();
+
+    // The buffer being read comprises a set of options, each starting with
+    // a one-byte type code and a one-byte length field.
     while (offset + 1 <= buf.size()) {
         uint8_t opt_type = buf[offset++];
 
@@ -176,8 +189,10 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
             continue;
 
         if (offset + 1 >= buf.size()) {
+            // opt_type must be cast to integer so as it is not treated as
+            // unsigned char value (a number is presented in error message).
             isc_throw(OutOfRange, "Attempt to parse truncated option "
-                      << opt_type);
+                      << static_cast<int>(opt_type));
         }
 
         uint8_t opt_len =  buf[offset++];
@@ -187,12 +202,35 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
                       << "-byte long buffer.");
         }
 
+        // Get all definitions with the particular option code. Note that option code
+        // is non-unique within this container however at this point we expect
+        // to get one option definition with the particular code. If more are
+        // returned we report an error.
+        const OptionDefContainerTypeRange& range = idx.equal_range(opt_type);
+        // Get the number of returned option definitions for the option code.
+        size_t num_defs = distance(range.first, range.second);
+
         OptionPtr opt;
-        switch(opt_type) {
-        default:
+        if (num_defs > 1) {
+            // Multiple options of the same code are not supported right now!
+            isc_throw(isc::Unexpected, "Internal error: multiple option definitions"
+                      " for option type " << static_cast<int>(opt_type)
+                      << " returned. Currently it is not supported to initialize"
+                      << " multiple option definitions for the same option code."
+                      << " This will be supported once support for option spaces"
+                      << " is implemented");
+        } else if (num_defs == 0) {
             opt = OptionPtr(new Option(Option::V4, opt_type,
-                                       buf.begin()+offset,
-                                       buf.begin()+offset+opt_len));
+                                       buf.begin() + offset,
+                                       buf.begin() + offset + opt_len));
+        } else {
+            // The option definition has been found. Use it to create
+            // the option instance from the provided buffer chunk.
+            const OptionDefinitionPtr& def = *(range.first);
+            assert(def);
+            opt = def->optionFactory(Option::V4, opt_type,
+                                     buf.begin() + offset,
+                                     buf.begin() + offset + opt_len);
         }
 
         options.insert(std::make_pair(opt_type, opt));
@@ -259,7 +297,32 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
 
 void
 LibDHCP::initStdOptionDefs4() {
-    isc_throw(isc::NotImplemented, "initStdOptionDefs4 is not implemented");
+    v4option_defs_.clear();
+
+    // Now let's add all option definitions.
+    for (int i = 0; i < OPTION_DEF_PARAMS_SIZE4; ++i) {
+        OptionDefinitionPtr definition(new OptionDefinition(OPTION_DEF_PARAMS4[i].name,
+                                                            OPTION_DEF_PARAMS4[i].code,
+                                                            OPTION_DEF_PARAMS4[i].type,
+                                                            OPTION_DEF_PARAMS4[i].array));
+
+        for (int rec = 0; rec < OPTION_DEF_PARAMS4[i].records_size; ++rec) {
+            definition->addRecordField(OPTION_DEF_PARAMS4[i].records[rec]);
+        }
+
+        // Sanity check if the option is valid.
+        try {
+            definition->validate();
+        } catch (const Exception& ex) {
+            // This is unlikely event that validation fails and may
+            // be only caused by programming error. To guarantee the
+            // data consistency we clear all option definitions that
+            // have been added so far and pass the exception forward.
+            v4option_defs_.clear();
+            throw;
+        }
+        v4option_defs_.push_back(definition);
+    }
 }
 
 void
diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h
index e855070..c325aa5 100644
--- a/src/lib/dhcp/libdhcp++.h
+++ b/src/lib/dhcp/libdhcp++.h
@@ -138,9 +138,9 @@ private:
     /// The method creates option definitions for all DHCPv4 options.
     /// Currently this function is not implemented.
     ///
-    /// @todo implemend this function.
-    ///
-    /// @throw isc::NotImplemeneted
+    /// @throw std::bad alloc if system went out of memory.
+    /// @throw MalformedOptionDefinition if any of the definitions
+    /// are incorrect. This is programming error.
     static void initStdOptionDefs4();
 
     /// Initialize standard DHCPv6 option definitions.
@@ -149,7 +149,7 @@ private:
     ///
     /// @throw std::bad_alloc if system went out of memory.
     /// @throw MalformedOptionDefinition if any of the definitions
-    /// is incorect. This is a programming error.
+    /// is incorrect. This is a programming error.
     static void initStdOptionDefs6();
 
     /// pointers to factories that produce DHCPv6 options
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 2a53f0f..dbdac0c 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -100,22 +100,14 @@ void Option::pack(isc::util::OutputBuffer& buf) {
 void
 Option::pack4(isc::util::OutputBuffer& buf) {
     if (universe_ == V4) {
-        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
-            /// of DHCPv4 options. Clients MUST concatenate them.
-            /// Fortunately, there are no such large options used today.
-        }
-
-        buf.writeUint8(type_);
-        buf.writeUint8(len() - getHeaderLen());
+        // Write a header.
+        packHeader(buf);
+        // Write data.
         if (!data_.empty()) {
             buf.writeData(&data_[0], data_.size());
         }
-
+        // Write sub-options.
         packOptions(buf);
-
     } else {
         isc_throw(BadValue, "Invalid universe type " << universe_);
     }
@@ -125,12 +117,13 @@ Option::pack4(isc::util::OutputBuffer& buf) {
 
 void Option::pack6(isc::util::OutputBuffer& buf) {
     if (universe_ == V6) {
-        buf.writeUint16(type_);
-        buf.writeUint16(len() - getHeaderLen());
+        // Write a header.
+        packHeader(buf);
+        // Write data.
         if (!data_.empty()) {
             buf.writeData(&data_[0], data_.size());
         }
-
+        // Write sub-options.
         packOptions(buf);
     } else {
         isc_throw(BadValue, "Invalid universe type " << universe_);
@@ -139,6 +132,26 @@ void Option::pack6(isc::util::OutputBuffer& buf) {
 }
 
 void
+Option::packHeader(isc::util::OutputBuffer& buf) {
+    if (universe_ == V4) {
+        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
+            /// of DHCPv4 options. Clients MUST concatenate them.
+            /// Fortunately, there are no such large options used today.
+        }
+
+        buf.writeUint8(type_);
+        buf.writeUint8(len() - getHeaderLen());
+
+    } else {
+        buf.writeUint16(type_);
+        buf.writeUint16(len() - getHeaderLen());
+    }
+}
+
+void
 Option::packOptions(isc::util::OutputBuffer& buf) {
     switch (universe_) {
     case V4:
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index 3c7799f..e4105cc 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -325,6 +325,22 @@ protected:
     /// @throw BadValue Universe is not V6.
     virtual void pack6(isc::util::OutputBuffer& buf);
 
+    /// @brief Store option's header in a buffer.
+    ///
+    /// This method writes option's header into a buffer in the
+    /// on-wire format. The universe set for the particular option
+    /// is used to determine whether option code and length are
+    /// stored as 2-byte (for DHCPv6) or single-byte (for DHCPv4)
+    /// values. For DHCPv4 options, this method checks if the
+    /// length does not exceed 255 bytes and throws exception if
+    /// it does.
+    /// This method is used by derived classes to pack option's
+    /// header into a buffer. This method should not be called
+    /// directly by other classes.
+    ///
+    /// @param [out] buf output buffer.
+    void packHeader(isc::util::OutputBuffer& buf);
+
     /// @brief Store sub options in a buffer.
     ///
     /// This method stores all sub-options defined for a particular
diff --git a/src/lib/dhcp/option4_addrlst.cc b/src/lib/dhcp/option4_addrlst.cc
index 76341b2..86da9f6 100644
--- a/src/lib/dhcp/option4_addrlst.cc
+++ b/src/lib/dhcp/option4_addrlst.cc
@@ -86,7 +86,7 @@ Option4AddrLst::pack4(isc::util::OutputBuffer& buf) {
 }
 
 void Option4AddrLst::setAddress(const isc::asiolink::IOAddress& addr) {
-    if (addr.getFamily() != AF_INET) {
+    if (!addr.isV4()) {
         isc_throw(BadValue, "Can't store non-IPv4 address in "
                   << "Option4AddrLst option");
     }
@@ -107,7 +107,7 @@ void Option4AddrLst::setAddresses(const AddressContainer& addrs) {
 
 
 void Option4AddrLst::addAddress(const isc::asiolink::IOAddress& addr) {
-    if (addr.getFamily() != AF_INET) {
+    if (!addr.isV4()) {
         isc_throw(BadValue, "Can't store non-IPv4 address in "
                   << "Option4AddrLst option");
     }
diff --git a/src/lib/dhcp/option6_addrlst.cc b/src/lib/dhcp/option6_addrlst.cc
index c53fe78..cb14070 100644
--- a/src/lib/dhcp/option6_addrlst.cc
+++ b/src/lib/dhcp/option6_addrlst.cc
@@ -49,7 +49,7 @@ Option6AddrLst::Option6AddrLst(uint16_t type, OptionBufferConstIter begin,
 
 void
 Option6AddrLst::setAddress(const isc::asiolink::IOAddress& addr) {
-    if (addr.getFamily() != AF_INET6) {
+    if (!addr.isV6()) {
         isc_throw(BadValue, "Can't store non-IPv6 address in Option6AddrLst option");
     }
 
@@ -72,7 +72,13 @@ void Option6AddrLst::pack(isc::util::OutputBuffer& buf) {
 
     for (AddressContainer::const_iterator addr=addrs_.begin();
          addr!=addrs_.end(); ++addr) {
-        buf.writeData(addr->getAddress().to_v6().to_bytes().data(), V6ADDRESS_LEN);
+        if (!addr->isV6()) {
+            isc_throw(isc::BadValue, addr->toText()
+                      << " is not an IPv6 address");
+        }
+        // If an address is IPv6 address it should have assumed
+        // length of V6ADDRESS_LEN.
+        buf.writeData(&addr->toBytes()[0], V6ADDRESS_LEN);
     }
 }
 
@@ -104,8 +110,7 @@ std::string Option6AddrLst::toText(int indent /* =0 */) {
 }
 
 uint16_t Option6AddrLst::len() {
-
-    return (OPTION6_HDR_LEN + addrs_.size()*V6ADDRESS_LEN);
+    return (OPTION6_HDR_LEN + addrs_.size() * V6ADDRESS_LEN);
 }
 
 } // end of namespace isc::dhcp
diff --git a/src/lib/dhcp/option6_iaaddr.cc b/src/lib/dhcp/option6_iaaddr.cc
index 8db5047..d0ba087 100644
--- a/src/lib/dhcp/option6_iaaddr.cc
+++ b/src/lib/dhcp/option6_iaaddr.cc
@@ -51,9 +51,11 @@ void Option6IAAddr::pack(isc::util::OutputBuffer& buf) {
     // length without 4-byte option header
     buf.writeUint16(len() - getHeaderLen());
 
-
-    buf.writeData(addr_.getAddress().to_v6().to_bytes().data(),
-                  isc::asiolink::V6ADDRESS_LEN);
+    if (!addr_.isV6()) {
+        isc_throw(isc::BadValue, addr_.toText()
+                  << " is not an IPv6 address");
+    }
+    buf.writeData(&addr_.toBytes()[0], isc::asiolink::V6ADDRESS_LEN);
 
     buf.writeUint32(preferred_);
     buf.writeUint32(valid_);
diff --git a/src/lib/dhcp/option6_int.h b/src/lib/dhcp/option6_int.h
deleted file mode 100644
index d61509b..0000000
--- a/src/lib/dhcp/option6_int.h
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef OPTION6_INT_H
-#define OPTION6_INT_H
-
-#include <dhcp/libdhcp++.h>
-#include <dhcp/option.h>
-#include <dhcp/option_data_types.h>
-#include <util/io_utilities.h>
-
-#include <stdint.h>
-
-namespace isc {
-namespace dhcp {
-
-/// This template class represents DHCPv6 option with single value.
-/// This value is of integer type and can be any of the following:
-/// - uint8_t,
-/// - uint16_t,
-/// - uint32_t,
-/// - int8_t,
-/// - int16_t,
-/// - int32_t.
-///
-/// @param T data field type (see above).
-template<typename T>
-class Option6Int: public Option {
-
-public:
-    /// @brief Constructor.
-    ///
-    /// @param type option type.
-    /// @param value option value.
-    ///
-    /// @throw isc::dhcp::InvalidDataType if data field type provided
-    /// as template parameter is not a supported integer type.
-    Option6Int(uint16_t type, T value)
-        : Option(Option::V6, type), value_(value) {
-        if (!OptionDataTypeTraits<T>::integer_type) {
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-    }
-
-    /// @brief Constructor.
-    ///
-    /// This constructor creates option from a buffer. This construtor
-    /// may throw exception if \ref unpack function throws during buffer
-    /// parsing.
-    ///
-    /// @param type option type.
-    /// @param begin iterator to first byte of option data.
-    /// @param end iterator to end of option data (first byte after option end).
-    ///
-    /// @throw isc::OutOfRange if provided buffer is shorter than data size.
-    /// @throw isc::dhcp::InvalidDataType if data field type provided
-    /// as template parameter is not a supported integer type.
-    Option6Int(uint16_t type, OptionBufferConstIter begin,
-               OptionBufferConstIter end)
-        : Option(Option::V6, type) {
-        if (!OptionDataTypeTraits<T>::integer_type) {
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-        unpack(begin, end);
-    }
-
-    /// Writes option in wire-format to buf, returns pointer to first unused
-    /// byte after stored option.
-    ///
-    /// @param [out] buf buffer (option will be stored here)
-    ///
-    /// @throw isc::dhcp::InvalidDataType if size of a data field type is not
-    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
-    /// because it is checked in a constructor.
-    void pack(isc::util::OutputBuffer& buf) {
-        buf.writeUint16(type_);
-        buf.writeUint16(len() - OPTION6_HDR_LEN);
-        // Depending on the data type length we use different utility functions
-        // writeUint16 or writeUint32 which write the data in the network byte
-        // order to the provided buffer. The same functions can be safely used
-        // for either unsigned or signed integers so there is not need to create
-        // special cases for intX_t types.
-        switch (OptionDataTypeTraits<T>::len) {
-        case 1:
-            buf.writeUint8(value_);
-            break;
-        case 2:
-            buf.writeUint16(value_);
-            break;
-        case 4:
-            buf.writeUint32(value_);
-            break;
-        default:
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-        packOptions(buf);
-    }
-
-    /// @brief Parses received buffer
-    ///
-    /// Parses received buffer and returns offset to the first unused byte after
-    /// parsed option.
-    ///
-    /// @param begin iterator to first byte of option data
-    /// @param end iterator to end of option data (first byte after option end)
-    ///
-    /// @throw isc::OutOfRange if provided buffer is shorter than data size.
-    /// @throw isc::dhcp::InvalidDataType if size of a data field type is not
-    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
-    /// because it is checked in a constructor.
-    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
-        if (distance(begin, end) < sizeof(T)) {
-            isc_throw(OutOfRange, "Option " << getType() << " truncated");
-        }
-        // @todo consider what to do if buffer is longer than data type.
-
-        // Depending on the data type length we use different utility functions
-        // readUint16 or readUint32 which read the data laid in the network byte
-        // order from the provided buffer. The same functions can be safely used
-        // for either unsigned or signed integers so there is not need to create
-        // special cases for intX_t types.
-        int data_size_len = OptionDataTypeTraits<T>::len;
-        switch (data_size_len) {
-        case 1:
-            value_ = *begin;
-            break;
-        case 2:
-            value_ = isc::util::readUint16(&(*begin));
-            break;
-        case 4:
-            value_ = isc::util::readUint32(&(*begin));
-            break;
-        default:
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-        // Use local variable to set a new value for this iterator.
-        // When using OptionDataTypeTraits<T>::len directly some versions
-        // of clang complain about unresolved reference to
-        // OptionDataTypeTraits structure during linking.
-        begin += data_size_len;
-        unpackOptions(OptionBuffer(begin, end));
-    }
-
-    /// @brief Set option value.
-    ///
-    /// @param value new option value.
-    void setValue(T value) { value_ = value; }
-
-    /// @brief Return option value.
-    ///
-    /// @return option value.
-    T getValue() const { return value_; }
-
-    /// @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() {
-        uint16_t length = OPTION6_HDR_LEN + sizeof(T);
-        // length of all suboptions
-        for (Option::OptionCollection::iterator it = options_.begin();
-             it != options_.end();
-             ++it) {
-            length += (*it).second->len();
-        }
-        return (length);
-    }
-
-private:
-
-    T value_;  ///< Value conveyed by the option.
-};
-
-} // isc::dhcp namespace
-} // isc namespace
-
-#endif // OPTION6_INT_H
diff --git a/src/lib/dhcp/option6_int_array.h b/src/lib/dhcp/option6_int_array.h
deleted file mode 100644
index 3b64213..0000000
--- a/src/lib/dhcp/option6_int_array.h
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef OPTION6_INT_ARRAY_H
-#define OPTION6_INT_ARRAY_H
-
-#include <dhcp/libdhcp++.h>
-#include <dhcp/option.h>
-#include <dhcp/option_data_types.h>
-#include <util/io_utilities.h>
-
-#include <stdint.h>
-
-namespace isc {
-namespace dhcp {
-
-/// This template class represents DHCPv6 option with array of
-/// integer values. The type of the elements in the array can be
-/// any of the following:
-/// - uint8_t,
-/// - uint16_t,
-/// - uint32_t,
-/// - int8_t,
-/// - int16_t,
-/// - int32_t.
-///
-/// @warning Since this option may convey variable number of integer
-/// values, sub-options are should not be added in this option as
-/// there is no way to distinguish them from other data. The API will
-/// allow addition of sub-options but they will be ignored during
-/// packing and unpacking option data.
-///
-/// @param T data field type (see above).
-template<typename T>
-class Option6IntArray: public Option {
-
-public:
-
-    /// @brief Constructor.
-    ///
-    /// Creates option with empty values vector.
-    ///
-    /// @param type option type.
-    ///
-    /// @throw isc::dhcp::InvalidDataType if data field type provided
-    /// as template parameter is not a supported integer type.
-    Option6IntArray(uint16_t type)
-        : Option(Option::V6, type),
-          values_(0) {
-        if (!OptionDataTypeTraits<T>::integer_type) {
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-    }
-
-    /// @brief Constructor.
-    ///
-    /// @param type option type.
-    /// @param buf buffer with option data (must not be empty).
-    ///
-    /// @throw isc::OutOfRange if provided buffer is empty or its length
-    /// is not multiple of size of the data type in bytes.
-    /// @throw isc::dhcp::InvalidDataType if data field type provided
-    /// as template parameter is not a supported integer type.
-    Option6IntArray(uint16_t type, const OptionBuffer& buf)
-        : Option(Option::V6, type) {
-        if (!OptionDataTypeTraits<T>::integer_type) {
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-        unpack(buf.begin(), buf.end());
-    }
-
-    /// @brief Constructor.
-    ///
-    /// This constructor creates option from a buffer. This construtor
-    /// may throw exception if \ref unpack function throws during buffer
-    /// parsing.
-    ///
-    /// @param type option type.
-    /// @param begin iterator to first byte of option data.
-    /// @param end iterator to end of option data (first byte after option end).
-    ///
-    /// @throw isc::OutOfRange if provided buffer is empty or its length
-    /// is not multiple of size of the data type in bytes.
-    /// @throw isc::dhcp::InvalidDataType if data field type provided
-    /// as template parameter is not a supported integer type.
-    Option6IntArray(uint16_t type, OptionBufferConstIter begin,
-                    OptionBufferConstIter end)
-        : Option(Option::V6, type) {
-        if (!OptionDataTypeTraits<T>::integer_type) {
-            isc_throw(dhcp::InvalidDataType, "non-integer type");
-        }
-        unpack(begin, end);
-    }
-
-    /// Writes option in wire-format to buf, returns pointer to first unused
-    /// byte after stored option.
-    ///
-    /// @param [out] buf buffer (option will be stored here)
-    ///
-    /// @throw isc::dhcp::InvalidDataType if size of a data fields type is not
-    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
-    /// because it is checked in a constructor.
-    void pack(isc::util::OutputBuffer& buf) {
-        buf.writeUint16(type_);
-        buf.writeUint16(len() - OPTION6_HDR_LEN);
-        for (int i = 0; i < values_.size(); ++i) {
-            // Depending on the data type length we use different utility functions
-            // writeUint16 or writeUint32 which write the data in the network byte
-            // order to the provided buffer. The same functions can be safely used
-            // for either unsigned or signed integers so there is not need to create
-            // special cases for intX_t types.
-            switch (OptionDataTypeTraits<T>::len) {
-            case 1:
-                buf.writeUint8(values_[i]);
-                break;
-            case 2:
-                buf.writeUint16(values_[i]);
-                break;
-            case 4:
-                buf.writeUint32(values_[i]);
-                break;
-            default:
-                isc_throw(dhcp::InvalidDataType, "non-integer type");
-            }
-        }
-        // We don't pack sub-options here because we have array-type option.
-        // We don't allow sub-options in array-type options as there is no
-        // way to distinguish them from the data fields on option reception.
-    }
-
-    /// @brief Parses received buffer
-    ///
-    /// Parses received buffer and returns offset to the first unused byte after
-    /// parsed option.
-    ///
-    /// @param begin iterator to first byte of option data
-    /// @param end iterator to end of option data (first byte after option end)
-    ///
-    /// @throw isc::dhcp::InvalidDataType if size of a data fields type is not
-    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
-    /// because it is checked in a constructor.
-    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
-        if (distance(begin, end) == 0) {
-            isc_throw(OutOfRange, "option " << getType() << " empty");
-        }
-        if (distance(begin, end) % sizeof(T) != 0) {
-            isc_throw(OutOfRange, "option " << getType() << " truncated");
-        }
-        // @todo consider what to do if buffer is longer than data type.
-
-        values_.clear();
-        while (begin != end) {
-            // Depending on the data type length we use different utility functions
-            // readUint16 or readUint32 which read the data laid in the network byte
-            // order from the provided buffer. The same functions can be safely used
-            // for either unsigned or signed integers so there is not need to create
-            // special cases for intX_t types.
-            int data_size_len = OptionDataTypeTraits<T>::len;
-            switch (data_size_len) {
-            case 1:
-                values_.push_back(*begin);
-                break;
-            case 2:
-                values_.push_back(isc::util::readUint16(&(*begin)));
-                break;
-            case 4:
-                values_.push_back(isc::util::readUint32(&(*begin)));
-                break;
-            default:
-                isc_throw(dhcp::InvalidDataType, "non-integer type");
-            }
-            // Use local variable to set a new value for this iterator.
-            // When using OptionDataTypeTraits<T>::len directly some versions
-            // of clang complain about unresolved reference to
-            // OptionDataTypeTraits structure during linking.
-            begin += data_size_len;
-        }
-        // We do not unpack sub-options here because we have array-type option.
-        // Such option have variable number of data fields, thus there is no
-        // way to assess where sub-options start.
-    }
-
-    /// @brief Return collection of option values.
-    ///
-    /// @return collection of values.
-    const std::vector<T>& getValues() const { return (values_); }
-
-    /// @brief Set option values.
-    ///
-    /// @param values collection of values to be set for option.
-    void setValues(const std::vector<T>& values) { values_ = values; }
-
-    /// @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() {
-        uint16_t length = OPTION6_HDR_LEN + values_.size() * sizeof(T);
-        // length of all suboptions
-        for (Option::OptionCollection::iterator it = options_.begin();
-             it != options_.end();
-             ++it) {
-            length += (*it).second->len();
-        }
-        return (length);
-    }
-
-private:
-
-    std::vector<T> values_;
-};
-
-} // isc::dhcp namespace
-} // isc namespace
-
-#endif // OPTION6_INT_ARRAY_H
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
index 0c3cb81..068e360 100644
--- a/src/lib/dhcp/option_custom.cc
+++ b/src/lib/dhcp/option_custom.cc
@@ -58,14 +58,12 @@ void
 OptionCustom::addArrayDataField(const asiolink::IOAddress& address) {
     checkArrayType();
 
-    if ((address.getFamily() == AF_INET &&
-         definition_.getType() != OPT_IPV4_ADDRESS_TYPE) ||
-        (address.getFamily() == AF_INET6 &&
-         definition_.getType() != OPT_IPV6_ADDRESS_TYPE)) {
+    if ((address.isV4() && definition_.getType() != OPT_IPV4_ADDRESS_TYPE) ||
+        (address.isV6() && definition_.getType() != OPT_IPV6_ADDRESS_TYPE)) {
         isc_throw(BadDataTypeCast, "invalid address specified "
                   << address.toText() << ". Expected a valid IPv"
-                  << (definition_.getType() == OPT_IPV4_ADDRESS_TYPE ? "4" : "6")
-                  << " address.");
+                  << (definition_.getType() == OPT_IPV4_ADDRESS_TYPE ?
+                      "4" : "6") << " address.");
     }
 
     OptionBuffer buf;
@@ -454,10 +452,8 @@ OptionCustom::writeAddress(const asiolink::IOAddress& address,
 
     checkIndex(index);
 
-    if ((address.getFamily() == AF_INET &&
-         buffers_[index].size() != V4ADDRESS_LEN) ||
-        (address.getFamily() == AF_INET6 &&
-         buffers_[index].size() != V6ADDRESS_LEN)) {
+    if ((address.isV4() && buffers_[index].size() != V4ADDRESS_LEN) ||
+        (address.isV6() && buffers_[index].size() != V6ADDRESS_LEN)) {
         isc_throw(BadDataTypeCast, "invalid address specified "
                   << address.toText() << ". Expected a valid IPv"
                   << (buffers_[index].size() == V4ADDRESS_LEN ? "4" : "6")
diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc
index 0c512d7..a567b7e 100644
--- a/src/lib/dhcp/option_data_types.cc
+++ b/src/lib/dhcp/option_data_types.cc
@@ -146,27 +146,8 @@ OptionDataTypeUtil::readAddress(const std::vector<uint8_t>& buf,
 void
 OptionDataTypeUtil::writeAddress(const asiolink::IOAddress& address,
                                  std::vector<uint8_t>& buf) {
-    // @todo There is a ticket 2396 submitted, which adds the
-    // functionality to return a buffer representation of
-    // IOAddress. If so, this function can be simplified.
-    if (address.getAddress().is_v4()) {
-        asio::ip::address_v4::bytes_type addr_bytes =
-            address.getAddress().to_v4().to_bytes();
-        // Increase the buffer size by the size of IPv4 address.
-        buf.resize(buf.size() + addr_bytes.size());
-        std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
-                           buf.end());
-    } else if (address.getAddress().is_v6()) {
-        asio::ip::address_v6::bytes_type addr_bytes =
-            address.getAddress().to_v6().to_bytes();
-        // Incresase the buffer size by the size of IPv6 address.
-        buf.resize(buf.size() + addr_bytes.size());
-        std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
-                           buf.end());
-    } else {
-        isc_throw(BadDataTypeCast, "the address " << address.toText()
-                  << " is neither valid IPv4 not IPv6 address.");
-    }
+    const std::vector<uint8_t>& vec = address.toBytes();
+    buf.insert(buf.end(), vec.begin(), vec.end());
 }
 
 void
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
index ff5789d..e53fa6e 100644
--- a/src/lib/dhcp/option_data_types.h
+++ b/src/lib/dhcp/option_data_types.h
@@ -70,7 +70,7 @@ enum OptionDataType {
 /// @brief Trait class for data types supported in DHCP option definitions.
 ///
 /// This is useful to check whether the type specified as template parameter
-/// is supported by classes like Option6Int, Option6IntArray and some template
+/// is supported by classes like OptionInt, OptionIntArray and some template
 /// factory functions in OptionDefinition class.
 template<typename T>
 struct OptionDataTypeTraits {
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index 2248bd7..e1d7cb6 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -17,10 +17,10 @@
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_definition.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_int_array.h>
 #include <util/encode/hex.h>
 
 using namespace std;
@@ -97,19 +97,19 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                     factoryInteger<int8_t>(u, type, begin, end));
 
         case OPT_UINT16_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint16_t>(type, begin, end) :
+            return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryInteger<uint16_t>(u, type, begin, end));
 
         case OPT_INT16_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint16_t>(type, begin, end) :
+            return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryInteger<int16_t>(u, type, begin, end));
 
         case OPT_UINT32_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint32_t>(type, begin, end) :
+            return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryInteger<uint32_t>(u, type, begin, end));
 
         case OPT_INT32_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint32_t>(type, begin, end) :
+            return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryInteger<int32_t>(u, type, begin, end));
 
         case OPT_IPV4_ADDRESS_TYPE:
@@ -379,12 +379,9 @@ OptionDefinition::writeToBuffer(const std::string& value,
     case OPT_IPV6_ADDRESS_TYPE:
         {
             asiolink::IOAddress address(value);
-            if (address.getFamily() != AF_INET &&
-                address.getFamily() != AF_INET6) {
+            if (!address.isV4() && !address.isV6()) {
                 isc_throw(BadDataTypeCast, "provided address " << address.toText()
-                          << " is not a valid "
-                          << (address.getAddress().is_v4() ? "IPv4" : "IPv6")
-                          << " address");
+                          << " is not a valid IPv4 or IPv6 address.");
             }
             OptionDataTypeUtil::writeAddress(address, buf);
             return;
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index ca40428..9f6bef2 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -48,25 +48,25 @@ class OptionDefinition;
 /// @brief Pointer to option definition object.
 typedef boost::shared_ptr<OptionDefinition> OptionDefinitionPtr;
 
-/// @brief Forward declaration to Option6Int.
+/// @brief Forward declaration to OptionInt.
 ///
-/// This forward declaration is needed to access Option6Int class
-/// without having to include option6_int.h header. This is because
-/// this header includes libdhcp++.h and this causes circular
-/// inclusion between libdhcp++.h, option_definition.h and
+/// This forward declaration is needed to access the OptionInt class without
+/// having to include the option_int.h header file. It is required because
+/// this header includes libdhcp++.h, and including option_int.h would cause
+/// circular inclusion between libdhcp++.h, option_definition.h and
 /// option6_int.h.
 template<typename T>
-class Option6Int;
+class OptionInt;
 
-/// @brief Forward declaration to Option6IntArray.
+/// @brief Forward declaration to OptionIntArray.
 ///
-/// This forward declaration is needed to access Option6IntArray class
-/// without having to include option6_int_array.h header. This is because
-/// this header includes libdhcp++.h and this causes circular
-/// inclusion between libdhcp++.h, option_definition.h and
-/// option6_int_array.h.
+/// This forward declaration is needed to access the OptionIntArray class
+/// without having to include the option_int_array.h header file. It is
+/// required because this header includes libdhcp++.h, and including
+/// option_int_array.h would cause circular inclusion between libdhcp++.h,
+/// option_definition.h and option_int_array.h.
 template<typename T>
-class Option6IntArray;
+class OptionIntArray;
 
 /// @brief Base class representing a DHCP option definition.
 ///
@@ -344,6 +344,7 @@ public:
 
     /// @brief Factory function to create option with integer value.
     ///
+    /// @param u universe (V4 or V6).
     /// @param type option type.
     /// @param begin iterator pointing to the beginning of the buffer.
     /// @param end iterator pointing to the end of the buffer.
@@ -351,15 +352,16 @@ public:
     ///
     /// @throw isc::OutOfRange if provided option buffer length is invalid.
     template<typename T>
-    static OptionPtr factoryInteger(Option::Universe, uint16_t type,
+    static OptionPtr factoryInteger(Option::Universe u, uint16_t type,
                                     OptionBufferConstIter begin,
                                     OptionBufferConstIter end) {
-        OptionPtr option(new Option6Int<T>(type, begin, end));
+        OptionPtr option(new OptionInt<T>(u, type, begin, end));
         return (option);
     }
 
     /// @brief Factory function to create option with array of integer values.
     ///
+    /// @param universe (V4 or V6).
     /// @param type option type.
     /// @param begin iterator pointing to the beginning of the buffer.
     /// @param end iterator pointing to the end of the buffer.
@@ -367,10 +369,11 @@ public:
     ///
     /// @throw isc::OutOfRange if provided option buffer length is invalid.
     template<typename T>
-    static OptionPtr factoryIntegerArray(uint16_t type,
+    static OptionPtr factoryIntegerArray(Option::Universe u,
+                                         uint16_t type,
                                          OptionBufferConstIter begin,
                                          OptionBufferConstIter end) {
-        OptionPtr option(new Option6IntArray<T>(type, begin, end));
+        OptionPtr option(new OptionIntArray<T>(u, type, begin, end));
         return (option);
     }
 
diff --git a/src/lib/dhcp/option_int.h b/src/lib/dhcp/option_int.h
new file mode 100644
index 0000000..ebb1641
--- /dev/null
+++ b/src/lib/dhcp/option_int.h
@@ -0,0 +1,194 @@
+// 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 OPTION_INT_H
+#define OPTION_INT_H
+
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/option_data_types.h>
+#include <util/io_utilities.h>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// This template class represents DHCP option with single value.
+/// This value is of integer type and can be any of the following:
+/// - uint8_t,
+/// - uint16_t,
+/// - uint32_t,
+/// - int8_t,
+/// - int16_t,
+/// - int32_t.
+///
+/// @param T data field type (see above).
+template<typename T>
+class OptionInt: public Option {
+
+public:
+    /// @brief Constructor.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param type option type.
+    /// @param value option value.
+    ///
+    /// @throw isc::dhcp::InvalidDataType if data field type provided
+    /// as template parameter is not a supported integer type.
+    OptionInt(Option::Universe u, uint16_t type, T value)
+        : Option(u, type), value_(value) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+    }
+
+    /// @brief Constructor.
+    ///
+    /// This constructor creates option from a buffer. This construtor
+    /// may throw exception if \ref unpack function throws during buffer
+    /// parsing.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @param type option type.
+    /// @param begin iterator to first byte of option data.
+    /// @param end iterator to end of option data (first byte after option end).
+    ///
+    /// @throw isc::OutOfRange if provided buffer is shorter than data size.
+    /// @throw isc::dhcp::InvalidDataType if data field type provided
+    /// as template parameter is not a supported integer type.
+    OptionInt(Option::Universe u, uint16_t type, OptionBufferConstIter begin,
+               OptionBufferConstIter end)
+        : Option(u, type) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+        unpack(begin, end);
+    }
+
+    /// Writes option in wire-format to buf, returns pointer to first unused
+    /// byte after stored option.
+    ///
+    /// @param [out] buf buffer (option will be stored here)
+    ///
+    /// @throw isc::dhcp::InvalidDataType if size of a data field type is not
+    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
+    /// because it is checked in a constructor.
+    void pack(isc::util::OutputBuffer& buf) {
+        // Pack option header.
+        packHeader(buf);
+        // Depending on the data type length we use different utility functions
+        // writeUint16 or writeUint32 which write the data in the network byte
+        // order to the provided buffer. The same functions can be safely used
+        // for either unsigned or signed integers so there is not need to create
+        // special cases for intX_t types.
+        switch (OptionDataTypeTraits<T>::len) {
+        case 1:
+            buf.writeUint8(value_);
+            break;
+        case 2:
+            buf.writeUint16(value_);
+            break;
+        case 4:
+            buf.writeUint32(value_);
+            break;
+        default:
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+        packOptions(buf);
+    }
+
+    /// @brief Parses received buffer
+    ///
+    /// Parses received buffer and returns offset to the first unused byte after
+    /// parsed option.
+    ///
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    ///
+    /// @throw isc::OutOfRange if provided buffer is shorter than data size.
+    /// @throw isc::dhcp::InvalidDataType if size of a data field type is not
+    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
+    /// because it is checked in a constructor.
+    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
+        if (distance(begin, end) < sizeof(T)) {
+            isc_throw(OutOfRange, "Option " << getType() << " truncated");
+        }
+        // @todo consider what to do if buffer is longer than data type.
+
+        // Depending on the data type length we use different utility functions
+        // readUint16 or readUint32 which read the data laid in the network byte
+        // order from the provided buffer. The same functions can be safely used
+        // for either unsigned or signed integers so there is not need to create
+        // special cases for intX_t types.
+        int data_size_len = OptionDataTypeTraits<T>::len;
+        switch (data_size_len) {
+        case 1:
+            value_ = *begin;
+            break;
+        case 2:
+            value_ = isc::util::readUint16(&(*begin));
+            break;
+        case 4:
+            value_ = isc::util::readUint32(&(*begin));
+            break;
+        default:
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+        // Use local variable to set a new value for this iterator.
+        // When using OptionDataTypeTraits<T>::len directly some versions
+        // of clang complain about unresolved reference to
+        // OptionDataTypeTraits structure during linking.
+        begin += data_size_len;
+        unpackOptions(OptionBuffer(begin, end));
+    }
+
+    /// @brief Set option value.
+    ///
+    /// @param value new option value.
+    void setValue(T value) { value_ = value; }
+
+    /// @brief Return option value.
+    ///
+    /// @return option value.
+    T getValue() const { return value_; }
+
+    /// @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() {
+        // Calculate the length of the header.
+        uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
+        // The data length is equal to size of T.
+        length += sizeof(T);;
+        // length of all suboptions
+        for (Option::OptionCollection::iterator it = options_.begin();
+             it != options_.end();
+             ++it) {
+            length += (*it).second->len();
+        }
+        return (length);
+    }
+
+private:
+
+    T value_;  ///< Value conveyed by the option.
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif // OPTION_INT_H
diff --git a/src/lib/dhcp/option_int_array.h b/src/lib/dhcp/option_int_array.h
new file mode 100644
index 0000000..5004152
--- /dev/null
+++ b/src/lib/dhcp/option_int_array.h
@@ -0,0 +1,234 @@
+// 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 OPTION_INT_ARRAY_H
+#define OPTION_INT_ARRAY_H
+
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option.h>
+#include <dhcp/option_data_types.h>
+#include <util/io_utilities.h>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// This template class represents DHCP (v4 or v6) option with an
+/// array of integer values. The type of the elements in the array
+/// can be any of the following:
+/// - uint8_t,
+/// - uint16_t,
+/// - uint32_t,
+/// - int8_t,
+/// - int16_t,
+/// - int32_t.
+///
+/// @warning Since this option may convey variable number of integer
+/// values, sub-options are should not be added in this option as
+/// there is no way to distinguish them from other data. The API will
+/// allow addition of sub-options but they will be ignored during
+/// packing and unpacking option data.
+///
+/// @param T data field type (see above).
+template<typename T>
+class OptionIntArray: public Option {
+
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Creates option with empty values vector.
+    ///
+    /// @param u universe (V4 or V6).
+    /// @param type option type.
+    ///
+    /// @throw isc::dhcp::InvalidDataType if data field type provided
+    /// as template parameter is not a supported integer type.
+    OptionIntArray(const Option::Universe u, const uint16_t type)
+        : Option(u, type),
+          values_(0) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+    }
+
+    /// @brief Constructor.
+    ///
+    /// @param u universe (V4 or V6).
+    /// @param type option type.
+    /// @param buf buffer with option data (must not be empty).
+    ///
+    /// @throw isc::OutOfRange if provided buffer is empty or its length
+    /// is not multiple of size of the data type in bytes.
+    /// @throw isc::dhcp::InvalidDataType if data field type provided
+    /// as template parameter is not a supported integer type.
+    OptionIntArray(const Option::Universe u, const uint16_t type,
+                   const OptionBuffer& buf)
+        : Option(u, type) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+        unpack(buf.begin(), buf.end());
+    }
+
+    /// @brief Constructor.
+    ///
+    /// This constructor creates option from a buffer. This construtor
+    /// may throw exception if \ref unpack function throws during buffer
+    /// parsing.
+    ///
+    /// @param u universe (V4 or V6).
+    /// @param type option type.
+    /// @param begin iterator to first byte of option data.
+    /// @param end iterator to end of option data (first byte after option end).
+    ///
+    /// @throw isc::OutOfRange if provided buffer is empty or its length
+    /// is not multiple of size of the data type in bytes.
+    /// @throw isc::dhcp::InvalidDataType if data field type provided
+    /// as template parameter is not a supported integer type.
+    OptionIntArray(const Option::Universe u, const uint16_t type,
+                   OptionBufferConstIter begin, OptionBufferConstIter end)
+        : Option(u, type) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(dhcp::InvalidDataType, "non-integer type");
+        }
+        unpack(begin, end);
+    }
+
+    /// Writes option in wire-format to buf, returns pointer to first unused
+    /// byte after stored option.
+    ///
+    /// @param [out] buf buffer (option will be stored here)
+    ///
+    /// @throw isc::dhcp::InvalidDataType if size of a data fields type is not
+    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
+    /// because it is checked in a constructor.
+    void pack(isc::util::OutputBuffer& buf) {
+        // Pack option header.
+        packHeader(buf);
+        // Pack option data.
+        for (int i = 0; i < values_.size(); ++i) {
+            // Depending on the data type length we use different utility functions
+            // writeUint16 or writeUint32 which write the data in the network byte
+            // order to the provided buffer. The same functions can be safely used
+            // for either unsigned or signed integers so there is not need to create
+            // special cases for intX_t types.
+            switch (OptionDataTypeTraits<T>::len) {
+            case 1:
+                buf.writeUint8(values_[i]);
+                break;
+            case 2:
+                buf.writeUint16(values_[i]);
+                break;
+            case 4:
+                buf.writeUint32(values_[i]);
+                break;
+            default:
+                isc_throw(dhcp::InvalidDataType, "non-integer type");
+            }
+        }
+        // We don't pack sub-options here because we have array-type option.
+        // We don't allow sub-options in array-type options as there is no
+        // way to distinguish them from the data fields on option reception.
+    }
+
+    /// @brief Parses received buffer
+    ///
+    /// Parses received buffer and returns offset to the first unused byte after
+    /// parsed option.
+    ///
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    ///
+    /// @throw isc::dhcp::InvalidDataType if size of a data fields type is not
+    /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
+    /// because it is checked in a constructor.
+    virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
+        if (distance(begin, end) == 0) {
+            isc_throw(OutOfRange, "option " << getType() << " empty");
+        }
+        if (distance(begin, end) % sizeof(T) != 0) {
+            isc_throw(OutOfRange, "option " << getType() << " truncated");
+        }
+        // @todo consider what to do if buffer is longer than data type.
+
+        values_.clear();
+        while (begin != end) {
+            // Depending on the data type length we use different utility functions
+            // readUint16 or readUint32 which read the data laid in the network byte
+            // order from the provided buffer. The same functions can be safely used
+            // for either unsigned or signed integers so there is not need to create
+            // special cases for intX_t types.
+            int data_size_len = OptionDataTypeTraits<T>::len;
+            switch (data_size_len) {
+            case 1:
+                values_.push_back(*begin);
+                break;
+            case 2:
+                values_.push_back(isc::util::readUint16(&(*begin)));
+                break;
+            case 4:
+                values_.push_back(isc::util::readUint32(&(*begin)));
+                break;
+            default:
+                isc_throw(dhcp::InvalidDataType, "non-integer type");
+            }
+            // Use local variable to set a new value for this iterator.
+            // When using OptionDataTypeTraits<T>::len directly some versions
+            // of clang complain about unresolved reference to
+            // OptionDataTypeTraits structure during linking.
+            begin += data_size_len;
+        }
+        // We do not unpack sub-options here because we have array-type option.
+        // Such option have variable number of data fields, thus there is no
+        // way to assess where sub-options start.
+    }
+
+    /// @brief Return collection of option values.
+    ///
+    /// @return collection of values.
+    const std::vector<T>& getValues() const { return (values_); }
+
+    /// @brief Set option values.
+    ///
+    /// @param values collection of values to be set for option.
+    void setValues(const std::vector<T>& values) { values_ = values; }
+
+    /// @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() {
+        uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
+        length += values_.size() * sizeof(T);
+        // length of all suboptions
+        for (Option::OptionCollection::iterator it = options_.begin();
+             it != options_.end();
+             ++it) {
+            length += (*it).second->len();
+        }
+        return (length);
+    }
+
+private:
+
+    std::vector<T> values_;
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
+#endif // OPTION_INT_ARRAY_H
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index 15c885c..15c8f4e 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -15,6 +15,7 @@
 #include <asiolink/io_address.h>
 #include <dhcp/dhcp4.h>
 #include <dhcp/libdhcp++.h>
+#include <dhcp/option_int.h>
 #include <dhcp/pkt4.h>
 #include <exceptions/exceptions.h>
 
@@ -188,9 +189,10 @@ Pkt4::unpack() {
 }
 
 void Pkt4::check() {
-    boost::shared_ptr<Option> typeOpt = getOption(DHO_DHCP_MESSAGE_TYPE);
+    boost::shared_ptr<OptionInt<uint8_t> > typeOpt =
+        boost::dynamic_pointer_cast<OptionInt<uint8_t> >(getOption(DHO_DHCP_MESSAGE_TYPE));
     if (typeOpt) {
-        uint8_t msg_type = typeOpt->getUint8();
+        uint8_t msg_type = typeOpt->getValue();
         if (msg_type > DHCPLEASEACTIVE) {
             isc_throw(BadValue, "Invalid DHCP message type received: "
                       << msg_type);
diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h
index d66dcf8..144df8b 100644
--- a/src/lib/dhcp/std_option_defs.h
+++ b/src/lib/dhcp/std_option_defs.h
@@ -25,7 +25,7 @@ namespace {
 /// @param name name of the array being declared.
 /// @param types data types of fields that belong to the record.
 #ifndef RECORD_DECL
-#define RECORD_DECL(name, types...) static OptionDataType name[] = { types }
+#define RECORD_DECL(name, types...) const OptionDataType name[] = { types }
 #endif
 
 /// @brief A pair of values: one pointing to the array holding types of
@@ -44,41 +44,186 @@ using namespace isc::dhcp;
 
 /// @brief Parameters being used to make up an option definition.
 struct OptionDefParams {
-    const char* name;         // option name
-    uint16_t code;            // option code
-    OptionDataType type;      // data type
-    bool array;               // is array
-    OptionDataType* records;  // record fields
-    size_t records_size;      // number of fields in a record
+    const char* name;              // option name
+    uint16_t code;                 // option code
+    OptionDataType type;           // data type
+    bool array;                    // is array
+    const OptionDataType* records; // record fields
+    size_t records_size;           // number of fields in a record
 };
 
+// fqdn option record fields.
+//
+// Note that the flags field indicates the type of domain
+// name encoding. There is a choice between deprecated
+// ASCII encoding and compressed encoding described in
+// RFC 1035, section 3.1. The latter could be handled
+// by OPT_FQDN_TYPE but we can't use it here because
+// clients may request ASCII encoding.
+RECORD_DECL(FQDN_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_STRING_TYPE);
+
+/// @brief Definitions of standard DHCPv4 options.
+const OptionDefParams OPTION_DEF_PARAMS4[] = {
+    { "subnet-mask", DHO_SUBNET_MASK, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "time-offset", DHO_TIME_OFFSET, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "routers", DHO_ROUTERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "time-servers", DHO_TIME_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "name-servers", DHO_NAME_SERVERS, OPT_IPV4_ADDRESS_TYPE,
+      false, NO_RECORD_DEF },
+    { "domain-name-servers", DHO_DOMAIN_NAME_SERVERS,
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "log-servers", DHO_LOG_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "cookie-servers", DHO_COOKIE_SERVERS, OPT_IPV4_ADDRESS_TYPE,
+      true, NO_RECORD_DEF },
+    { "lpr-servers", DHO_LPR_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "impress-servers", DHO_IMPRESS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "resource-location-servers", DHO_RESOURCE_LOCATION_SERVERS,
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "host-name", DHO_HOST_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "boot-size", DHO_BOOT_SIZE, OPT_UINT16_TYPE, false, NO_RECORD_DEF },
+    { "merit-dump", DHO_MERIT_DUMP, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE, false, NO_RECORD_DEF },
+    { "swap-server", DHO_SWAP_SERVER, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "root-path", DHO_ROOT_PATH, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "extensions-path", DHO_EXTENSIONS_PATH, OPT_STRING_TYPE,
+      false, NO_RECORD_DEF },
+    { "ip-forwarding", DHO_IP_FORWARDING, OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "non-local-source-routing", DHO_NON_LOCAL_SOURCE_ROUTING,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "policy-filter", DHO_POLICY_FILTER, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "max-dgram-reassembly", DHO_MAX_DGRAM_REASSEMBLY,
+      OPT_UINT16_TYPE, false, NO_RECORD_DEF },
+    { "default-ip-ttl", DHO_DEFAULT_IP_TTL, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+    { "path-mtu-aging-timeout", DHO_PATH_MTU_AGING_TIMEOUT,
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "path-mtu-plateau-table", DHO_PATH_MTU_PLATEAU_TABLE,
+      OPT_UINT16_TYPE, true, NO_RECORD_DEF },
+    { "interface-mtu", DHO_INTERFACE_MTU, OPT_UINT16_TYPE, false, NO_RECORD_DEF },
+    { "all-subnets-local", DHO_ALL_SUBNETS_LOCAL,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "broadcast-address", DHO_BROADCAST_ADDRESS,
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "perform-mask-discovery", DHO_PERFORM_MASK_DISCOVERY,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "mask-supplier", DHO_MASK_SUPPLIER, OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "router-discovery", DHO_ROUTER_DISCOVERY,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "router-solicitation-address", DHO_ROUTER_SOLICITATION_ADDRESS,
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "static-routes", DHO_STATIC_ROUTES,
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "trailer-encapsulation", DHO_TRAILER_ENCAPSULATION,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "arp-cache-timeout", DHO_ARP_CACHE_TIMEOUT,
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "ieee802-3-encapsulation", DHO_IEEE802_3_ENCAPSULATION,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "default-tcp-ttl", DHO_DEFAULT_TCP_TTL, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+    { "tcp-keepalive-internal", DHO_TCP_KEEPALIVE_INTERVAL,
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "tcp-keepalive-garbage", DHO_TCP_KEEPALIVE_GARBAGE,
+      OPT_BOOLEAN_TYPE, false, NO_RECORD_DEF },
+    { "nis-domain", DHO_NIS_DOMAIN, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "nis-servers", DHO_NIS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "ntp-servers", DHO_NTP_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "vendor-encapsulated-options", DHO_VENDOR_ENCAPSULATED_OPTIONS,
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "netbios-name-servers", DHO_NETBIOS_NAME_SERVERS,
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "netbios-dd-server", DHO_NETBIOS_DD_SERVER,
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "netbios-node-type", DHO_NETBIOS_NODE_TYPE,
+      OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+    { "netbios-scope", DHO_NETBIOS_SCOPE, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "font-servers", DHO_FONT_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "x-display-manager", DHO_X_DISPLAY_MANAGER,
+      OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "dhcp-requested-address", DHO_DHCP_REQUESTED_ADDRESS,
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-lease-time", DHO_DHCP_LEASE_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-option-overload", DHO_DHCP_OPTION_OVERLOAD,
+      OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-message-type", DHO_DHCP_MESSAGE_TYPE, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-server-identifier", DHO_DHCP_SERVER_IDENTIFIER,
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-parameter-request-list", DHO_DHCP_PARAMETER_REQUEST_LIST,
+      OPT_UINT8_TYPE, true, NO_RECORD_DEF },
+    { "dhcp-message", DHO_DHCP_MESSAGE, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-max-message-size", DHO_DHCP_MAX_MESSAGE_SIZE,
+      OPT_UINT16_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-renewal-time", DHO_DHCP_RENEWAL_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-rebinding-time", DHO_DHCP_REBINDING_TIME,
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "vendor-class-identifier", DHO_VENDOR_CLASS_IDENTIFIER,
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "dhcp-client-identifier", DHO_DHCP_CLIENT_IDENTIFIER,
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "nwip-domain-name", DHO_NWIP_DOMAIN_NAME, OPT_STRING_TYPE, false, NO_RECORD_DEF },
+    { "nwip-suboptions", DHO_NWIP_SUBOPTIONS, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "user-class", DHO_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "fqdn", DHO_FQDN, OPT_RECORD_TYPE, false, RECORD_DEF(FQDN_RECORDS) },
+    { "dhcp-agent-options", DHO_DHCP_AGENT_OPTIONS,
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    // Unfortunatelly the AUTHENTICATE option contains a 64-bit
+    // data field called 'replay-detection' that can't be added
+    // as a record field to a custom option. Also, there is no
+    // dedicated option class to handle it so we simply return
+    // binary option type for now.
+    // @todo implement a class to handle AUTH option.
+    { "authenticate", DHO_AUTHENTICATE, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "client-last-transaction-time", DHO_CLIENT_LAST_TRANSACTION_TIME,
+      OPT_UINT32_TYPE, false, NO_RECORD_DEF },
+    { "associated-ip", DHO_ASSOCIATED_IP, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF },
+    { "subnet-selection", DHO_SUBNET_SELECTION,
+      OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF },
+    // The following options need a special encoding of data
+    // being carried by them. Therefore, there is no way they can
+    // be handled by OptionCustom. We may need to implement
+    // dedicated classes to handle them. Until that happens
+    // let's treat them as 'binary' options.
+    { "domain-search", DHO_DOMAIN_SEARCH, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "vivco-suboptions", DHO_VIVCO_SUBOPTIONS,
+      OPT_BINARY_TYPE, false, NO_RECORD_DEF },
+    { "vivso-suboptions", DHO_VIVSO_SUBOPTIONS, OPT_BINARY_TYPE,
+      false, NO_RECORD_DEF }
+
+        // @todo add definitions for all remaning options.
+};
+
+/// Number of option definitions defined.
+const int OPTION_DEF_PARAMS_SIZE4  =
+    sizeof(OPTION_DEF_PARAMS4) / sizeof(OPTION_DEF_PARAMS4[0]);
+
+
+/// Start Definition of DHCPv6 options
+
 // client-fqdn
-RECORD_DECL(clientFqdnRecords, OPT_UINT8_TYPE, OPT_FQDN_TYPE);
+RECORD_DECL(CLIENT_FQDN_RECORDS, OPT_UINT8_TYPE, OPT_FQDN_TYPE);
 // geoconf-civic
-RECORD_DECL(geoconfCivicRecords, OPT_UINT8_TYPE, OPT_UINT16_TYPE,
+RECORD_DECL(GEOCONF_CIVIC_RECORDS, OPT_UINT8_TYPE, OPT_UINT16_TYPE,
             OPT_BINARY_TYPE);
 // iaddr
-RECORD_DECL(iaaddrRecords, OPT_IPV6_ADDRESS_TYPE, OPT_UINT32_TYPE,
+RECORD_DECL(IAADDR_RECORDS, OPT_IPV6_ADDRESS_TYPE, OPT_UINT32_TYPE,
             OPT_UINT32_TYPE);
 // ia-na
-RECORD_DECL(ianaRecords, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
+RECORD_DECL(IA_NA_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 // ia-pd
-RECORD_DECL(iapdRecords, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
+RECORD_DECL(IA_PD_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 // ia-prefix
-RECORD_DECL(iaPrefixRecords, OPT_UINT32_TYPE, OPT_UINT32_TYPE,
+RECORD_DECL(IA_PREFIX_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE,
             OPT_UINT8_TYPE, OPT_BINARY_TYPE);
 // lq-query
-RECORD_DECL(lqQueryRecords, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
+RECORD_DECL(LQ_QUERY_RECORDS, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
 // lq-relay-data
-RECORD_DECL(lqRelayData, OPT_IPV6_ADDRESS_TYPE, OPT_BINARY_TYPE);
+RECORD_DECL(LQ_RELAY_DATA_RECORDS, OPT_IPV6_ADDRESS_TYPE, OPT_BINARY_TYPE);
 // remote-id
-RECORD_DECL(remoteIdRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+RECORD_DECL(REMOTE_ID_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 // status-code
-RECORD_DECL(statusCodeRecords, OPT_UINT16_TYPE, OPT_STRING_TYPE);
+RECORD_DECL(STATUS_CODE_RECORDS, OPT_UINT16_TYPE, OPT_STRING_TYPE);
 // vendor-class
-RECORD_DECL(vendorClassRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+RECORD_DECL(VENDOR_CLASS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 // vendor-opts
-RECORD_DECL(vendorOptsRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+RECORD_DECL(VENDOR_OPTS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 
 /// Standard DHCPv6 option definitions.
 ///
@@ -90,12 +235,12 @@ RECORD_DECL(vendorOptsRecords, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 /// This however does not work on Solaris (GCC) which issues a
 /// warning about lack of initializers for some struct members
 /// causing build to fail.
-static const OptionDefParams OPTION_DEF_PARAMS6[] = {
+const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "clientid", D6O_CLIENTID, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
     { "serverid", D6O_SERVERID, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
-    { "ia-na", D6O_IA_NA, OPT_RECORD_TYPE, false, RECORD_DEF(ianaRecords) },
+    { "ia-na", D6O_IA_NA, OPT_RECORD_TYPE, false, RECORD_DEF(IA_NA_RECORDS) },
     { "ia-ta", D6O_IA_TA, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
-    { "iaaddr", D6O_IAADDR, OPT_RECORD_TYPE, false, RECORD_DEF(iaaddrRecords) },
+    { "iaaddr", D6O_IAADDR, OPT_RECORD_TYPE, false, RECORD_DEF(IAADDR_RECORDS) },
     { "oro", D6O_ORO, OPT_UINT16_TYPE, true, NO_RECORD_DEF },
     { "preference", D6O_PREFERENCE, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
     { "elapsed-time", D6O_ELAPSED_TIME, OPT_UINT16_TYPE, false, NO_RECORD_DEF },
@@ -109,13 +254,13 @@ static const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "auth", D6O_AUTH, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
     { "unicast", D6O_UNICAST, OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF },
     { "status-code", D6O_STATUS_CODE, OPT_RECORD_TYPE, false,
-      RECORD_DEF(statusCodeRecords) },
+      RECORD_DEF(STATUS_CODE_RECORDS) },
     { "rapid-commit", D6O_RAPID_COMMIT, OPT_EMPTY_TYPE, false, NO_RECORD_DEF },
     { "user-class", D6O_USER_CLASS, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
     { "vendor-class", D6O_VENDOR_CLASS, OPT_RECORD_TYPE, false,
-      RECORD_DEF(vendorClassRecords) },
+      RECORD_DEF(VENDOR_CLASS_RECORDS) },
     { "vendor-opts", D6O_VENDOR_OPTS, OPT_RECORD_TYPE, false,
-      RECORD_DEF(vendorOptsRecords) },
+      RECORD_DEF(VENDOR_OPTS_RECORDS) },
     { "interface-id", D6O_INTERFACE_ID, OPT_BINARY_TYPE, false, NO_RECORD_DEF },
     { "reconf-msg", D6O_RECONF_MSG, OPT_UINT8_TYPE, false, NO_RECORD_DEF },
     { "reconf-accept", D6O_RECONF_ACCEPT, OPT_EMPTY_TYPE, false,
@@ -127,9 +272,9 @@ static const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "dns-servers", D6O_NAME_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
       NO_RECORD_DEF },
     { "domain-search", D6O_DOMAIN_SEARCH, OPT_FQDN_TYPE, true, NO_RECORD_DEF },
-    { "ia-pd", D6O_IA_PD, OPT_RECORD_TYPE, false, RECORD_DEF(iapdRecords) },
+    { "ia-pd", D6O_IA_PD, OPT_RECORD_TYPE, false, RECORD_DEF(IA_PD_RECORDS) },
     { "iaprefix", D6O_IAPREFIX, OPT_RECORD_TYPE, false,
-      RECORD_DEF(iaPrefixRecords) },
+      RECORD_DEF(IA_PREFIX_RECORDS) },
     { "nis-servers", D6O_NIS_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
       NO_RECORD_DEF },
     { "nisp-servers", D6O_NISP_SERVERS, OPT_IPV6_ADDRESS_TYPE, true,
@@ -147,13 +292,13 @@ static const OptionDefParams OPTION_DEF_PARAMS6[] = {
     { "bcmcs-server-addr", D6O_BCMCS_SERVER_A, OPT_IPV6_ADDRESS_TYPE, true,
       NO_RECORD_DEF },
     { "geoconf-civic", D6O_GEOCONF_CIVIC, OPT_RECORD_TYPE, false,
-      RECORD_DEF(geoconfCivicRecords) },
+      RECORD_DEF(GEOCONF_CIVIC_RECORDS) },
     { "remote-id", D6O_REMOTE_ID, OPT_RECORD_TYPE, false,
-      RECORD_DEF(remoteIdRecords) },
+      RECORD_DEF(REMOTE_ID_RECORDS) },
     { "subscriber-id", D6O_SUBSCRIBER_ID, OPT_BINARY_TYPE, false,
       NO_RECORD_DEF },
     { "client-fqdn", D6O_CLIENT_FQDN, OPT_RECORD_TYPE, false,
-      RECORD_DEF(clientFqdnRecords) },
+      RECORD_DEF(CLIENT_FQDN_RECORDS) },
     { "pana-agent", D6O_PANA_AGENT, OPT_IPV6_ADDRESS_TYPE, true,
       NO_RECORD_DEF },
     { "new-posix-timezone", D6O_NEW_POSIX_TIMEZONE, OPT_STRING_TYPE, false,
@@ -162,11 +307,11 @@ static const OptionDefParams OPTION_DEF_PARAMS6[] = {
       NO_RECORD_DEF },
     { "ero", D6O_ERO, OPT_UINT16_TYPE, true, NO_RECORD_DEF },
     { "lq-query", D6O_LQ_QUERY, OPT_RECORD_TYPE, false,
-      RECORD_DEF(lqQueryRecords) },
+      RECORD_DEF(LQ_QUERY_RECORDS) },
     { "client-data", D6O_CLIENT_DATA, OPT_EMPTY_TYPE, false, NO_RECORD_DEF },
     { "clt-time", D6O_CLT_TIME, OPT_UINT32_TYPE, false, NO_RECORD_DEF },
     { "lq-relay-data", D6O_LQ_RELAY_DATA, OPT_RECORD_TYPE, false,
-      RECORD_DEF(lqRelayData) },
+      RECORD_DEF(LQ_RELAY_DATA_RECORDS) },
     { "lq-client-link", D6O_LQ_CLIENT_LINK, OPT_IPV6_ADDRESS_TYPE, true,
       NO_RECORD_DEF }
 
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 5799d58..4d177d1 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -33,8 +33,8 @@ libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
 libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
 libdhcp___unittests_SOURCES += option6_ia_unittest.cc
 libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
-libdhcp___unittests_SOURCES += option6_int_array_unittest.cc
-libdhcp___unittests_SOURCES += option6_int_unittest.cc
+libdhcp___unittests_SOURCES += option_int_unittest.cc
+libdhcp___unittests_SOURCES += option_int_array_unittest.cc
 libdhcp___unittests_SOURCES += option_data_types_unittest.cc
 libdhcp___unittests_SOURCES += option_definition_unittest.cc
 libdhcp___unittests_SOURCES += option_custom_unittest.cc
diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc
index 8ead521..e44ef58 100644
--- a/src/lib/dhcp/tests/libdhcp++_unittest.cc
+++ b/src/lib/dhcp/tests/libdhcp++_unittest.cc
@@ -17,12 +17,13 @@
 #include <dhcp/dhcp4.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/libdhcp++.h>
+#include <dhcp/option4_addrlst.h>
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option_custom.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_int_array.h>
 #include <util/buffer.h>
 
 #include <gtest/gtest.h>
@@ -55,23 +56,69 @@ public:
         return OptionPtr(option);
     }
 
-    /// @brief Test option option definition.
+    /// @brief Test DHCPv4 option definition.
     ///
     /// This function tests if option definition for standard
     /// option has been initialized correctly.
     ///
     /// @param code option code.
-    /// @param bug buffer to be used to create option instance.
+    /// @param begin iterator pointing a begining of a buffer to
+    /// be used to create option instance.
+    /// @param end iterator pointing an end of a buffer to be
+    /// used to create option instance.
+    /// @param expected_type type of the option created by the
+    /// factory function returned by the option definition.
+    static void testStdOptionDefs4(const uint16_t code,
+                                   const OptionBufferConstIter begin,
+                                   const OptionBufferConstIter end,
+                                   const std::type_info& expected_type) {
+        // Use V4 universe.
+        testStdOptionDefs(Option::V4, code, begin, end, expected_type);
+    }
+
+    /// @brief Test DHCPv6 option definition.
+    ///
+    /// This function tests if option definition for standard
+    /// option has been initialized correctly.
+    ///
+    /// @param code option code.
+    /// @param begin iterator pointing a begining of a buffer to
+    /// be used to create option instance.
+    /// @param end iterator pointing an end of a buffer to be
+    /// used to create option instance.
     /// @param expected_type type of the option created by the
     /// factory function returned by the option definition.
     static void testStdOptionDefs6(const uint16_t code,
-                             const OptionBuffer& buf,
-                             const std::type_info& expected_type) {
+                                   const OptionBufferConstIter begin,
+                                   const OptionBufferConstIter end,
+                                   const std::type_info& expected_type) {
+        // Use V6 universe.
+        testStdOptionDefs(Option::V6, code, begin, end, expected_type);
+    }
+private:
+
+    /// @brief Test DHCPv4 or DHCPv6 option definition.
+    ///
+    /// This function tests if option definition for standard
+    /// option has been initialized correctly.
+    ///
+    /// @param code option code.
+    /// @param begin iterator pointing a begining of a buffer to
+    /// be used to create option instance.
+    /// @param end iterator pointing an end of a buffer to be
+    /// used to create option instance.
+    /// @param expected_type type of the option created by the
+    /// factory function returned by the option definition.
+    static void testStdOptionDefs(const Option::Universe u,
+                                  const uint16_t code,
+                                  const OptionBufferConstIter begin,
+                                  const OptionBufferConstIter end,
+                                  const std::type_info& expected_type) {
         // Get all option definitions, we will use them to extract
         // the definition for a particular option code.
         // We don't have to initialize option definitions here because they
         // are initialized in the class's constructor.
-        OptionDefContainer options = LibDHCP::getOptionDefs(Option::V6);
+        OptionDefContainer options = LibDHCP::getOptionDefs(u);
         // Get the container index #1. This one allows for searching
         // option definitions using option code.
         const OptionDefContainerTypeIndex& idx = options.get<1>();
@@ -79,20 +126,25 @@ public:
         // For standard options we expect that the range returned
         // will contain single option as their codes are unique.
         OptionDefContainerTypeRange range = idx.equal_range(code);
-        ASSERT_EQ(1, std::distance(range.first, range.second));
+        ASSERT_EQ(1, std::distance(range.first, range.second))
+            << "Standard option definition for the code " << code
+            << " has not been found.";
         // If we have single option definition returned, the
         // first iterator holds it.
         OptionDefinitionPtr def = *(range.first);
         // It should not happen that option definition is NULL but
         // let's make sure (test should take things like that into
         // account).
-        ASSERT_TRUE(def);
+        ASSERT_TRUE(def) << "Option definition for the code "
+                         << code << " is NULL.";
         // Check that option definition is valid.
-        ASSERT_NO_THROW(def->validate());
+        ASSERT_NO_THROW(def->validate())
+            << "Option definition for the option code " << code
+            << " is invalid";
         OptionPtr option;
         // Create the option.
-        ASSERT_NO_THROW(option = def->optionFactory(Option::V6, code, buf))
-            << "Option creation failed to option code " << code;
+        ASSERT_NO_THROW(option = def->optionFactory(u, code, begin, end))
+            << "Option creation failed for option code " << code;
         // Make sure it is not NULL.
         ASSERT_TRUE(option);
         // And the actual object type is the one that we expect.
@@ -103,7 +155,8 @@ public:
     }
 };
 
-static const uint8_t packed[] = {
+// The DHCPv6 options in the wire format, used by multiple tests.
+const uint8_t v6packed[] = {
     0, 1, 0, 5, 100, 101, 102, 103, 104, // CLIENT_ID (9 bytes)
     0, 2, 0, 3, 105, 106, 107, // SERVER_ID (7 bytes)
     0, 14, 0, 0, // RAPID_COMMIT (0 bytes)
@@ -207,8 +260,8 @@ TEST_F(LibDhcpTest, packOptions6) {
     OutputBuffer assembled(512);
 
     EXPECT_NO_THROW(LibDHCP::packOptions6(assembled, opts));
-    EXPECT_EQ(sizeof(packed), assembled.getLength());
-    EXPECT_EQ(0, memcmp(assembled.getData(), packed, sizeof(packed)));
+    EXPECT_EQ(sizeof(v6packed), assembled.getLength());
+    EXPECT_EQ(0, memcmp(assembled.getData(), v6packed, sizeof(v6packed)));
 }
 
 TEST_F(LibDhcpTest, unpackOptions6) {
@@ -220,10 +273,10 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     isc::dhcp::Option::OptionCollection options; // list of options
 
     OptionBuffer buf(512);
-    memcpy(&buf[0], packed, sizeof(packed));
+    memcpy(&buf[0], v6packed, sizeof(v6packed));
 
     EXPECT_NO_THROW ({
-            LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(packed)),
+            LibDHCP::unpackOptions6(OptionBuffer(buf.begin(), buf.begin() + sizeof(v6packed)),
                                     options);
     });
 
@@ -234,14 +287,14 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     EXPECT_EQ(1, x->second->getType());  // this should be option 1
     ASSERT_EQ(9, x->second->len()); // it should be of length 9
     ASSERT_EQ(5, x->second->getData().size());
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed + 4, 5)); // data len=5
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v6packed + 4, 5)); // data len=5
 
         x = options.find(2);
     ASSERT_FALSE(x == options.end()); // option 2 should exist
     EXPECT_EQ(2, x->second->getType());  // this should be option 2
     ASSERT_EQ(7, x->second->len()); // it should be of length 7
     ASSERT_EQ(3, x->second->getData().size());
-    EXPECT_EQ(0, memcmp(&x->second->getData()[0], packed + 13, 3)); // data len=3
+    EXPECT_EQ(0, memcmp(&x->second->getData()[0], v6packed + 13, 3)); // data len=3
 
     x = options.find(14);
     ASSERT_FALSE(x == options.end()); // option 14 should exist
@@ -254,11 +307,11 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     EXPECT_EQ(6, x->second->getType());  // this should be option 6
     ASSERT_EQ(8, x->second->len()); // it should be of length 8
     // Option with code 6 is the OPTION_ORO. This option is
-    // represented by the Option6IntArray<uint16_t> class which
+    // represented by the OptionIntArray<uint16_t> class which
     // comprises the set of uint16_t values. We need to cast the
     // returned pointer to this type to get values stored in it.
-    boost::shared_ptr<Option6IntArray<uint16_t> > opt_oro =
-        boost::dynamic_pointer_cast<Option6IntArray<uint16_t> >(x->second);
+    boost::shared_ptr<OptionIntArray<uint16_t> > opt_oro =
+        boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(x->second);
     // This value will be NULL if cast was unsuccessful. This is the case
     // when returned option has different type than expected.
     ASSERT_TRUE(opt_oro);
@@ -280,8 +333,8 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     // Option with code 8 is OPTION_ELAPSED_TIME. This option is
     // represented by Option6Int<uint16_t> value that holds single
     // uint16_t value.
-    boost::shared_ptr<Option6Int<uint16_t> > opt_elapsed_time =
-        boost::dynamic_pointer_cast<Option6Int<uint16_t> >(x->second);
+    boost::shared_ptr<OptionInt<uint16_t> > opt_elapsed_time =
+        boost::dynamic_pointer_cast<OptionInt<uint16_t> >(x->second);
     // This value will be NULL if cast was unsuccessful. This is the case
     // when returned option has different type than expected.
     ASSERT_TRUE(opt_elapsed_time);
@@ -301,13 +354,17 @@ TEST_F(LibDhcpTest, unpackOptions6) {
     EXPECT_TRUE(x == options.end()); // option 32000 not found */
 }
 
-
+/// V4 Options being used to test pack/unpack operations.
+/// These are variable length options only so as there
+/// is no restriction on the data length being carried by them.
+/// For simplicity, we assign data of the length 3 for each
+/// of them.
 static uint8_t v4Opts[] = {
-    12,  3, 0,   1,  2,
-    13,  3, 10, 11, 12,
-    14,  3, 20, 21, 22,
-    254, 3, 30, 31, 32,
-    128, 3, 40, 41, 42
+    12,  3, 0,   1,  2, // Hostname
+    60,  3, 10, 11, 12, // Class Id
+    14,  3, 20, 21, 22, // Merit Dump File
+    254, 3, 30, 31, 32, // Reserved
+    128, 3, 40, 41, 42  // Vendor specific
 };
 
 TEST_F(LibDhcpTest, packOptions4) {
@@ -321,7 +378,7 @@ TEST_F(LibDhcpTest, packOptions4) {
     }
 
     OptionPtr opt1(new Option(Option::V4, 12, payload[0]));
-    OptionPtr opt2(new Option(Option::V4, 13, payload[1]));
+    OptionPtr opt2(new Option(Option::V4, 60, 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]));
@@ -344,11 +401,11 @@ TEST_F(LibDhcpTest, packOptions4) {
 
 TEST_F(LibDhcpTest, unpackOptions4) {
 
-    vector<uint8_t> packed(v4Opts, v4Opts + sizeof(v4Opts));
+    vector<uint8_t> v4packed(v4Opts, v4Opts + sizeof(v4Opts));
     isc::dhcp::Option::OptionCollection options; // list of options
 
     ASSERT_NO_THROW(
-        LibDHCP::unpackOptions4(packed, options);
+        LibDHCP::unpackOptions4(v4packed, options);
     );
 
     isc::dhcp::Option::OptionCollection::const_iterator x = options.find(12);
@@ -358,9 +415,9 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+2, 3)); // data len=3
 
-    x = options.find(13);
-    ASSERT_FALSE(x == options.end()); // option 1 should exist
-    EXPECT_EQ(13, x->second->getType());  // this should be option 13
+    x = options.find(60);
+    ASSERT_FALSE(x == options.end()); // option 2 should exist
+    EXPECT_EQ(60, x->second->getType());  // this should be option 60
     ASSERT_EQ(3, x->second->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->second->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->second->getData()[0], v4Opts+7, 3)); // data len=3
@@ -396,6 +453,235 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     EXPECT_TRUE(x == options.end()); // option 2 not found
 }
 
+TEST_F(LibDhcpTest, stdOptionDefs4) {
+
+    // Create a buffer that holds dummy option data.
+    // It will be used to create most of the options.
+    std::vector<uint8_t> buf(48, 1);
+    OptionBufferConstIter begin = buf.begin();
+    OptionBufferConstIter end = buf.begin();
+
+    LibDhcpTest::testStdOptionDefs4(DHO_SUBNET_MASK, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_TIME_OFFSET, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ROUTERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_TIME_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NAME_SERVERS, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DOMAIN_NAME_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_LOG_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_COOKIE_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_LPR_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_IMPRESS_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_RESOURCE_LOCATION_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_HOST_NAME, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_BOOT_SIZE, begin, begin + 2,
+                                    typeid(OptionInt<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_MERIT_DUMP, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DOMAIN_NAME, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_SWAP_SERVER, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ROOT_PATH, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_EXTENSIONS_PATH, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_IP_FORWARDING, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NON_LOCAL_SOURCE_ROUTING, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_POLICY_FILTER, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_MAX_DGRAM_REASSEMBLY, begin, begin + 2,
+                                    typeid(OptionInt<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DEFAULT_IP_TTL, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_PATH_MTU_AGING_TIMEOUT, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_PATH_MTU_PLATEAU_TABLE, begin, begin + 10,
+                                    typeid(OptionIntArray<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_INTERFACE_MTU, begin, begin + 2,
+                                    typeid(OptionInt<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ALL_SUBNETS_LOCAL, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_BROADCAST_ADDRESS, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_PERFORM_MASK_DISCOVERY, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_MASK_SUPPLIER, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ROUTER_DISCOVERY, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ROUTER_SOLICITATION_ADDRESS, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_STATIC_ROUTES, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_TRAILER_ENCAPSULATION, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ARP_CACHE_TIMEOUT, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_IEEE802_3_ENCAPSULATION, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DEFAULT_TCP_TTL, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_INTERVAL, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_TCP_KEEPALIVE_GARBAGE, begin, begin + 1,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NIS_DOMAIN, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NIS_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NTP_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_ENCAPSULATED_OPTIONS, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NAME_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_DD_SERVER, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NODE_TYPE, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_SCOPE, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_FONT_SERVERS, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_X_DISPLAY_MANAGER, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_REQUESTED_ADDRESS, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_LEASE_TIME, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_OPTION_OVERLOAD, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_MESSAGE_TYPE, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_SERVER_IDENTIFIER, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_PARAMETER_REQUEST_LIST, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_MESSAGE, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_MAX_MESSAGE_SIZE, begin, begin + 2,
+                                    typeid(OptionInt<uint16_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_RENEWAL_TIME, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_REBINDING_TIME, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_VENDOR_CLASS_IDENTIFIER, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_CLIENT_IDENTIFIER, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NWIP_DOMAIN_NAME, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_NWIP_SUBOPTIONS, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_USER_CLASS, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_FQDN, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DHCP_AGENT_OPTIONS, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_AUTHENTICATE, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_CLIENT_LAST_TRANSACTION_TIME,
+                                    begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_ASSOCIATED_IP, begin, end,
+                                    typeid(Option4AddrLst));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_SUBNET_SELECTION, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_DOMAIN_SEARCH, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_VIVCO_SUBOPTIONS, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs4(DHO_VIVSO_SUBOPTIONS, begin, end,
+                                    typeid(Option));
+}
+
 // Test that definitions of standard options have been initialized
 // correctly.
 // @todo Only limited number of option definitions are now created
@@ -406,6 +692,8 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
     // Create a buffer that holds dummy option data.
     // It will be used to create most of the options.
     std::vector<uint8_t> buf(48, 1);
+    OptionBufferConstIter begin = buf.begin();
+    OptionBufferConstIter end = buf.end();
 
     // Prepare buffer holding an array of FQDNs.
     const char data[] = {
@@ -428,122 +716,144 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
                            fqdn_buf.end());
 
     // The actual test starts here for all supported option codes.
-    LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_SERVERID, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_SERVERID, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_IA_NA, buf, typeid(Option6IA));
+    LibDhcpTest::testStdOptionDefs6(D6O_IA_NA, begin, end,
+                                    typeid(Option6IA));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_IA_TA, buf,
-                                    typeid(Option6Int<uint32_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_IA_TA, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_IAADDR, buf, typeid(Option6IAAddr));
+    LibDhcpTest::testStdOptionDefs6(D6O_IAADDR, begin, end,
+                                    typeid(Option6IAAddr));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_ORO, buf,
-                                    typeid(Option6IntArray<uint16_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_ORO, begin, end,
+                                    typeid(OptionIntArray<uint16_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_PREFERENCE, buf,
-                                    typeid(Option6Int<uint8_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_PREFERENCE, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_ELAPSED_TIME, buf,
-                                    typeid(Option6Int<uint16_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_ELAPSED_TIME, begin, begin + 2,
+                                    typeid(OptionInt<uint16_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_RELAY_MSG, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_RELAY_MSG, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_STATUS_CODE, buf, typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_STATUS_CODE, begin, end,
+                                    typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_RAPID_COMMIT, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_RAPID_COMMIT, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_USER_CLASS, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_USER_CLASS, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_CLASS, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_CLASS, begin, end,
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_OPTS, buf, typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_VENDOR_OPTS, begin, end,
+                                    typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_INTERFACE_ID, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_INTERFACE_ID, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_RECONF_MSG, buf,
-                                    typeid(Option6Int<uint8_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_RECONF_MSG, begin, begin + 1,
+                                    typeid(OptionInt<uint8_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_RECONF_ACCEPT, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_RECONF_ACCEPT, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_SIP_SERVERS_DNS, fqdn_buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_SIP_SERVERS_DNS, fqdn_buf.begin(),
+                                    fqdn_buf.end(),
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_SIP_SERVERS_ADDR, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_SIP_SERVERS_ADDR, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NAME_SERVERS, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NAME_SERVERS, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_DOMAIN_SEARCH, fqdn_buf,
-                                    typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_DOMAIN_SEARCH, fqdn_buf.begin(),
+                                    fqdn_buf.end(), typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_IA_PD, buf, typeid(Option6IA));
+    LibDhcpTest::testStdOptionDefs6(D6O_IA_PD, begin, end,
+                                    typeid(Option6IA));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_IAPREFIX, buf, typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_IAPREFIX, begin, end,
+                                    typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NIS_SERVERS, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NIS_SERVERS, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NISP_SERVERS, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NISP_SERVERS, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NIS_DOMAIN_NAME, fqdn_buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NIS_DOMAIN_NAME, fqdn_buf.begin(),
+                                    fqdn_buf.end(),
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NISP_DOMAIN_NAME, fqdn_buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NISP_DOMAIN_NAME, fqdn_buf.begin(),
+                                    fqdn_buf.end(),
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_SNTP_SERVERS, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_SNTP_SERVERS, begin, end,
                                     typeid(Option6AddrLst));
 
     LibDhcpTest::testStdOptionDefs6(D6O_INFORMATION_REFRESH_TIME,
-                                    buf, typeid(Option6Int<uint32_t>));
+                                    begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_BCMCS_SERVER_D, fqdn_buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_BCMCS_SERVER_D, fqdn_buf.begin(),
+                                    fqdn_buf.end(),
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_BCMCS_SERVER_A, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_BCMCS_SERVER_A, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_GEOCONF_CIVIC, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_GEOCONF_CIVIC, begin, end,
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_REMOTE_ID, buf, typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_REMOTE_ID, begin, end,
+                                    typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_SUBSCRIBER_ID, buf,typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_SUBSCRIBER_ID, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_FQDN, client_fqdn_buf,
-                                    typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_FQDN, client_fqdn_buf.begin(),
+                                    client_fqdn_buf.end(), typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, begin, end,
                                     typeid(Option6AddrLst));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NEW_POSIX_TIMEZONE, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NEW_POSIX_TIMEZONE, begin, end,
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_NEW_TZDB_TIMEZONE, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_NEW_TZDB_TIMEZONE, begin, end,
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_ERO, buf,
-                                    typeid(Option6IntArray<uint16_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_ERO, begin, end,
+                                    typeid(OptionIntArray<uint16_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_LQ_QUERY, buf, typeid(OptionCustom));
+    LibDhcpTest::testStdOptionDefs6(D6O_LQ_QUERY, begin, end,
+                                    typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_DATA, buf, typeid(Option));
+    LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_DATA, begin, end,
+                                    typeid(Option));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_CLT_TIME, buf,
-                                    typeid(Option6Int<uint32_t>));
+    LibDhcpTest::testStdOptionDefs6(D6O_CLT_TIME, begin, begin + 4,
+                                    typeid(OptionInt<uint32_t>));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_LQ_RELAY_DATA, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_LQ_RELAY_DATA, begin, end,
                                     typeid(OptionCustom));
 
-    LibDhcpTest::testStdOptionDefs6(D6O_LQ_CLIENT_LINK, buf,
+    LibDhcpTest::testStdOptionDefs6(D6O_LQ_CLIENT_LINK, begin, end,
                                     typeid(Option6AddrLst));
 }
 
diff --git a/src/lib/dhcp/tests/option6_int_array_unittest.cc b/src/lib/dhcp/tests/option6_int_array_unittest.cc
deleted file mode 100644
index 3a8640a..0000000
--- a/src/lib/dhcp/tests/option6_int_array_unittest.cc
+++ /dev/null
@@ -1,421 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <dhcp/dhcp6.h>
-#include <dhcp/option.h>
-#include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int_array.h>
-#include <util/buffer.h>
-
-#include <boost/pointer_cast.hpp>
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
-using namespace isc::asiolink;
-using namespace isc::util;
-
-namespace {
-
-/// @brief Option6IntArray test class.
-class Option6IntArrayTest : public ::testing::Test {
-public:
-    /// @brief Constructor.
-    ///
-    /// Initializes the option buffer with some data.
-    Option6IntArrayTest(): buf_(255), out_buf_(255) {
-        for (int i = 0; i < 255; i++) {
-            buf_[i] = 255 - i;
-        }
-    }
-
-    /// @brief Test parsing buffer into array of int8_t or uint8_t values.
-    ///
-    /// @warning this function does not perform type check. Make
-    /// sure that only int8_t or uint8_t type is used.
-    ///
-    /// @tparam T int8_t or uint8_t.
-    template<typename T>
-    void bufferToIntTest8() {
-        // Create option that conveys array of multiple uint8_t or int8_t values.
-        // In fact there is no need to use this template class for array
-        // of uint8_t values because Option class is sufficient - it
-        // returns the buffer which is actually the array of uint8_t.
-        // However, since we allow using uint8_t types with this template
-        // class we have to test it here.
-        boost::shared_ptr<Option6IntArray<T> > opt;
-        const int opt_len = 10;
-        const uint16_t opt_code = 80;
-
-        // Constructor throws exception if provided buffer is empty.
-        EXPECT_THROW(
-            Option6IntArray<T>(opt_code, buf_.begin(), buf_.begin()),
-            isc::OutOfRange
-        );
-
-        // Provided buffer is not empty so it should not throw exception.
-        ASSERT_NO_THROW(
-            opt = boost::shared_ptr<
-                Option6IntArray<T> >(new Option6IntArray<T>(opt_code, buf_.begin(),
-                                                            buf_.begin() + opt_len))
-        );
-
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(opt_code, opt->getType());
-        // Option should return the collection of int8_t or uint8_t values that
-        // we can match with the buffer we used to create the option.
-        std::vector<T> values = opt->getValues();
-        // We need to copy values from the buffer to apply sign if signed
-        // type is used.
-        std::vector<T> reference_values;
-        for (int i = 0; i < opt_len; ++i) {
-            // Values have been read from the buffer in network
-            // byte order. We put them back in the same order here.
-            reference_values.push_back(static_cast<T>(buf_[i]));
-        }
-
-        // Compare the values against the reference buffer.
-        ASSERT_EQ(opt_len, values.size());
-        EXPECT_TRUE(std::equal(reference_values.begin(), reference_values.begin()
-                               + opt_len, values.begin()));
-
-        // test for pack()
-        opt->pack(out_buf_);
-
-        // Data length is 10 bytes.
-        EXPECT_EQ(10, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(opt_code, opt->getType());
-        // The total length is 10 bytes for data and 4 bytes for header.
-        ASSERT_EQ(14, out_buf_.getLength());
-
-        // Check if pack worked properly:
-        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(opt_code, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(10, out.readUint16());
-        // if data is correct
-        std::vector<uint8_t> out_data;
-        ASSERT_NO_THROW(out.readVector(out_data, opt_len));
-        ASSERT_EQ(opt_len, out_data.size());
-        EXPECT_TRUE(std::equal(buf_.begin(), buf_.begin() + opt_len, out_data.begin()));;
-    }
-
-    /// @brief Test parsing buffer into array of int16_t or uint16_t values.
-    ///
-    /// @warning this function does not perform type check. Make
-    /// sure that only int16_t or uint16_t type is used.
-    ///
-    /// @tparam T int16_t or uint16_t.
-    template<typename T>
-    void bufferToIntTest16() {
-        // Create option that conveys array of multiple uint16_t or int16_t values.
-        boost::shared_ptr<Option6IntArray<T> > opt;
-        const int opt_len = 20;
-        const uint16_t opt_code = 81;
-
-        // Constructor throws exception if provided buffer is empty.
-        EXPECT_THROW(
-            Option6IntArray<T>(opt_code, buf_.begin(), buf_.begin()),
-            isc::OutOfRange
-        );
-
-        // Constructor throws exception if provided buffer's length is not
-        // multiple of 2-bytes.
-        EXPECT_THROW(
-            Option6IntArray<T>(opt_code, buf_.begin(), buf_.begin() + 5),
-            isc::OutOfRange
-        );
-
-        // Now the buffer length is correct.
-        ASSERT_NO_THROW(
-            opt = boost::shared_ptr<
-                Option6IntArray<T> >(new Option6IntArray<T>(opt_code, buf_.begin(),
-                                                            buf_.begin() + opt_len))
-        );
-
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(opt_code, opt->getType());
-        // Option should return vector of uint16_t values which should be
-        // constructed from the buffer we provided.
-        std::vector<T> values = opt->getValues();
-        ASSERT_EQ(opt_len, values.size() * sizeof(T));
-        // Create reference values from the buffer so as we can
-        // simply compare two vectors.
-        std::vector<T> reference_values;
-        for (int i = 0; i < opt_len; i += 2) {
-            reference_values.push_back((buf_[i] << 8) |
-                                       buf_[i + 1]);
-        }
-        EXPECT_TRUE(std::equal(reference_values.begin(), reference_values.end(),
-                               values.begin()));
-
-        // Test for pack()
-        opt->pack(out_buf_);
-
-        // Data length is 20 bytes.
-        EXPECT_EQ(20, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(opt_code, opt->getType());
-        // The total length is 20 bytes for data and 4 bytes for header.
-        ASSERT_EQ(24, out_buf_.getLength());
-
-        // Check if pack worked properly:
-        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(opt_code, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(20, out.readUint16());
-        // if data is correct
-        std::vector<uint8_t> out_data;
-        ASSERT_NO_THROW(out.readVector(out_data, opt_len));
-        ASSERT_EQ(opt_len, out_data.size());
-        EXPECT_TRUE(std::equal(buf_.begin(), buf_.begin() + opt_len, out_data.begin()));;
-    }
-
-    /// @brief Test parsing buffer into array of int32_t or uint32_t values.
-    ///
-    /// @warning this function does not perform type check. Make
-    /// sure that only int32_t or uint32_t type is used.
-    ///
-    /// @tparam T int32_t or uint32_t.
-    template<typename T>
-    void bufferToIntTest32() {
-        // Create option that conveys array of multiple uint16_t values.
-        boost::shared_ptr<Option6IntArray<T> > opt;
-        const int opt_len = 40;
-        const uint16_t opt_code = 82;
-
-        // Constructor throws exception if provided buffer is empty.
-        EXPECT_THROW(
-            Option6IntArray<T>(opt_code, buf_.begin(), buf_.begin()),
-            isc::OutOfRange
-        );
-
-        // Constructor throws exception if provided buffer's length is not
-        // multiple of 4-bytes.
-        EXPECT_THROW(
-            Option6IntArray<T>(opt_code, buf_.begin(), buf_.begin() + 9),
-            isc::OutOfRange
-        );
-
-        // Now the buffer length is correct.
-        ASSERT_NO_THROW(
-            opt = boost::shared_ptr<
-                Option6IntArray<T> >(new Option6IntArray<T>(opt_code, buf_.begin(),
-                                                            buf_.begin() + opt_len))
-        );
-
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(opt_code, opt->getType());
-        // Option should return vector of uint32_t values which should be
-        // constructed from the buffer we provided.
-        std::vector<T> values = opt->getValues();
-        ASSERT_EQ(opt_len, values.size() * sizeof(T));
-        // Create reference values from the buffer so as we can
-        // simply compare two vectors.
-        std::vector<T> reference_values;
-        for (int i = 0; i < opt_len; i += 4) {
-            reference_values.push_back((buf_[i] << 24) |
-                                       (buf_[i + 1] << 16 & 0x00FF0000) |
-                                       (buf_[i + 2] << 8 & 0xFF00) |
-                                       (buf_[i + 3] & 0xFF));
-        }
-        EXPECT_TRUE(std::equal(reference_values.begin(), reference_values.end(),
-                               values.begin()));
-
-        // Test for pack()
-        opt->pack(out_buf_);
-
-        // Data length is 40 bytes.
-        EXPECT_EQ(40, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(opt_code, opt->getType());
-        // The total length is 40 bytes for data and 4 bytes for header.
-        ASSERT_EQ(44, out_buf_.getLength());
-
-        // Check if pack worked properly:
-        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(opt_code, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(40, out.readUint16());
-        // if data is correct
-        std::vector<uint8_t> out_data;
-        ASSERT_NO_THROW(out.readVector(out_data, opt_len));
-        ASSERT_EQ(opt_len, out_data.size());
-        EXPECT_TRUE(std::equal(buf_.begin(), buf_.begin() + opt_len, out_data.begin()));;
-    }
-
-
-    OptionBuffer buf_;     ///< Option buffer
-    OutputBuffer out_buf_; ///< Output buffer
-};
-
-/// @todo: below, there is a bunch of tests for options that
-/// convey unsigned values. We should maybe extend these tests for
-/// signed types too.
-
-TEST_F(Option6IntArrayTest, useInvalidType) {
-    const uint16_t opt_code = 80;
-    EXPECT_THROW(
-        boost::scoped_ptr<
-            Option6IntArray<bool> >(new Option6IntArray<bool>(opt_code, OptionBuffer(5))),
-        InvalidDataType
-    );
-
-    EXPECT_THROW(
-        boost::scoped_ptr<
-            Option6IntArray<int64_t> >(new Option6IntArray<int64_t>(opt_code,
-                                                                    OptionBuffer(10))),
-        InvalidDataType
-    );
-
-}
-
-TEST_F(Option6IntArrayTest, bufferToUint8) {
-    bufferToIntTest8<uint8_t>();
-}
-
-TEST_F(Option6IntArrayTest, bufferToInt8) {
-    bufferToIntTest8<int8_t>();
-}
-
-TEST_F(Option6IntArrayTest, bufferToUint16) {
-    bufferToIntTest16<uint16_t>();
-}
-
-TEST_F(Option6IntArrayTest, bufferToInt16) {
-    bufferToIntTest16<int16_t>();
-}
-
-TEST_F(Option6IntArrayTest, bufferToUint32) {
-    bufferToIntTest32<uint32_t>();
-}
-
-TEST_F(Option6IntArrayTest, bufferToInt32) {
-    bufferToIntTest32<int32_t>();
-}
-
-TEST_F(Option6IntArrayTest, setValuesUint8) {
-    const uint16_t opt_code = 100;
-    // Create option with empty vector of values.
-    boost::shared_ptr<Option6IntArray<uint8_t> > opt(new Option6IntArray<uint8_t>(opt_code));
-    // Initialize vector with some data and pass to the option.
-    std::vector<uint8_t> values;
-    for (int i = 0; i < 10; ++i) {
-        values.push_back(numeric_limits<uint8_t>::max() - i);
-    }
-    opt->setValues(values);
-
-    // Check if universe, option type and data was set correctly.
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(opt_code, opt->getType());
-    std::vector<uint8_t> returned_values = opt->getValues();
-    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
-}
-
-TEST_F(Option6IntArrayTest, setValuesInt8) {
-    const uint16_t opt_code = 100;
-    // Create option with empty vector of values.
-    boost::shared_ptr<Option6IntArray<int8_t> > opt(new Option6IntArray<int8_t>(opt_code));
-    // Initialize vector with some data and pass to the option.
-    std::vector<int8_t> values;
-    for (int i = 0; i < 10; ++i) {
-        values.push_back(numeric_limits<int8_t>::min() + i);
-    }
-    opt->setValues(values);
-
-    // Check if universe, option type and data was set correctly.
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(opt_code, opt->getType());
-    std::vector<int8_t> returned_values = opt->getValues();
-    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
-}
-
-TEST_F(Option6IntArrayTest, setValuesUint16) {
-    const uint16_t opt_code = 101;
-    // Create option with empty vector of values.
-    boost::shared_ptr<Option6IntArray<uint16_t> > opt(new Option6IntArray<uint16_t>(opt_code));
-    // Initialize vector with some data and pass to the option.
-    std::vector<uint16_t> values;
-    for (int i = 0; i < 10; ++i) {
-        values.push_back(numeric_limits<uint16_t>::max() - i);
-    }
-    opt->setValues(values);
-
-    // Check if universe, option type and data was set correctly.
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(opt_code, opt->getType());
-    std::vector<uint16_t> returned_values = opt->getValues();
-    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
-}
-
-TEST_F(Option6IntArrayTest, setValuesInt16) {
-    const uint16_t opt_code = 101;
-    // Create option with empty vector of values.
-    boost::shared_ptr<Option6IntArray<int16_t> > opt(new Option6IntArray<int16_t>(opt_code));
-    // Initialize vector with some data and pass to the option.
-    std::vector<int16_t> values;
-    for (int i = 0; i < 10; ++i) {
-        values.push_back(numeric_limits<int16_t>::min() + i);
-    }
-    opt->setValues(values);
-
-    // Check if universe, option type and data was set correctly.
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(opt_code, opt->getType());
-    std::vector<int16_t> returned_values = opt->getValues();
-    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
-}
-
-TEST_F(Option6IntArrayTest, setValuesUint32) {
-    const uint32_t opt_code = 101;
-    // Create option with empty vector of values.
-    boost::shared_ptr<Option6IntArray<uint32_t> > opt(new Option6IntArray<uint32_t>(opt_code));
-    // Initialize vector with some data and pass to the option.
-    std::vector<uint32_t> values;
-    for (int i = 0; i < 10; ++i) {
-        values.push_back(numeric_limits<uint32_t>::max() - i);
-    }
-    opt->setValues(values);
-
-    // Check if universe, option type and data was set correctly.
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(opt_code, opt->getType());
-    std::vector<uint32_t> returned_values = opt->getValues();
-    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
-}
-
-TEST_F(Option6IntArrayTest, setValuesInt32) {
-    const uint32_t opt_code = 101;
-    // Create option with empty vector of values.
-    boost::shared_ptr<Option6IntArray<int32_t> > opt(new Option6IntArray<int32_t>(opt_code));
-    // Initialize vector with some data and pass to the option.
-    std::vector<int32_t> values;
-    for (int i = 0; i < 10; ++i) {
-        values.push_back(numeric_limits<int32_t>::min() + i);
-    }
-    opt->setValues(values);
-
-    // Check if universe, option type and data was set correctly.
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(opt_code, opt->getType());
-    std::vector<int32_t> returned_values = opt->getValues();
-    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
-}
-
-
-} // anonymous namespace
diff --git a/src/lib/dhcp/tests/option6_int_unittest.cc b/src/lib/dhcp/tests/option6_int_unittest.cc
deleted file mode 100644
index c9eac9c..0000000
--- a/src/lib/dhcp/tests/option6_int_unittest.cc
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <config.h>
-
-#include <dhcp/dhcp6.h>
-#include <dhcp/option.h>
-#include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int.h>
-#include <util/buffer.h>
-
-#include <boost/pointer_cast.hpp>
-#include <gtest/gtest.h>
-
-using namespace std;
-using namespace isc;
-using namespace isc::dhcp;
-using namespace isc::asiolink;
-using namespace isc::util;
-
-namespace {
-
-/// @brief Option6Int test class.
-class Option6IntTest : public ::testing::Test {
-public:
-    /// @brief Constructor.
-    ///
-    /// Initializes the option buffer with some data.
-    Option6IntTest(): buf_(255), out_buf_(255) {
-        for (int i = 0; i < 255; i++) {
-            buf_[i] = 255 - i;
-        }
-    }
-
-    /// @brief Basic test for int8 and uint8 types.
-    ///
-    /// @note this function does not perform type check. Make
-    /// sure that only int8_t or uint8_t type is used.
-    ///
-    /// @tparam T int8_t or uint8_t.
-    template<typename T>
-    void basicTest8() {
-        // Create option that conveys single 8 bit integer value.
-        boost::shared_ptr<Option6Int<T> > opt;
-        // Initialize buffer with this value.
-        buf_[0] = 0xa1;
-        // Constructor may throw in case provided buffer is too short.
-        ASSERT_NO_THROW(
-            opt = boost::shared_ptr<Option6Int<T> >(new Option6Int<T>(D6O_PREFERENCE,
-                                                                      buf_.begin(),
-                                                                      buf_.end()))
-        );
-
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(D6O_PREFERENCE, opt->getType());
-        // Option should return the same value that we initialized the first
-        // byte of the buffer with.
-        EXPECT_EQ(static_cast<T>(0xa1), opt->getValue());
-
-        // test for pack()
-        opt->pack(out_buf_);
-
-        // Data length is 1 byte.
-        EXPECT_EQ(1, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(D6O_PREFERENCE, opt->getType());
-        // The total length is 1 byte for data and 4 bytes for header.
-        EXPECT_EQ(5, out_buf_.getLength());
-
-        // Check if pack worked properly:
-        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(D6O_PREFERENCE, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(1, out.readUint16());
-        // if data is correct
-        EXPECT_EQ(0xa1, out.readUint8() );
-    }
-
-    /// @brief Basic test for int16 and uint16 types.
-    ///
-    /// @note this function does not perform type check. Make
-    /// sure that only int16_t or uint16_t type is used.
-    ///
-    /// @tparam T int16_t or uint16_t.
-    template<typename T>
-    void basicTest16() {
-        // Create option that conveys single 16-bit integer value.
-        boost::shared_ptr<Option6Int<T> > opt;
-        // Initialize buffer with uint16_t value.
-        buf_[0] = 0xa1;
-        buf_[1] = 0xa2;
-        // Constructor may throw in case provided buffer is too short.
-        ASSERT_NO_THROW(
-            opt = boost::shared_ptr<Option6Int<T> >(new Option6Int<T>(D6O_ELAPSED_TIME,
-                                                                      buf_.begin(),
-                                                                      buf_.end()))
-        );
-
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
-        // Option should return the value equal to the contents of first
-        // and second byte of the buffer.
-        EXPECT_EQ(static_cast<T>(0xa1a2), opt->getValue());
-
-        // Test for pack()
-        opt->pack(out_buf_);
-
-        // Data length is 2 bytes.
-        EXPECT_EQ(2, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
-        // The total length is 2 byte for data and 4 bytes for header.
-        EXPECT_EQ(6, out_buf_.getLength());
-
-        // Check if pack worked properly:
-        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(D6O_ELAPSED_TIME, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(2, out.readUint16());
-        // if data is correct
-        EXPECT_EQ(0xa1a2, out.readUint16() );
-    }
-
-    /// @brief Basic test for int32 and uint32 types.
-    ///
-    /// @note this function does not perform type check. Make
-    /// sure that only int32_t or uint32_t type is used.
-    ///
-    /// @tparam T int32_t or uint32_t.
-    template<typename T>
-    void basicTest32() {
-        // Create option that conveys single 32-bit integer value.
-        boost::shared_ptr<Option6Int<T> > opt;
-        // Initialize buffer with 32-bit integer value.
-        buf_[0] = 0xa1;
-        buf_[1] = 0xa2;
-        buf_[2] = 0xa3;
-        buf_[3] = 0xa4;
-        // Constructor may throw in case provided buffer is too short.
-        ASSERT_NO_THROW(
-                        opt = boost::shared_ptr<Option6Int<T> >(new Option6Int<T>(D6O_CLT_TIME,
-                                                                                  buf_.begin(),
-                                                                                  buf_.end()))
-                        );
-
-        EXPECT_EQ(Option::V6, opt->getUniverse());
-        EXPECT_EQ(D6O_CLT_TIME, opt->getType());
-        // Option should return the value equal to the value made of
-        // first 4 bytes of the buffer.
-        EXPECT_EQ(static_cast<T>(0xa1a2a3a4), opt->getValue());
-
-        // Test for pack()
-        opt->pack(out_buf_);
-
-        // Data length is 4 bytes.
-        EXPECT_EQ(4, opt->len() - opt->getHeaderLen());
-        EXPECT_EQ(D6O_CLT_TIME, opt->getType());
-        // The total length is 4 bytes for data and 4 bytes for header.
-        EXPECT_EQ(8, out_buf_.getLength());
-
-        // Check if pack worked properly:
-        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
-        // if option type is correct
-        EXPECT_EQ(D6O_CLT_TIME, out.readUint16());
-        // if option length is correct
-        EXPECT_EQ(4, out.readUint16());
-        // if data is correct
-        EXPECT_EQ(0xa1a2a3a4, out.readUint32());
-    }
-
-    OptionBuffer buf_;     ///< Option buffer
-    OutputBuffer out_buf_; ///< Output buffer
-};
-
-/// @todo: below, there is a bunch of tests for options that
-/// convey unsigned value. We should maybe extend these tests for
-/// signed types too.
-
-TEST_F(Option6IntTest, useInvalidType) {
-    EXPECT_THROW(
-        boost::scoped_ptr<Option6Int<bool> >(new Option6Int<bool>(D6O_ELAPSED_TIME, 10)),
-        InvalidDataType
-    );
-
-    EXPECT_THROW(
-        boost::scoped_ptr<Option6Int<int64_t> >(new Option6Int<int64_t>(D6O_ELAPSED_TIME, 10)),
-        InvalidDataType
-    );
-
-}
-
-TEST_F(Option6IntTest, basicUint8) {
-    basicTest8<uint8_t>();
-}
-
-TEST_F(Option6IntTest, basicUint16) {
-    basicTest16<uint16_t>();
-}
-
-TEST_F(Option6IntTest, basicUint32) {
-    basicTest32<uint32_t>();
-}
-
-TEST_F(Option6IntTest, basicInt8) {
-    basicTest8<int8_t>();
-}
-
-TEST_F(Option6IntTest, basicInt16) {
-    basicTest16<int16_t>();
-}
-
-TEST_F(Option6IntTest, basicInt32) {
-    basicTest32<int32_t>();
-}
-
-TEST_F(Option6IntTest, setValueUint8) {
-    boost::shared_ptr<Option6Int<uint8_t> > opt(new Option6Int<uint8_t>(D6O_PREFERENCE, 123));
-    // Check if constructor intitialized the option value correctly.
-    EXPECT_EQ(123, opt->getValue());
-    // Override the value.
-    opt->setValue(111);
-
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_PREFERENCE, opt->getType());
-    // Check if the value has been overriden.
-    EXPECT_EQ(111, opt->getValue());
-}
-
-TEST_F(Option6IntTest, setValueInt8) {
-    boost::shared_ptr<Option6Int<int8_t> > opt(new Option6Int<int8_t>(D6O_PREFERENCE, -123));
-    // Check if constructor intitialized the option value correctly.
-    EXPECT_EQ(-123, opt->getValue());
-    // Override the value.
-    opt->setValue(-111);
-
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_PREFERENCE, opt->getType());
-    // Check if the value has been overriden.
-    EXPECT_EQ(-111, opt->getValue());
-}
-
-
-TEST_F(Option6IntTest, setValueUint16) {
-    boost::shared_ptr<Option6Int<uint16_t> > opt(new Option6Int<uint16_t>(D6O_ELAPSED_TIME, 123));
-    // Check if constructor intitialized the option value correctly.
-    EXPECT_EQ(123, opt->getValue());
-    // Override the value.
-    opt->setValue(0x0102);
-
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
-    // Check if the value has been overriden.
-    EXPECT_EQ(0x0102, opt->getValue());
-}
-
-TEST_F(Option6IntTest, setValueInt16) {
-    boost::shared_ptr<Option6Int<int16_t> > opt(new Option6Int<int16_t>(D6O_ELAPSED_TIME, -16500));
-    // Check if constructor intitialized the option value correctly.
-    EXPECT_EQ(-16500, opt->getValue());
-    // Override the value.
-    opt->setValue(-20100);
-
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
-    // Check if the value has been overriden.
-    EXPECT_EQ(-20100, opt->getValue());
-}
-
-TEST_F(Option6IntTest, setValueUint32) {
-    boost::shared_ptr<Option6Int<uint32_t> > opt(new Option6Int<uint32_t>(D6O_CLT_TIME, 123));
-    // Check if constructor intitialized the option value correctly.
-    EXPECT_EQ(123, opt->getValue());
-    // Override the value.
-    opt->setValue(0x01020304);
-
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_CLT_TIME, opt->getType());
-    // Check if the value has been overriden.
-    EXPECT_EQ(0x01020304, opt->getValue());
-}
-
-TEST_F(Option6IntTest, setValueint32) {
-    boost::shared_ptr<Option6Int<int32_t> > opt(new Option6Int<int32_t>(D6O_CLT_TIME, -120100));
-    // Check if constructor intitialized the option value correctly.
-    EXPECT_EQ(-120100, opt->getValue());
-    // Override the value.
-    opt->setValue(-125000);
-
-    EXPECT_EQ(Option::V6, opt->getUniverse());
-    EXPECT_EQ(D6O_CLT_TIME, opt->getType());
-    // Check if the value has been overriden.
-    EXPECT_EQ(-125000, opt->getValue());
-}
-
-TEST_F(Option6IntTest, packSuboptions) {
-    // option code is really uint16_t, but using uint8_t
-    // for easier conversion to uint8_t array.
-    uint8_t opt_code = 80;
-
-    boost::shared_ptr<Option6Int<uint32_t> > opt(new Option6Int<uint32_t>(opt_code, 0x01020304));
-    OptionPtr sub1(new Option(Option::V6, 0xcafe));
-
-    boost::shared_ptr<Option6IAAddr> addr1(
-        new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"), 0x5000, 0x7000));
-
-    opt->addOption(sub1);
-    opt->addOption(addr1);
-
-    ASSERT_EQ(28, addr1->len());
-    ASSERT_EQ(4, sub1->len());
-    ASSERT_EQ(40, opt->len());
-
-    uint8_t expected[] = {
-        0, opt_code, // type
-        0, 36, // length
-        0x01, 0x02, 0x03, 0x04, // uint32_t value
-
-        // iaaddr suboption
-        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
-        0, 0, 0x50, 0, // preferred-lifetime
-        0, 0, 0x70, 0, // valid-lifetime
-
-        // suboption
-        0xca, 0xfe, // type
-        0, 0 // len
-    };
-
-    // Create on-wire format of option and suboptions.
-    opt->pack(out_buf_);
-    // Compare the on-wire data with the reference buffer.
-    ASSERT_EQ(40, out_buf_.getLength());
-    EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, 40));
-}
-
-
-TEST_F(Option6IntTest, unpackSuboptions) {
-    // option code is really uint16_t, but using uint8_t
-    // for easier conversion to uint8_t array.
-    const uint8_t opt_code = 80;
-    // Prepare reference data.
-    uint8_t expected[] = {
-        0, opt_code, // type
-        0, 34, // length
-        0x01, 0x02, // uint16_t value
-
-        // iaaddr suboption
-        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
-        0, 0, 0x50, 0, // preferred-lifetime
-        0, 0, 0x70, 0, // valid-lifetime
-
-        // suboption
-        0xca, 0xfe, // type
-        0, 0 // len
-    };
-    ASSERT_EQ(38, sizeof(expected));
-
-    memcpy(&buf_[0], expected, sizeof(expected));
-
-    boost::shared_ptr<Option6Int<uint16_t> > opt;
-    EXPECT_NO_THROW(
-        opt = boost::shared_ptr<
-            Option6Int<uint16_t> >(new Option6Int<uint16_t>(opt_code, buf_.begin() + 4,
-                                                            buf_.begin() + sizeof(expected)));
-    );
-    ASSERT_TRUE(opt);
-
-    EXPECT_EQ(opt_code, opt->getType());
-    EXPECT_EQ(0x0102, opt->getValue());
-
-    // Checks for address option
-    OptionPtr subopt = opt->getOption(D6O_IAADDR);
-    ASSERT_TRUE(subopt);
-    boost::shared_ptr<Option6IAAddr> addr(boost::dynamic_pointer_cast<Option6IAAddr>(subopt));
-    ASSERT_TRUE(addr);
-
-    EXPECT_EQ(D6O_IAADDR, addr->getType());
-    EXPECT_EQ(28, addr->len());
-    EXPECT_EQ(0x5000, addr->getPreferred());
-    EXPECT_EQ(0x7000, addr->getValid());
-    EXPECT_EQ("2001:db8:1234:5678::abcd", addr->getAddress().toText());
-
-    // Checks for dummy option
-    subopt = opt->getOption(0xcafe);
-    ASSERT_TRUE(subopt); // should be non-NULL
-
-    EXPECT_EQ(0xcafe, subopt->getType());
-    EXPECT_EQ(4, subopt->len());
-    // There should be no data at all
-    EXPECT_EQ(0, subopt->getData().size());
-
-    // Try to get non-existent option.
-    subopt = opt->getOption(1);
-    // Expecting NULL which means that option does not exist.
-    ASSERT_FALSE(subopt);
-}
-
-} // anonymous namespace
diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc
index 044279b..a34852b 100644
--- a/src/lib/dhcp/tests/option_custom_unittest.cc
+++ b/src/lib/dhcp/tests/option_custom_unittest.cc
@@ -38,16 +38,8 @@ public:
     /// @param [out] buf output buffer.
     void writeAddress(const asiolink::IOAddress& address,
                       std::vector<uint8_t>& buf) {
-        short family = address.getFamily();
-        if (family == AF_INET) {
-            asio::ip::address_v4::bytes_type buf_addr =
-                address.getAddress().to_v4().to_bytes();
-            buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
-        } else if (family == AF_INET6) {
-            asio::ip::address_v6::bytes_type buf_addr =
-                address.getAddress().to_v6().to_bytes();
-            buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
-        }
+        const std::vector<uint8_t>& vec = address.toBytes();
+        buf.insert(buf.end(), vec.begin(), vec.end());
     }
 
     /// @brief Write integer (signed or unsigned) into a buffer.
diff --git a/src/lib/dhcp/tests/option_data_types_unittest.cc b/src/lib/dhcp/tests/option_data_types_unittest.cc
index 748a84b..fd5294c 100644
--- a/src/lib/dhcp/tests/option_data_types_unittest.cc
+++ b/src/lib/dhcp/tests/option_data_types_unittest.cc
@@ -34,16 +34,8 @@ public:
     /// @param [out] buf output buffer.
     void writeAddress(const asiolink::IOAddress& address,
                       std::vector<uint8_t>& buf) {
-        short family = address.getFamily();
-        if (family == AF_INET) {
-            asio::ip::address_v4::bytes_type buf_addr =
-                address.getAddress().to_v4().to_bytes();
-            buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
-        } else if (family == AF_INET6) {
-            asio::ip::address_v6::bytes_type buf_addr =
-                address.getAddress().to_v6().to_bytes();
-            buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
-        }
+        const std::vector<uint8_t>& vec = address.toBytes();
+        buf.insert(buf.end(), vec.begin(), vec.end());
     }
 
     /// @brief Write integer (signed or unsigned) into a buffer.
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 91b822c..deb61b5 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -21,10 +21,10 @@
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_int.h>
-#include <dhcp/option6_int_array.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_definition.h>
+#include <dhcp/option_int.h>
+#include <dhcp/option_int_array.h>
 #include <exceptions/exceptions.h>
 
 #include <boost/pointer_cast.hpp>
@@ -207,10 +207,9 @@ TEST_F(OptionDefinitionTest, ipv6AddressArray) {
     // Write addresses to the buffer.
     OptionBuffer buf(addrs.size() * asiolink::V6ADDRESS_LEN);
     for (int i = 0; i < addrs.size(); ++i) {
-        asio::ip::address_v6::bytes_type addr_bytes =
-            addrs[i].getAddress().to_v6().to_bytes();
-        ASSERT_EQ(asiolink::V6ADDRESS_LEN, addr_bytes.size());
-        std::copy(addr_bytes.begin(), addr_bytes.end(),
+        const std::vector<uint8_t>& vec = addrs[i].toBytes();
+        ASSERT_EQ(asiolink::V6ADDRESS_LEN, vec.size());
+        std::copy(vec.begin(), vec.end(),
                   buf.begin() + i * asiolink::V6ADDRESS_LEN);
     }
     // Create DHCPv6 option from this buffer. Once option is created it is
@@ -306,10 +305,9 @@ TEST_F(OptionDefinitionTest, ipv4AddressArray) {
     // Write addresses to the buffer.
     OptionBuffer buf(addrs.size() * asiolink::V4ADDRESS_LEN);
     for (int i = 0; i < addrs.size(); ++i) {
-        asio::ip::address_v4::bytes_type addr_bytes =
-            addrs[i].getAddress().to_v4().to_bytes();
-        ASSERT_EQ(asiolink::V4ADDRESS_LEN, addr_bytes.size());
-        std::copy(addr_bytes.begin(), addr_bytes.end(),
+        const std::vector<uint8_t> vec = addrs[i].toBytes();
+        ASSERT_EQ(asiolink::V4ADDRESS_LEN, vec.size());
+        std::copy(vec.begin(), vec.end(),
                   buf.begin() + i * asiolink::V4ADDRESS_LEN);
     }
     // Create DHCPv6 option from this buffer. Once option is created it is
@@ -512,11 +510,10 @@ TEST_F(OptionDefinitionTest, recordIAAddr6) {
     OptionPtr option_v6;
     asiolink::IOAddress addr_v6("2001:0db8::ff00:0042:8329");
     OptionBuffer buf(asiolink::V6ADDRESS_LEN);
-    ASSERT_TRUE(addr_v6.getAddress().is_v6());
-    asio::ip::address_v6::bytes_type addr_bytes =
-        addr_v6.getAddress().to_v6().to_bytes();
-    ASSERT_EQ(asiolink::V6ADDRESS_LEN, addr_bytes.size());
-    std::copy(addr_bytes.begin(), addr_bytes.end(), buf.begin());
+    ASSERT_TRUE(addr_v6.isV6());
+    const std::vector<uint8_t>& vec = addr_v6.toBytes();
+    ASSERT_EQ(asiolink::V6ADDRESS_LEN, vec.size());
+    std::copy(vec.begin(), vec.end(), buf.begin());
 
     for (int i = 0; i < option6_iaaddr_len - asiolink::V6ADDRESS_LEN; ++i) {
         buf.push_back(i);
@@ -578,10 +575,10 @@ TEST_F(OptionDefinitionTest, uint8) {
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, OptionBuffer(1, 1));
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint8_t>));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint8_t>));
     // Validate the value.
-    boost::shared_ptr<Option6Int<uint8_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6Int<uint8_t> >(option_v6);
+    boost::shared_ptr<OptionInt<uint8_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionInt<uint8_t> >(option_v6);
     EXPECT_EQ(1, option_cast_v6->getValue());
 
     // Try to provide zero-length buffer. Expect exception.
@@ -606,10 +603,10 @@ TEST_F(OptionDefinitionTest, uint8Tokenized) {
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint8_t>));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint8_t>));
     // Validate the value.
-    boost::shared_ptr<Option6Int<uint8_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6Int<uint8_t> >(option_v6);
+    boost::shared_ptr<OptionInt<uint8_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionInt<uint8_t> >(option_v6);
     EXPECT_EQ(123, option_cast_v6->getValue());
 
     // @todo Add more cases for DHCPv4
@@ -629,10 +626,10 @@ TEST_F(OptionDefinitionTest, uint16) {
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_ELAPSED_TIME, buf);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint16_t>));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint16_t>));
     // Validate the value.
-    boost::shared_ptr<Option6Int<uint16_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6Int<uint16_t> >(option_v6);
+    boost::shared_ptr<OptionInt<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionInt<uint16_t> >(option_v6);
     EXPECT_EQ(0x0102, option_cast_v6->getValue());
 
     // Try to provide zero-length buffer. Expect exception.
@@ -658,10 +655,10 @@ TEST_F(OptionDefinitionTest, uint16Tokenized) {
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_ELAPSED_TIME, values);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint16_t>));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint16_t>));
     // Validate the value.
-    boost::shared_ptr<Option6Int<uint16_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6Int<uint16_t> >(option_v6);
+    boost::shared_ptr<OptionInt<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionInt<uint16_t> >(option_v6);
     EXPECT_EQ(1234, option_cast_v6->getValue());
 
     // @todo Add more cases for DHCPv4
@@ -683,10 +680,10 @@ TEST_F(OptionDefinitionTest, uint32) {
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_CLT_TIME, buf);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint32_t>));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint32_t>));
     // Validate the value.
-    boost::shared_ptr<Option6Int<uint32_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6Int<uint32_t> >(option_v6);
+    boost::shared_ptr<OptionInt<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionInt<uint32_t> >(option_v6);
     EXPECT_EQ(0x01020304, option_cast_v6->getValue());
 
     // Try to provide too short buffer. Expect exception.
@@ -711,10 +708,10 @@ TEST_F(OptionDefinitionTest, uint32Tokenized) {
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_CLT_TIME, values);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6Int<uint32_t>));
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionInt<uint32_t>));
     // Validate the value.
-    boost::shared_ptr<Option6Int<uint32_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6Int<uint32_t> >(option_v6);
+    boost::shared_ptr<OptionInt<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionInt<uint32_t> >(option_v6);
     EXPECT_EQ(123456, option_cast_v6->getValue());
 
     // @todo Add more cases for DHCPv4
@@ -740,9 +737,9 @@ TEST_F(OptionDefinitionTest, uint16Array) {
     EXPECT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, opt_code, buf);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint16_t>));
-    boost::shared_ptr<Option6IntArray<uint16_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6IntArray<uint16_t> >(option_v6);
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionIntArray<uint16_t>));
+    boost::shared_ptr<OptionIntArray<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionIntArray<uint16_t> >(option_v6);
     // Get the values from the initiated options and validate.
     std::vector<uint16_t> values = option_cast_v6->getValues();
     for (int i = 0; i < values.size(); ++i) {
@@ -782,9 +779,9 @@ TEST_F(OptionDefinitionTest, uint16ArrayTokenized) {
     EXPECT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, opt_code, str_values);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint16_t>));
-    boost::shared_ptr<Option6IntArray<uint16_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6IntArray<uint16_t> >(option_v6);
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionIntArray<uint16_t>));
+    boost::shared_ptr<OptionIntArray<uint16_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionIntArray<uint16_t> >(option_v6);
     // Get the values from the initiated options and validate.
     std::vector<uint16_t> values = option_cast_v6->getValues();
     EXPECT_EQ(12345, values[0]);
@@ -813,9 +810,9 @@ TEST_F(OptionDefinitionTest, uint32Array) {
     EXPECT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, opt_code, buf);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint32_t>));
-    boost::shared_ptr<Option6IntArray<uint32_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6IntArray<uint32_t> >(option_v6);
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionIntArray<uint32_t>));
+    boost::shared_ptr<OptionIntArray<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionIntArray<uint32_t> >(option_v6);
     // Get the values from the initiated options and validate.
     std::vector<uint32_t> values = option_cast_v6->getValues();
     for (int i = 0; i < values.size(); ++i) {
@@ -858,9 +855,9 @@ TEST_F(OptionDefinitionTest, uint32ArrayTokenized) {
     EXPECT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, opt_code, str_values);
     );
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option6IntArray<uint32_t>));
-    boost::shared_ptr<Option6IntArray<uint32_t> > option_cast_v6 =
-        boost::static_pointer_cast<Option6IntArray<uint32_t> >(option_v6);
+    ASSERT_TRUE(typeid(*option_v6) == typeid(OptionIntArray<uint32_t>));
+    boost::shared_ptr<OptionIntArray<uint32_t> > option_cast_v6 =
+        boost::static_pointer_cast<OptionIntArray<uint32_t> >(option_v6);
     // Get the values from the initiated options and validate.
     std::vector<uint32_t> values = option_cast_v6->getValues();
     EXPECT_EQ(123456, values[0]);
diff --git a/src/lib/dhcp/tests/option_int_array_unittest.cc b/src/lib/dhcp/tests/option_int_array_unittest.cc
new file mode 100644
index 0000000..1aeb584
--- /dev/null
+++ b/src/lib/dhcp/tests/option_int_array_unittest.cc
@@ -0,0 +1,488 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_int_array.h>
+#include <util/buffer.h>
+
+#include <boost/pointer_cast.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace {
+
+/// @brief OptionIntArray test class.
+class OptionIntArrayTest : public ::testing::Test {
+public:
+    /// @brief Constructor.
+    ///
+    /// Initializes the option buffer with some data.
+    OptionIntArrayTest(): buf_(255), out_buf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
+    }
+
+    /// @brief Test parsing buffer into array of int8_t or uint8_t values.
+    ///
+    /// @warning this function does not perform type check. Make
+    /// sure that only int8_t or uint8_t type is used.
+    ///
+    /// @param u universe (v4 or V6).
+    /// @tparam T int8_t or uint8_t.
+    template<typename T>
+    void bufferToIntTest8(const Option::Universe u) {
+        // Create option that conveys array of multiple uint8_t or int8_t values.
+        // In fact there is no need to use this template class for array
+        // of uint8_t values because Option class is sufficient - it
+        // returns the buffer which is actually the array of uint8_t.
+        // However, since we allow using uint8_t types with this template
+        // class we have to test it here.
+        boost::shared_ptr<OptionIntArray<T> > opt;
+        const int opt_len = 10;
+        const uint16_t opt_code = 80;
+
+        // Constructor throws exception if provided buffer is empty.
+        EXPECT_THROW(
+            OptionIntArray<T>(u, opt_code, buf_.begin(), buf_.begin()),
+            isc::OutOfRange
+        );
+
+        // Provided buffer is not empty so it should not throw exception.
+        ASSERT_NO_THROW(
+            opt = boost::shared_ptr<
+                OptionIntArray<T> >(new OptionIntArray<T>(u, opt_code, buf_.begin(),
+                                                          buf_.begin() + opt_len))
+        );
+
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(opt_code, opt->getType());
+        // Option should return the collection of int8_t or uint8_t values that
+        // we can match with the buffer we used to create the option.
+        std::vector<T> values = opt->getValues();
+        // We need to copy values from the buffer to apply sign if signed
+        // type is used.
+        std::vector<T> reference_values;
+        for (int i = 0; i < opt_len; ++i) {
+            // Values have been read from the buffer in network
+            // byte order. We put them back in the same order here.
+            reference_values.push_back(static_cast<T>(buf_[i]));
+        }
+
+        // Compare the values against the reference buffer.
+        ASSERT_EQ(opt_len, values.size());
+        EXPECT_TRUE(std::equal(reference_values.begin(), reference_values.begin()
+                               + opt_len, values.begin()));
+
+        // test for pack()
+        opt->pack(out_buf_);
+
+        // Data length is 10 bytes.
+        EXPECT_EQ(10, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(opt_code, opt->getType());
+
+        // Check if pack worked properly:
+        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+
+        if (u == Option::V4) {
+            // The total length is 10 bytes for data and 2 bytes for a header.
+            ASSERT_EQ(12, out_buf_.getLength());
+            // if option type is correct
+            EXPECT_EQ(opt_code, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(10, out.readUint8());
+        } else {
+            // The total length is 10 bytes for data and 4 bytes for a header.
+            ASSERT_EQ(14, out_buf_.getLength());
+            // if option type is correct
+            EXPECT_EQ(opt_code, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(10, out.readUint16());
+        }
+
+        // if data is correct
+        std::vector<uint8_t> out_data;
+        ASSERT_NO_THROW(out.readVector(out_data, opt_len));
+        ASSERT_EQ(opt_len, out_data.size());
+        EXPECT_TRUE(std::equal(buf_.begin(), buf_.begin() + opt_len, out_data.begin()));;
+    }
+
+    /// @brief Test parsing buffer into array of int16_t or uint16_t values.
+    ///
+    /// @warning this function does not perform type check. Make
+    /// sure that only int16_t or uint16_t type is used.
+    ///
+    /// @param u universe (V4 or V6).
+    /// @tparam T int16_t or uint16_t.
+    template<typename T>
+    void bufferToIntTest16(const Option::Universe u) {
+        // Create option that conveys array of multiple uint16_t or int16_t values.
+        boost::shared_ptr<OptionIntArray<T> > opt;
+        const int opt_len = 20;
+        const uint16_t opt_code = 81;
+
+        // Constructor throws exception if provided buffer is empty.
+        EXPECT_THROW(
+            OptionIntArray<T>(u, opt_code, buf_.begin(), buf_.begin()),
+            isc::OutOfRange
+        );
+
+        // Constructor throws exception if provided buffer's length is not
+        // multiple of 2-bytes.
+        EXPECT_THROW(
+            OptionIntArray<T>(u, opt_code, buf_.begin(), buf_.begin() + 5),
+            isc::OutOfRange
+        );
+
+        // Now the buffer length is correct.
+        ASSERT_NO_THROW(
+            opt = boost::shared_ptr<
+                OptionIntArray<T> >(new OptionIntArray<T>(u, opt_code, buf_.begin(),
+                                                          buf_.begin() + opt_len))
+        );
+
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(opt_code, opt->getType());
+        // Option should return vector of uint16_t values which should be
+        // constructed from the buffer we provided.
+        std::vector<T> values = opt->getValues();
+        ASSERT_EQ(opt_len, values.size() * sizeof(T));
+        // Create reference values from the buffer so as we can
+        // simply compare two vectors.
+        std::vector<T> reference_values;
+        for (int i = 0; i < opt_len; i += 2) {
+            reference_values.push_back((buf_[i] << 8) |
+                                       buf_[i + 1]);
+        }
+        EXPECT_TRUE(std::equal(reference_values.begin(), reference_values.end(),
+                               values.begin()));
+
+        // Test for pack()
+        opt->pack(out_buf_);
+
+        // Data length is 20 bytes.
+        EXPECT_EQ(20, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(opt_code, opt->getType());
+
+        // Check if pack worked properly:
+        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+
+        if (u == Option::V4) {
+            // The total length is 20 bytes for data and 2 bytes for a header.
+            ASSERT_EQ(22, out_buf_.getLength());
+            // if option type is correct
+            EXPECT_EQ(opt_code, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(20, out.readUint8());
+        } else {
+            // The total length is 20 bytes for data and 4 bytes for a header.
+            ASSERT_EQ(24, out_buf_.getLength());
+            // if option type is correct
+            EXPECT_EQ(opt_code, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(20, out.readUint16());
+        }
+        // if data is correct
+        std::vector<uint8_t> out_data;
+        ASSERT_NO_THROW(out.readVector(out_data, opt_len));
+        ASSERT_EQ(opt_len, out_data.size());
+        EXPECT_TRUE(std::equal(buf_.begin(), buf_.begin() + opt_len, out_data.begin()));;
+    }
+
+    /// @brief Test parsing buffer into array of int32_t or uint32_t values.
+    ///
+    /// @warning this function does not perform type check. Make
+    /// sure that only int32_t or uint32_t type is used.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @tparam T int32_t or uint32_t.
+    template<typename T>
+    void bufferToIntTest32(const Option::Universe u) {
+        // Create option that conveys array of multiple uint16_t values.
+        boost::shared_ptr<OptionIntArray<T> > opt;
+        const int opt_len = 40;
+        const uint16_t opt_code = 82;
+
+        // Constructor throws exception if provided buffer is empty.
+        EXPECT_THROW(
+            OptionIntArray<T>(u, opt_code, buf_.begin(), buf_.begin()),
+            isc::OutOfRange
+        );
+
+        // Constructor throws exception if provided buffer's length is not
+        // multiple of 4-bytes.
+        EXPECT_THROW(
+            OptionIntArray<T>(u, opt_code, buf_.begin(), buf_.begin() + 9),
+            isc::OutOfRange
+        );
+
+        // Now the buffer length is correct.
+        ASSERT_NO_THROW(
+            opt = boost::shared_ptr<
+                OptionIntArray<T> >(new OptionIntArray<T>(u, opt_code, buf_.begin(),
+                                                          buf_.begin() + opt_len))
+        );
+
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(opt_code, opt->getType());
+        // Option should return vector of uint32_t values which should be
+        // constructed from the buffer we provided.
+        std::vector<T> values = opt->getValues();
+        ASSERT_EQ(opt_len, values.size() * sizeof(T));
+        // Create reference values from the buffer so as we can
+        // simply compare two vectors.
+        std::vector<T> reference_values;
+        for (int i = 0; i < opt_len; i += 4) {
+            reference_values.push_back((buf_[i] << 24) |
+                                       (buf_[i + 1] << 16 & 0x00FF0000) |
+                                       (buf_[i + 2] << 8 & 0xFF00) |
+                                       (buf_[i + 3] & 0xFF));
+        }
+        EXPECT_TRUE(std::equal(reference_values.begin(), reference_values.end(),
+                               values.begin()));
+
+        // Test for pack()
+        opt->pack(out_buf_);
+
+        // Data length is 40 bytes.
+        EXPECT_EQ(40, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(opt_code, opt->getType());
+
+        // Check if pack worked properly:
+        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+
+        if (u == Option::V4) {
+            // The total length is 40 bytes for data and 2 bytes for a header.
+            ASSERT_EQ(42, out_buf_.getLength());
+            // if option type is correct
+            EXPECT_EQ(opt_code, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(40, out.readUint8());
+        } else {
+            // The total length is 40 bytes for data and 4 bytes for a header.
+            ASSERT_EQ(44, out_buf_.getLength());
+            // if option type is correct
+            EXPECT_EQ(opt_code, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(40, out.readUint16());
+        }
+
+        // if data is correct
+        std::vector<uint8_t> out_data;
+        ASSERT_NO_THROW(out.readVector(out_data, opt_len));
+        ASSERT_EQ(opt_len, out_data.size());
+        EXPECT_TRUE(std::equal(buf_.begin(), buf_.begin() + opt_len, out_data.begin()));;
+    }
+
+
+    OptionBuffer buf_;     ///< Option buffer
+    OutputBuffer out_buf_; ///< Output buffer
+};
+
+/// @todo: below, there is a bunch of tests for options that
+/// convey unsigned values. We should maybe extend these tests for
+/// signed types too.
+
+TEST_F(OptionIntArrayTest, useInvalidType) {
+    const uint16_t opt_code = 80;
+    EXPECT_THROW(
+        boost::scoped_ptr<
+            OptionIntArray<bool> >(new OptionIntArray<bool>(Option::V6, opt_code,
+                                                            OptionBuffer(5))),
+        InvalidDataType
+    );
+
+    EXPECT_THROW(
+        boost::scoped_ptr<
+            OptionIntArray<int64_t> >(new OptionIntArray<int64_t>(Option::V6,
+                                                                  opt_code,
+                                                                  OptionBuffer(10))),
+        InvalidDataType
+    );
+
+}
+
+TEST_F(OptionIntArrayTest, bufferToUint8V4) {
+    bufferToIntTest8<uint8_t>(Option::V4);
+}
+
+TEST_F(OptionIntArrayTest, bufferToUint8V6) {
+    bufferToIntTest8<uint8_t>(Option::V6);
+}
+
+TEST_F(OptionIntArrayTest, bufferToInt8V4) {
+    bufferToIntTest8<int8_t>(Option::V4);
+}
+
+TEST_F(OptionIntArrayTest, bufferToInt8V6) {
+    bufferToIntTest8<int8_t>(Option::V6);
+}
+
+TEST_F(OptionIntArrayTest, bufferToUint16V4) {
+    bufferToIntTest16<uint16_t>(Option::V4);
+}
+
+TEST_F(OptionIntArrayTest, bufferToUint16V6) {
+    bufferToIntTest16<uint16_t>(Option::V6);
+}
+
+TEST_F(OptionIntArrayTest, bufferToInt16V4) {
+    bufferToIntTest16<int16_t>(Option::V4);
+}
+
+TEST_F(OptionIntArrayTest, bufferToInt16V6) {
+    bufferToIntTest16<int16_t>(Option::V6);
+}
+
+TEST_F(OptionIntArrayTest, bufferToUint32V4) {
+    bufferToIntTest32<uint32_t>(Option::V4);
+}
+
+TEST_F(OptionIntArrayTest, bufferToUint32V6) {
+    bufferToIntTest32<uint32_t>(Option::V6);
+}
+
+TEST_F(OptionIntArrayTest, bufferToInt32V4) {
+    bufferToIntTest32<int32_t>(Option::V4);
+}
+
+TEST_F(OptionIntArrayTest, bufferToInt32V6) {
+    bufferToIntTest32<int32_t>(Option::V6);
+}
+
+TEST_F(OptionIntArrayTest, setValuesUint8) {
+    const uint16_t opt_code = 100;
+    // Create option with empty vector of values.
+    boost::shared_ptr<OptionIntArray<uint8_t> >
+        opt(new OptionIntArray<uint8_t>(Option::V6, opt_code));
+    // Initialize vector with some data and pass to the option.
+    std::vector<uint8_t> values;
+    for (int i = 0; i < 10; ++i) {
+        values.push_back(numeric_limits<uint8_t>::max() - i);
+    }
+    opt->setValues(values);
+
+    // Check if universe, option type and data was set correctly.
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(opt_code, opt->getType());
+    std::vector<uint8_t> returned_values = opt->getValues();
+    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
+}
+
+TEST_F(OptionIntArrayTest, setValuesInt8) {
+    const uint16_t opt_code = 100;
+    // Create option with empty vector of values.
+    boost::shared_ptr<OptionIntArray<int8_t> >
+        opt(new OptionIntArray<int8_t>(Option::V6, opt_code));
+    // Initialize vector with some data and pass to the option.
+    std::vector<int8_t> values;
+    for (int i = 0; i < 10; ++i) {
+        values.push_back(numeric_limits<int8_t>::min() + i);
+    }
+    opt->setValues(values);
+
+    // Check if universe, option type and data was set correctly.
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(opt_code, opt->getType());
+    std::vector<int8_t> returned_values = opt->getValues();
+    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
+}
+
+TEST_F(OptionIntArrayTest, setValuesUint16) {
+    const uint16_t opt_code = 101;
+    // Create option with empty vector of values.
+    boost::shared_ptr<OptionIntArray<uint16_t> >
+        opt(new OptionIntArray<uint16_t>(Option::V6, opt_code));
+    // Initialize vector with some data and pass to the option.
+    std::vector<uint16_t> values;
+    for (int i = 0; i < 10; ++i) {
+        values.push_back(numeric_limits<uint16_t>::max() - i);
+    }
+    opt->setValues(values);
+
+    // Check if universe, option type and data was set correctly.
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(opt_code, opt->getType());
+    std::vector<uint16_t> returned_values = opt->getValues();
+    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
+}
+
+TEST_F(OptionIntArrayTest, setValuesInt16) {
+    const uint16_t opt_code = 101;
+    // Create option with empty vector of values.
+    boost::shared_ptr<OptionIntArray<int16_t> >
+        opt(new OptionIntArray<int16_t>(Option::V6, opt_code));
+    // Initialize vector with some data and pass to the option.
+    std::vector<int16_t> values;
+    for (int i = 0; i < 10; ++i) {
+        values.push_back(numeric_limits<int16_t>::min() + i);
+    }
+    opt->setValues(values);
+
+    // Check if universe, option type and data was set correctly.
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(opt_code, opt->getType());
+    std::vector<int16_t> returned_values = opt->getValues();
+    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
+}
+
+TEST_F(OptionIntArrayTest, setValuesUint32) {
+    const uint32_t opt_code = 101;
+    // Create option with empty vector of values.
+    boost::shared_ptr<OptionIntArray<uint32_t> >
+        opt(new OptionIntArray<uint32_t>(Option::V6, opt_code));
+    // Initialize vector with some data and pass to the option.
+    std::vector<uint32_t> values;
+    for (int i = 0; i < 10; ++i) {
+        values.push_back(numeric_limits<uint32_t>::max() - i);
+    }
+    opt->setValues(values);
+
+    // Check if universe, option type and data was set correctly.
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(opt_code, opt->getType());
+    std::vector<uint32_t> returned_values = opt->getValues();
+    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
+}
+
+TEST_F(OptionIntArrayTest, setValuesInt32) {
+    const uint32_t opt_code = 101;
+    // Create option with empty vector of values.
+    boost::shared_ptr<OptionIntArray<int32_t> >
+        opt(new OptionIntArray<int32_t>(Option::V6, opt_code));
+    // Initialize vector with some data and pass to the option.
+    std::vector<int32_t> values;
+    for (int i = 0; i < 10; ++i) {
+        values.push_back(numeric_limits<int32_t>::min() + i);
+    }
+    opt->setValues(values);
+
+    // Check if universe, option type and data was set correctly.
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(opt_code, opt->getType());
+    std::vector<int32_t> returned_values = opt->getValues();
+    EXPECT_TRUE(std::equal(values.begin(), values.end(), returned_values.begin()));
+}
+
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/option_int_unittest.cc b/src/lib/dhcp/tests/option_int_unittest.cc
new file mode 100644
index 0000000..81ebcf0
--- /dev/null
+++ b/src/lib/dhcp/tests/option_int_unittest.cc
@@ -0,0 +1,563 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option_int.h>
+#include <util/buffer.h>
+
+#include <boost/pointer_cast.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace {
+
+/// Option code being used in many test cases.
+const uint16_t TEST_OPT_CODE = 232;
+
+/// @brief OptionInt test class.
+class OptionIntTest : public ::testing::Test {
+public:
+    /// @brief Constructor.
+    ///
+    /// Initializes the option buffer with some data.
+    OptionIntTest(): buf_(255), out_buf_(255) {
+        for (int i = 0; i < 255; i++) {
+            buf_[i] = 255 - i;
+        }
+    }
+
+    /// @brief Basic test for int8 and uint8 types.
+    ///
+    /// @note this function does not perform type check. Make
+    /// sure that only int8_t or uint8_t type is used.
+    ///
+    /// @param u universe (V4 or V6).
+    /// @tparam T int8_t or uint8_t.
+    template<typename T>
+    void basicTest8(const Option::Universe u) {
+        // Create option that conveys single 8 bit integer value.
+        boost::shared_ptr<OptionInt<T> > opt;
+        // Initialize buffer with this value.
+        buf_[0] = 0xa1;
+        // Constructor may throw in case provided buffer is too short.
+        ASSERT_NO_THROW(
+            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+                                                                    TEST_OPT_CODE,
+                                                                    buf_.begin(),
+                                                                    buf_.begin() + 1))
+        );
+
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // Option should return the same value that we initialized the first
+        // byte of the buffer with.
+        EXPECT_EQ(static_cast<T>(0xa1), opt->getValue());
+
+        // test for pack()
+        opt->pack(out_buf_);
+
+        // Data length is 1 byte.
+        EXPECT_EQ(1, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // The total length is 1 byte for data and 2 bytes or 4 bytes
+        // for option code and option length.
+        if (u == Option::V4) {
+            EXPECT_EQ(3, out_buf_.getLength());
+        } else {
+            EXPECT_EQ(5, out_buf_.getLength());
+        }
+
+        // Check if pack worked properly:
+        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+        if (u == Option::V4) {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(1, out.readUint8());
+        } else {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(1, out.readUint16());
+        }
+        // if data is correct
+        EXPECT_EQ(0xa1, out.readUint8() );
+    }
+
+    /// @brief Basic test for int16 and uint16 types.
+    ///
+    /// @note this function does not perform type check. Make
+    /// sure that only int16_t or uint16_t type is used.
+    ///
+    /// @param u universe (V4 or V6)
+    /// @tparam T int16_t or uint16_t.
+    template<typename T>
+    void basicTest16(const Option::Universe u) {
+        // Create option that conveys single 16-bit integer value.
+        boost::shared_ptr<OptionInt<T> > opt;
+        // Initialize buffer with uint16_t value.
+        buf_[0] = 0xa1;
+        buf_[1] = 0xa2;
+        // Constructor may throw in case provided buffer is too short.
+        ASSERT_NO_THROW(
+            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+                                                                    TEST_OPT_CODE,
+                                                                    buf_.begin(),
+                                                                    buf_.begin() + 2))
+        );
+
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // Option should return the value equal to the contents of first
+        // and second byte of the buffer.
+        EXPECT_EQ(static_cast<T>(0xa1a2), opt->getValue());
+
+        // Test for pack()
+        opt->pack(out_buf_);
+
+        // Data length is 2 bytes.
+        EXPECT_EQ(2, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // The total length is 2 bytes for data and 2 or 4 bytes for aheader.
+        if (u == Option::V4) {
+            EXPECT_EQ(4, out_buf_.getLength());
+        } else {
+            EXPECT_EQ(6, out_buf_.getLength());
+        }
+
+        // Check if pack worked properly:
+        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+        if (u == Option::V4) {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(2, out.readUint8());
+        } else {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(2, out.readUint16());
+        }
+        // if data is correct
+        EXPECT_EQ(0xa1a2, out.readUint16() );
+    }
+
+    /// @brief Basic test for int32 and uint32 types.
+    ///
+    /// @note this function does not perform type check. Make
+    /// sure that only int32_t or uint32_t type is used.
+    ///
+    /// @param u universe (V4 or V6).
+    /// @tparam T int32_t or uint32_t.
+    template<typename T>
+    void basicTest32(const Option::Universe u) {
+        // Create option that conveys single 32-bit integer value.
+        boost::shared_ptr<OptionInt<T> > opt;
+        // Initialize buffer with 32-bit integer value.
+        buf_[0] = 0xa1;
+        buf_[1] = 0xa2;
+        buf_[2] = 0xa3;
+        buf_[3] = 0xa4;
+        // Constructor may throw in case provided buffer is too short.
+        ASSERT_NO_THROW(
+            opt = boost::shared_ptr<OptionInt<T> >(new OptionInt<T>(u,
+                                                                    TEST_OPT_CODE,
+                                                                    buf_.begin(),
+                                                                    buf_.begin() + 4))
+        );
+
+        EXPECT_EQ(u, opt->getUniverse());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // Option should return the value equal to the value made of
+        // first 4 bytes of the buffer.
+        EXPECT_EQ(static_cast<T>(0xa1a2a3a4), opt->getValue());
+
+        // Test for pack()
+        opt->pack(out_buf_);
+
+        // Data length is 4 bytes.
+        EXPECT_EQ(4, opt->len() - opt->getHeaderLen());
+        EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+        // The total length is 4 bytes for data and 2 or 4 bytes for a header.
+        if (u == Option::V4) {
+            EXPECT_EQ(6, out_buf_.getLength());
+        } else {
+            EXPECT_EQ(8, out_buf_.getLength());
+        }
+
+        // Check if pack worked properly:
+        InputBuffer out(out_buf_.getData(), out_buf_.getLength());
+        if (u == Option::V4) {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint8());
+            // if option length is correct
+            EXPECT_EQ(4, out.readUint8());
+        } else {
+            // if option type is correct
+            EXPECT_EQ(TEST_OPT_CODE, out.readUint16());
+            // if option length is correct
+            EXPECT_EQ(4, out.readUint16());
+        }
+        // if data is correct
+        EXPECT_EQ(0xa1a2a3a4, out.readUint32());
+    }
+
+    OptionBuffer buf_;     ///< Option buffer
+    OutputBuffer out_buf_; ///< Output buffer
+};
+
+/// @todo: below, there is a bunch of tests for options that
+/// convey unsigned value. We should maybe extend these tests for
+/// signed types too.
+
+TEST_F(OptionIntTest, useInvalidType) {
+    EXPECT_THROW(
+        boost::scoped_ptr<OptionInt<bool> >(new OptionInt<bool>(Option::V6,
+                                                                D6O_ELAPSED_TIME, 10)),
+        InvalidDataType
+    );
+
+    EXPECT_THROW(
+        boost::scoped_ptr<OptionInt<int64_t> >(new OptionInt<int64_t>(Option::V6,
+                                                                      D6O_ELAPSED_TIME, 10)),
+        InvalidDataType
+    );
+
+}
+
+TEST_F(OptionIntTest, basicUint8V4) {
+    basicTest8<uint8_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint8V6) {
+    basicTest8<uint8_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicUint16V4) {
+    basicTest16<uint16_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint16V6) {
+    basicTest16<uint16_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicUint32V4) {
+    basicTest32<uint32_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicUint32V6) {
+    basicTest32<uint32_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt8V4) {
+    basicTest8<int8_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt8V6) {
+    basicTest8<int8_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt16V4) {
+    basicTest16<int16_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt16V6) {
+    basicTest16<int16_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, basicInt32V4) {
+    basicTest32<int32_t>(Option::V4);
+}
+
+TEST_F(OptionIntTest, basicInt32V6) {
+    basicTest32<int32_t>(Option::V6);
+}
+
+TEST_F(OptionIntTest, setValueUint8) {
+    boost::shared_ptr<OptionInt<uint8_t> > opt(new OptionInt<uint8_t>(Option::V6,
+                                                                      D6O_PREFERENCE, 123));
+    // Check if constructor intitialized the option value correctly.
+    EXPECT_EQ(123, opt->getValue());
+    // Override the value.
+    opt->setValue(111);
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_PREFERENCE, opt->getType());
+    // Check if the value has been overriden.
+    EXPECT_EQ(111, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueInt8) {
+    boost::shared_ptr<OptionInt<int8_t> > opt(new OptionInt<int8_t>(Option::V6,
+                                                                    D6O_PREFERENCE, -123));
+    // Check if constructor intitialized the option value correctly.
+    EXPECT_EQ(-123, opt->getValue());
+    // Override the value.
+    opt->setValue(-111);
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_PREFERENCE, opt->getType());
+    // Check if the value has been overriden.
+    EXPECT_EQ(-111, opt->getValue());
+}
+
+
+TEST_F(OptionIntTest, setValueUint16) {
+    boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(Option::V6,
+                                                                        D6O_ELAPSED_TIME, 123));
+    // Check if constructor intitialized the option value correctly.
+    EXPECT_EQ(123, opt->getValue());
+    // Override the value.
+    opt->setValue(0x0102);
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
+    // Check if the value has been overriden.
+    EXPECT_EQ(0x0102, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueInt16) {
+    boost::shared_ptr<OptionInt<int16_t> > opt(new OptionInt<int16_t>(Option::V6,
+                                                                      D6O_ELAPSED_TIME, -16500));
+    // Check if constructor intitialized the option value correctly.
+    EXPECT_EQ(-16500, opt->getValue());
+    // Override the value.
+    opt->setValue(-20100);
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_ELAPSED_TIME, opt->getType());
+    // Check if the value has been overriden.
+    EXPECT_EQ(-20100, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueUint32) {
+    boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(Option::V6,
+                                                                        D6O_CLT_TIME, 123));
+    // Check if constructor intitialized the option value correctly.
+    EXPECT_EQ(123, opt->getValue());
+    // Override the value.
+    opt->setValue(0x01020304);
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_CLT_TIME, opt->getType());
+    // Check if the value has been overriden.
+    EXPECT_EQ(0x01020304, opt->getValue());
+}
+
+TEST_F(OptionIntTest, setValueInt32) {
+    boost::shared_ptr<OptionInt<int32_t> > opt(new OptionInt<int32_t>(Option::V6,
+                                                                      D6O_CLT_TIME, -120100));
+    // Check if constructor intitialized the option value correctly.
+    EXPECT_EQ(-120100, opt->getValue());
+    // Override the value.
+    opt->setValue(-125000);
+
+    EXPECT_EQ(Option::V6, opt->getUniverse());
+    EXPECT_EQ(D6O_CLT_TIME, opt->getType());
+    // Check if the value has been overriden.
+    EXPECT_EQ(-125000, opt->getValue());
+}
+
+TEST_F(OptionIntTest, packSuboptions4) {
+    boost::shared_ptr<OptionInt<uint16_t> > opt(new OptionInt<uint16_t>(Option::V4,
+                                                                        TEST_OPT_CODE,
+                                                                        0x0102));
+    // Add sub option with some 4 bytes of data (each byte set to 1)
+    OptionPtr sub1(new Option(Option::V4, TEST_OPT_CODE + 1, OptionBuffer(4, 1)));
+    // Add sub option with some 5 bytes of data (each byte set to 2)
+    OptionPtr sub2(new Option(Option::V4, TEST_OPT_CODE + 2, OptionBuffer(5, 2)));
+
+    // Add suboptions.
+    opt->addOption(sub1);
+    opt->addOption(sub2);
+
+    // Prepare reference data: option + suoptions in wire format.
+    uint8_t expected[] = {
+        TEST_OPT_CODE, 15, // option header
+        0x01, 0x02,        // data, uint16_t value = 0x0102
+        TEST_OPT_CODE + 1, 0x04, 0x01, 0x01, 0x01, 0x01, // sub1
+        TEST_OPT_CODE + 2, 0x05, 0x02, 0x02, 0x02, 0x02, 0x02 // sub2
+    };
+
+    // Create on-wire format of option and suboptions.
+    opt->pack(out_buf_);
+    // Compare the on-wire data with the reference buffer.
+    ASSERT_EQ(sizeof(expected), out_buf_.getLength());
+    EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, sizeof(expected)));
+}
+
+TEST_F(OptionIntTest, packSuboptions6) {
+    // option code is really uint16_t, but using uint8_t
+    // for easier conversion to uint8_t array.
+    uint8_t opt_code = 80;
+
+    boost::shared_ptr<OptionInt<uint32_t> > opt(new OptionInt<uint32_t>(Option::V6,
+                                                                        opt_code, 0x01020304));
+    OptionPtr sub1(new Option(Option::V6, 0xcafe));
+
+    boost::shared_ptr<Option6IAAddr> addr1(
+        new Option6IAAddr(D6O_IAADDR, IOAddress("2001:db8:1234:5678::abcd"), 0x5000, 0x7000));
+
+    opt->addOption(sub1);
+    opt->addOption(addr1);
+
+    ASSERT_EQ(28, addr1->len());
+    ASSERT_EQ(4, sub1->len());
+    ASSERT_EQ(40, opt->len());
+
+    uint8_t expected[] = {
+        0, opt_code, // type
+        0, 36, // length
+        0x01, 0x02, 0x03, 0x04, // uint32_t value
+
+        // iaaddr suboption
+        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
+        0, 0, 0x50, 0, // preferred-lifetime
+        0, 0, 0x70, 0, // valid-lifetime
+
+        // suboption
+        0xca, 0xfe, // type
+        0, 0 // len
+    };
+
+    // Create on-wire format of option and suboptions.
+    opt->pack(out_buf_);
+    // Compare the on-wire data with the reference buffer.
+    ASSERT_EQ(40, out_buf_.getLength());
+    EXPECT_EQ(0, memcmp(out_buf_.getData(), expected, 40));
+}
+
+TEST_F(OptionIntTest, unpackSuboptions4) {
+    // Prepare reference data.
+    const uint8_t expected[] = {
+        TEST_OPT_CODE, 0x0A, // option code and length
+        0x01, 0x02, 0x03, 0x04, // data, uint32_t value = 0x01020304
+        TEST_OPT_CODE + 1, 0x4, 0x01, 0x01, 0x01, 0x01 // suboption
+    };
+    // Make sure that the buffer size is sufficient to copy the
+    // elements from the array.
+    ASSERT_GE(buf_.size(), sizeof(expected));
+    // Copy the data to a vector so as we can pass it to the
+    // OptionInt's constructor.
+    memcpy(&buf_[0], expected, sizeof(expected));
+
+    // Create an option.
+    boost::shared_ptr<OptionInt<uint32_t> > opt;
+    EXPECT_NO_THROW(
+        opt = boost::shared_ptr<
+            OptionInt<uint32_t> >(new OptionInt<uint32_t>(Option::V4, TEST_OPT_CODE,
+                                                          buf_.begin() + 2,
+                                                          buf_.begin() + sizeof(expected)));
+    );
+    ASSERT_TRUE(opt);
+
+    // Verify that it has expected type and data.
+    EXPECT_EQ(TEST_OPT_CODE, opt->getType());
+    EXPECT_EQ(0x01020304, opt->getValue());
+
+    // Expect that there is the sub option with the particular
+    // option code added.
+    OptionPtr subopt = opt->getOption(TEST_OPT_CODE + 1);
+    ASSERT_TRUE(subopt);
+    // Check that this option has correct universe and code.
+    EXPECT_EQ(Option::V4, subopt->getUniverse());
+    EXPECT_EQ(TEST_OPT_CODE + 1, subopt->getType());
+    // Check the sub option's data.
+    OptionBuffer subopt_buf = subopt->getData();
+    ASSERT_EQ(4, subopt_buf.size());
+    // The data in the input buffer starts at offset 8.
+    EXPECT_TRUE(std::equal(subopt_buf.begin(), subopt_buf.end(), buf_.begin() + 8));
+}
+
+TEST_F(OptionIntTest, unpackSuboptions6) {
+    // option code is really uint16_t, but using uint8_t
+    // for easier conversion to uint8_t array.
+    const uint8_t opt_code = 80;
+    // Prepare reference data.
+    uint8_t expected[] = {
+        0, opt_code, // type
+        0, 34, // length
+        0x01, 0x02, // uint16_t value
+
+        // iaaddr suboption
+        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
+        0, 0, 0x50, 0, // preferred-lifetime
+        0, 0, 0x70, 0, // valid-lifetime
+
+        // suboption
+        0xca, 0xfe, // type
+        0, 0 // len
+    };
+    ASSERT_EQ(38, sizeof(expected));
+
+    // Make sure that the buffer's size is sufficient to
+    // copy the elements from the array.
+    ASSERT_GE(buf_.size(), sizeof(expected));
+    memcpy(&buf_[0], expected, sizeof(expected));
+
+    boost::shared_ptr<OptionInt<uint16_t> > opt;
+    EXPECT_NO_THROW(
+        opt = boost::shared_ptr<
+            OptionInt<uint16_t> >(new OptionInt<uint16_t>(Option::V6, opt_code,
+                                                          buf_.begin() + 4,
+                                                          buf_.begin() + sizeof(expected)));
+    );
+    ASSERT_TRUE(opt);
+
+    EXPECT_EQ(opt_code, opt->getType());
+    EXPECT_EQ(0x0102, opt->getValue());
+
+    // Checks for address option
+    OptionPtr subopt = opt->getOption(D6O_IAADDR);
+    ASSERT_TRUE(subopt);
+    boost::shared_ptr<Option6IAAddr> addr(boost::dynamic_pointer_cast<Option6IAAddr>(subopt));
+    ASSERT_TRUE(addr);
+
+    EXPECT_EQ(D6O_IAADDR, addr->getType());
+    EXPECT_EQ(28, addr->len());
+    EXPECT_EQ(0x5000, addr->getPreferred());
+    EXPECT_EQ(0x7000, addr->getValid());
+    EXPECT_EQ("2001:db8:1234:5678::abcd", addr->getAddress().toText());
+
+    // Checks for dummy option
+    subopt = opt->getOption(0xcafe);
+    ASSERT_TRUE(subopt); // should be non-NULL
+
+    EXPECT_EQ(0xcafe, subopt->getType());
+    EXPECT_EQ(4, subopt->len());
+    // There should be no data at all
+    EXPECT_EQ(0, subopt->getData().size());
+
+    // Try to get non-existent option.
+    subopt = opt->getOption(1);
+    // Expecting NULL which means that option does not exist.
+    ASSERT_FALSE(subopt);
+}
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index 2ef982a..bd848ff 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -462,13 +462,17 @@ TEST(Pkt4Test, file) {
     EXPECT_THROW(pkt4.setFile(NULL, 0), InvalidParameter);
 }
 
+/// V4 Options being used for pack/unpack testing.
+/// For test simplicity, all selected options have
+/// variable length data so as there are no restrictions
+/// on a length of their data.
 static uint8_t v4Opts[] = {
-    12,  3, 0,   1,  2,
-    13,  3, 10, 11, 12,
-    14,  3, 20, 21, 22,
-    53, 1, 1, // DHCP_MESSAGE_TYPE (required to not throw exception during unpack)
-    128, 3, 30, 31, 32,
-    254, 3, 40, 41, 42,
+    12,  3, 0,   1,  2, // Hostname
+    14,  3, 10, 11, 12, // Merit Dump File
+    53, 1, 1, // Message Type (required to not throw exception during unpack)
+    60,  3, 20, 21, 22, // Class Id
+    128, 3, 30, 31, 32, // Vendor specific
+    254, 3, 40, 41, 42, // Reserved
 };
 
 TEST(Pkt4Test, options) {
@@ -482,9 +486,9 @@ TEST(Pkt4Test, options) {
     }
 
     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> opt3(new Option(Option::V4, 14, payload[1]));
     boost::shared_ptr<Option> optMsgType(new Option(Option::V4, DHO_DHCP_MESSAGE_TYPE));
+    boost::shared_ptr<Option> opt2(new Option(Option::V4, 60, payload[2]));
     boost::shared_ptr<Option> opt5(new Option(Option::V4,128, payload[3]));
     boost::shared_ptr<Option> opt4(new Option(Option::V4,254, payload[4]));
     optMsgType->setUint8(static_cast<uint8_t>(DHCPDISCOVER));
@@ -497,7 +501,7 @@ TEST(Pkt4Test, options) {
     pkt->addOption(optMsgType);
 
     EXPECT_TRUE(pkt->getOption(12));
-    EXPECT_TRUE(pkt->getOption(13));
+    EXPECT_TRUE(pkt->getOption(60));
     EXPECT_TRUE(pkt->getOption(14));
     EXPECT_TRUE(pkt->getOption(128));
     EXPECT_TRUE(pkt->getOption(254));
@@ -554,7 +558,7 @@ TEST(Pkt4Test, unpackOptions) {
     );
 
     EXPECT_TRUE(pkt->getOption(12));
-    EXPECT_TRUE(pkt->getOption(13));
+    EXPECT_TRUE(pkt->getOption(60));
     EXPECT_TRUE(pkt->getOption(14));
     EXPECT_TRUE(pkt->getOption(128));
     EXPECT_TRUE(pkt->getOption(254));
@@ -566,19 +570,19 @@ TEST(Pkt4Test, unpackOptions) {
     EXPECT_EQ(5, x->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+2, 3)); // data len=3
 
-    x = pkt->getOption(13);
+    x = pkt->getOption(14);
     ASSERT_TRUE(x); // option 13 should exist
-    EXPECT_EQ(13, x->getType());  // this should be option 13
+    EXPECT_EQ(14, x->getType());  // this should be option 13
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->len()); // total option length 5
     EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+7, 3)); // data len=3
 
-    x = pkt->getOption(14);
-    ASSERT_TRUE(x); // option 14 should exist
-    EXPECT_EQ(14, x->getType());  // this should be option 14
+    x = pkt->getOption(60);
+    ASSERT_TRUE(x); // option 60 should exist
+    EXPECT_EQ(60, x->getType());  // this should be option 60
     ASSERT_EQ(3, x->getData().size()); // it should be of length 3
     EXPECT_EQ(5, x->len()); // total option length 5
-    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+12, 3)); // data len=3
+    EXPECT_EQ(0, memcmp(&x->getData()[0], v4Opts+15, 3)); // data len=3
 
     x = pkt->getOption(128);
     ASSERT_TRUE(x); // option 3 should exist
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index d2e604a..d535582 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -52,7 +52,7 @@ libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
-libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 2:0:0
+libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 3:0:0
 if HAVE_MYSQL
 libb10_dhcpsrv_la_LDFLAGS += $(MYSQL_LIBS)
 endif
diff --git a/src/lib/dhcpsrv/addr_utilities.cc b/src/lib/dhcpsrv/addr_utilities.cc
index a2d9960..98fbeb9 100644
--- a/src/lib/dhcpsrv/addr_utilities.cc
+++ b/src/lib/dhcpsrv/addr_utilities.cc
@@ -53,8 +53,11 @@ isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& pref
     }
 
     // First we copy the whole address as 16 bytes.
+    // We don't check that it is a valid IPv6 address and thus has
+    // the required length because it is already checked by
+    // the calling function.
     uint8_t packed[V6ADDRESS_LEN];
-    memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
+    memcpy(packed, &prefix.toBytes()[0], V6ADDRESS_LEN);
 
     // If the length is divisible by 8, it is simple. We just zero out the host
     // part. Otherwise we need to handle the byte that has to be partially
@@ -95,6 +98,9 @@ isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& pref
         isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
     }
 
+    // We don't check that it is a valid IPv4 address and thus has
+    // a required length of 4 bytes because it has been already
+    // checked by the calling function.
     uint32_t addr = prefix;
     return (IOAddress(addr & (~bitMask4[len])));
 }
@@ -132,7 +138,7 @@ isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefi
 
     // First we copy the whole address as 16 bytes.
     uint8_t packed[V6ADDRESS_LEN];
-    memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
+    memcpy(packed, &prefix.toBytes()[0], 16);
 
     // if the length is divisible by 8, it is simple. We just fill the host part
     // with ones. Otherwise we need to handle the byte that has to be partially
@@ -168,20 +174,24 @@ namespace isc {
 namespace dhcp {
 
 isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
-                                            uint8_t len) {
-    if (prefix.getFamily() == AF_INET) {
-        return firstAddrInPrefix4(prefix, len);
+                                           uint8_t len) {
+    if (prefix.isV4()) {
+        return (firstAddrInPrefix4(prefix, len));
+
     } else {
-        return firstAddrInPrefix6(prefix, len);
+        return (firstAddrInPrefix6(prefix, len));
+
     }
 }
 
 isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
                                            uint8_t len) {
-    if (prefix.getFamily() == AF_INET) {
-        return lastAddrInPrefix4(prefix, len);
+    if (prefix.isV4()) {
+        return (lastAddrInPrefix4(prefix, len));
+
     } else {
-        return lastAddrInPrefix6(prefix, len);
+        return (lastAddrInPrefix6(prefix, len));
+
     }
 }
 
diff --git a/src/lib/dhcpsrv/alloc_engine.cc b/src/lib/dhcpsrv/alloc_engine.cc
index 77a2df4..1c64c04 100644
--- a/src/lib/dhcpsrv/alloc_engine.cc
+++ b/src/lib/dhcpsrv/alloc_engine.cc
@@ -30,22 +30,24 @@ AllocEngine::IterativeAllocator::IterativeAllocator()
 
 isc::asiolink::IOAddress
 AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) {
+    // Get a buffer holding an address.
+    const std::vector<uint8_t>& vec = addr.toBytes();
+    // Get the address length.
+    const int len = vec.size();
+
+    // Since the same array will be used to hold the IPv4 and IPv6
+    // address we have to make sure that the size of the array
+    // we allocate will work for both types of address.
+    BOOST_STATIC_ASSERT(V4ADDRESS_LEN <= V6ADDRESS_LEN);
     uint8_t packed[V6ADDRESS_LEN];
-    int len;
 
-    // First we copy the whole address as 16 bytes.
-    if (addr.getFamily()==AF_INET) {
-        // IPv4
-        std::memcpy(packed, addr.getAddress().to_v4().to_bytes().data(), 4);
-        len = 4;
-    } else {
-        // IPv6
-        std::memcpy(packed, addr.getAddress().to_v6().to_bytes().data(), 16);
-        len = 16;
-    }
+    // Copy the address. It can be either V4 or V6.
+    std::memcpy(packed, &vec[0], len);
 
+    // Start increasing the least significant byte
     for (int i = len - 1; i >= 0; --i) {
         ++packed[i];
+        // if we haven't overflowed (0xff -> 0x0), than we are done
         if (packed[i] != 0) {
             break;
         }
@@ -197,9 +199,31 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             if (lease) {
                 return (lease);
             }
+        } else {
+            if (existing->expired()) {
+                return (reuseExpiredLease(existing, subnet, duid, iaid,
+                                          fake_allocation));
+            }
+
         }
     }
 
+    // Hint is in the pool but is not available. Search the pool until first of
+    // the following occurs:
+    // - we find a free address
+    // - we find an address for which the lease has expired
+    // - we exhaust number of tries
+    //
+    // @todo: Current code does not handle pool exhaustion well. It will be
+    // improved. Current problems:
+    // 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
+    // 10 addresses), we will iterate over it 100 times before giving up
+    // 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
+    // 3. the whole concept of infinite attempts is just asking for infinite loop
+    // We may consider some form or reference counting (this pool has X addresses
+    // left), but this has one major problem. We exactly control allocation
+    // moment, but we currently do not control expiration time at all
+
     unsigned int i = attempts_;
     do {
         IOAddress candidate = allocator_->pickAddress(subnet, duid, hint);
@@ -208,9 +232,9 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
         /// implemented
 
         Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(candidate);
-        // there's no existing lease for selected candidate, so it is
-        // free. Let's allocate it.
         if (!existing) {
+            // there's no existing lease for selected candidate, so it is
+            // free. Let's allocate it.
             Lease6Ptr lease = createLease(subnet, duid, iaid, candidate,
                                           fake_allocation);
             if (lease) {
@@ -220,6 +244,11 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
             // Although the address was free just microseconds ago, it may have
             // been taken just now. If the lease insertion fails, we continue
             // allocation attempts.
+        } else {
+            if (existing->expired()) {
+                return (reuseExpiredLease(existing, subnet, duid, iaid,
+                                          fake_allocation));
+            }
         }
 
         // continue trying allocation until we run out of attempts
@@ -231,6 +260,46 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
               << " tries");
 }
 
+Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
+                                         const Subnet6Ptr& subnet,
+                                         const DuidPtr& duid,
+                                         uint32_t iaid,
+                                         bool fake_allocation /*= false */ ) {
+
+    if (!expired->expired()) {
+        isc_throw(BadValue, "Attempt to recycle lease that is still valid");
+    }
+
+    // address, lease type and prefixlen (0) stay the same
+    expired->iaid_ = iaid;
+    expired->duid_ = duid;
+    expired->preferred_lft_ = subnet->getPreferred();
+    expired->valid_lft_ = subnet->getValid();
+    expired->t1_ = subnet->getT1();
+    expired->t2_ = subnet->getT2();
+    expired->cltt_ = time(NULL);
+    expired->subnet_id_ = subnet->getID();
+    expired->fixed_ = false;
+    expired->hostname_ = std::string("");
+    expired->fqdn_fwd_ = false;
+    expired->fqdn_rev_ = false;
+
+    /// @todo: log here that the lease was reused (there's ticket #2524 for
+    /// logging in libdhcpsrv)
+
+    if (!fake_allocation) {
+        // for REQUEST we do update the lease
+        LeaseMgrFactory::instance().updateLease6(expired);
+    }
+
+    // We do nothing for SOLICIT. We'll just update database when
+    // the client gets back to us with REQUEST message.
+
+    // it's not really expired at this stage anymore - let's return it as
+    // an updated lease
+    return (expired);
+}
+
 Lease6Ptr AllocEngine::createLease(const Subnet6Ptr& subnet,
                                    const DuidPtr& duid,
                                    uint32_t iaid,
diff --git a/src/lib/dhcpsrv/alloc_engine.h b/src/lib/dhcpsrv/alloc_engine.h
index 940b1f4..eab6e12 100644
--- a/src/lib/dhcpsrv/alloc_engine.h
+++ b/src/lib/dhcpsrv/alloc_engine.h
@@ -216,6 +216,24 @@ private:
                           uint32_t iaid, const isc::asiolink::IOAddress& addr,
                           bool fake_allocation = false);
 
+    /// @brief reuses expired lease
+    ///
+    /// Updates existing expired lease with new information. Lease database
+    /// is updated if this is real (i.e. REQUEST, fake_allocation = false), not
+    /// dummy allocation request (i.e. SOLICIT, fake_allocation = true).
+    ///
+    /// @param expired old, expired lease
+    /// @param subnet subnet the lease is allocated from
+    /// @param duid client's DUID
+    /// @param iaid IAID from the IA_NA container the client sent to us
+    /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
+    ///        an address for SOLICIT that is not really allocated (true)
+    /// @return refreshed lease
+    /// @throw BadValue if trying to recycle lease that is still valid
+    Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
+                                const DuidPtr& duid, uint32_t iaid,
+                                bool fake_allocation = false);
+
     /// @brief a pointer to currently used allocator
     boost::shared_ptr<Allocator> allocator_;
 
diff --git a/src/lib/dhcpsrv/lease_mgr.cc b/src/lib/dhcpsrv/lease_mgr.cc
index 74e49e8..996fd58 100644
--- a/src/lib/dhcpsrv/lease_mgr.cc
+++ b/src/lib/dhcpsrv/lease_mgr.cc
@@ -46,6 +46,14 @@ Lease6::Lease6(LeaseType type, const isc::asiolink::IOAddress& addr,
     cltt_ = time(NULL);
 }
 
+bool Lease6::expired() const {
+
+    // Let's use int64 to avoid problems with negative/large uint32 values
+    int64_t expire_time = cltt_ + valid_lft_;
+    return (expire_time < time(NULL));
+}
+
+
 std::string LeaseMgr::getParameter(const std::string& name) const {
     ParameterMap::const_iterator param = parameters_.find(name);
     if (param == parameters_.end()) {
diff --git a/src/lib/dhcpsrv/lease_mgr.h b/src/lib/dhcpsrv/lease_mgr.h
index 427e315..375a586 100644
--- a/src/lib/dhcpsrv/lease_mgr.h
+++ b/src/lib/dhcpsrv/lease_mgr.h
@@ -384,6 +384,10 @@ struct Lease6 {
     /// @return String form of the lease
     std::string toText() const;
 
+    /// @brief returns true if the lease is expired
+    /// @return true if the lease is expired
+    bool expired() const;
+
     /// @brief Compare two leases for equality
     ///
     /// @param other lease6 object with which to compare
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc
index d13b776..6b2c847 100644
--- a/src/lib/dhcpsrv/memfile_lease_mgr.cc
+++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc
@@ -21,9 +21,8 @@ using namespace isc::dhcp;
 
 Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
     : LeaseMgr(parameters) {
-    std::cout << "Warning: Using memfile database backend. It is usable for" << std::endl;
-    std::cout << "Warning: limited testing only. File support not implemented yet." << std::endl;
-    std::cout << "Warning: Leases will be lost after restart." << std::endl;
+    std::cout << "Warning: Using memfile database backend. It is usable for limited"
+              << " testing only. Leases will be lost after restart." << std::endl;
 }
 
 Memfile_LeaseMgr::~Memfile_LeaseMgr() {
diff --git a/src/lib/dhcpsrv/pool.cc b/src/lib/dhcpsrv/pool.cc
index 9c91acb..3548762 100644
--- a/src/lib/dhcpsrv/pool.cc
+++ b/src/lib/dhcpsrv/pool.cc
@@ -34,7 +34,7 @@ Pool4::Pool4(const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
     :Pool(first, last) {
     // check if specified address boundaries are sane
-    if (first.getFamily() != AF_INET || last.getFamily() != AF_INET) {
+    if (!first.isV4() || !last.isV4()) {
         isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
     }
 
@@ -48,7 +48,7 @@ Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
     :Pool(prefix, IOAddress("0.0.0.0")) {
 
     // check if the prefix is sane
-    if (prefix.getFamily() != AF_INET) {
+    if (!prefix.isV4()) {
         isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
     }
 
@@ -67,7 +67,7 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
     :Pool(first, last), type_(type), prefix_len_(0) {
 
     // check if specified address boundaries are sane
-    if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
+    if (!first.isV6() || !last.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
@@ -98,7 +98,7 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
      type_(type), prefix_len_(prefix_len) {
 
     // check if the prefix is sane
-    if (prefix.getFamily() != AF_INET6) {
+    if (!prefix.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc
index 78da250..3f8733f 100644
--- a/src/lib/dhcpsrv/subnet.cc
+++ b/src/lib/dhcpsrv/subnet.cc
@@ -30,8 +30,8 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
     :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
      t2_(t2), valid_(valid_lifetime),
      last_allocated_(lastAddrInPrefix(prefix, len)) {
-    if ( (prefix.getFamily() == AF_INET6 && len > 128) ||
-         (prefix.getFamily() == AF_INET && len > 32) ) {
+    if ((prefix.isV6() && len > 128) ||
+        (prefix.isV4() && len > 32)) {
         isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
     }
 }
@@ -65,7 +65,7 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& t2,
                  const Triplet<uint32_t>& valid_lifetime)
     :Subnet(prefix, length, t1, t2, valid_lifetime) {
-    if (prefix.getFamily() != AF_INET) {
+    if (!prefix.isV4()) {
         isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
                   << " specified in subnet4");
     }
@@ -136,7 +136,7 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& valid_lifetime)
     :Subnet(prefix, length, t1, t2, valid_lifetime),
      preferred_(preferred_lifetime){
-    if (prefix.getFamily() != AF_INET6) {
+    if (!prefix.isV6()) {
         isc_throw(BadValue, "Non IPv6 prefix " << prefix.toText()
                   << " specified in subnet6");
     }
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index 6be863e..7f8d634 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -41,6 +41,7 @@ libdhcpsrv_unittests_SOURCES += pool_unittest.cc
 libdhcpsrv_unittests_SOURCES += schema_copy.h
 libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
 libdhcpsrv_unittests_SOURCES += triplet_unittest.cc
+libdhcpsrv_unittests_SOURCES += test_utils.cc test_utils.h
 
 libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 if HAVE_MYSQL
diff --git a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
index dcfb68b..95eabb2 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
@@ -22,6 +22,8 @@
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/memfile_lease_mgr.h>
 
+#include <dhcpsrv/tests/test_utils.h>
+
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
@@ -29,11 +31,13 @@
 #include <iostream>
 #include <sstream>
 #include <map>
+#include <time.h>
 
 using namespace std;
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 
 namespace {
 
@@ -107,26 +111,6 @@ TEST_F(AllocEngineTest, constructor) {
     ASSERT_NO_THROW(x.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
 }
 
-/// @todo: This method is taken from mysql_lease_mgr_utilities.cc from ticket
-/// #2342. Get rid of one instance once the code is merged
-void
-detailCompareLease6(const Lease6Ptr& first, const Lease6Ptr& second) {
-    EXPECT_EQ(first->type_, second->type_);
-
-    // Compare address strings - odd things happen when they are different
-    // as the EXPECT_EQ appears to call the operator uint32_t() function,
-    // which causes an exception to be thrown for IPv6 addresses.
-    EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
-    EXPECT_EQ(first->prefixlen_, second->prefixlen_);
-    EXPECT_EQ(first->iaid_, second->iaid_);
-    EXPECT_TRUE(*first->duid_ == *second->duid_);
-    EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
-    EXPECT_EQ(first->valid_lft_, second->valid_lft_);
-    EXPECT_EQ(first->cltt_, second->cltt_);
-    EXPECT_EQ(first->subnet_id_, second->subnet_id_);
-}
-
-
 // This test checks if the simple allocation can succeed
 TEST_F(AllocEngineTest, simpleAlloc) {
     boost::scoped_ptr<AllocEngine> engine;
@@ -147,7 +131,7 @@ TEST_F(AllocEngineTest, simpleAlloc) {
     ASSERT_TRUE(from_mgr);
 
     // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease6(lease, from_mgr);
+    detailCompareLease(lease, from_mgr);
 }
 
 // This test checks if the fake allocation (for SOLICIT) can succeed
@@ -195,7 +179,7 @@ TEST_F(AllocEngineTest, allocWithValidHint) {
     ASSERT_TRUE(from_mgr);
 
     // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease6(lease, from_mgr);
+    detailCompareLease(lease, from_mgr);
 }
 
 // This test checks if the allocation with a hint that is in range,
@@ -234,7 +218,7 @@ TEST_F(AllocEngineTest, allocWithUsedHint) {
     ASSERT_TRUE(from_mgr);
 
     // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease6(lease, from_mgr);
+    detailCompareLease(lease, from_mgr);
 }
 
 // This test checks if the allocation with a hint that is out the blue
@@ -264,7 +248,7 @@ TEST_F(AllocEngineTest, allocBogusHint) {
     ASSERT_TRUE(from_mgr);
 
     // Now check that the lease in LeaseMgr has the same parameters
-    detailCompareLease6(lease, from_mgr);
+    detailCompareLease(lease, from_mgr);
 }
 
 // This test verifies that the allocator picks addresses that belong to the
@@ -337,4 +321,156 @@ TEST_F(AllocEngineTest, IterativeAllocator_manyPools) {
     delete alloc;
 }
 
+// This test checks if really small pools are working
+TEST_F(AllocEngineTest, smallPool) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    IOAddress addr("2001:db8:1::ad");
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
+
+    // Create configuration similar to other tests, but with a single address pool
+    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    subnet_->addPool6(pool_);
+    cfg_mgr.addSubnet6(subnet_);
+
+    Lease6Ptr lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                               false);
+
+    // Check that we got that single lease
+    ASSERT_TRUE(lease);
+
+    EXPECT_EQ("2001:db8:1::ad", lease->addr_.toText());
+
+    // do all checks on the lease
+    checkLease6(lease);
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    detailCompareLease(lease, from_mgr);
+}
+
+// This test checks if all addresses in a pool are currently used, the attempt
+// to find out a new lease fails.
+TEST_F(AllocEngineTest, outOfAddresses) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    IOAddress addr("2001:db8:1::ad");
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
+
+    // Create configuration similar to other tests, but with a single address pool
+    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    subnet_->addPool6(pool_);
+    cfg_mgr.addSubnet6(subnet_);
+
+    // Just a different duid
+    DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
+    const uint32_t other_iaid = 3568;
+    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, other_duid, other_iaid,
+                               501, 502, 503, 504, subnet_->getID(), 0));
+    lease->cltt_ = time(NULL) - 10; // Allocated 10 seconds ago
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // There is just a single address in the pool and allocated it to someone
+    // else, so the allocation should fail
+
+    EXPECT_THROW(engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),false),
+                 AllocFailed);
+}
+
+// This test checks if an expired lease can be reused in SOLICIT (fake allocation)
+TEST_F(AllocEngineTest, solicitReuseExpiredLease) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    IOAddress addr("2001:db8:1::ad");
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
+
+    // Create configuration similar to other tests, but with a single address pool
+    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    subnet_->addPool6(pool_);
+    cfg_mgr.addSubnet6(subnet_);
+
+    // Just a different duid
+    DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
+    const uint32_t other_iaid = 3568;
+    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, other_duid, other_iaid,
+                               501, 502, 503, 504, subnet_->getID(), 0));
+    lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
+    lease->valid_lft_ = 495; // Lease was valid for 495 seconds
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // CASE 1: Asking for any address
+    lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
+                                     true);
+    // Check that we got that single lease
+    ASSERT_TRUE(lease);
+    EXPECT_EQ(addr.toText(), lease->addr_.toText());
+
+    // Do all checks on the lease (if subnet-id, preferred/valid times are ok etc.)
+    checkLease6(lease);
+
+    // CASE 2: Asking specifically for this address
+    lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress(addr.toText()),
+                                     true);
+    // Check that we got that single lease
+    ASSERT_TRUE(lease);
+    EXPECT_EQ(addr.toText(), lease->addr_.toText());
+}
+
+// This test checks if an expired lease can be reused in REQUEST (actual allocation)
+TEST_F(AllocEngineTest, requestReuseExpiredLease) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    IOAddress addr("2001:db8:1::ad");
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
+
+    // Create configuration similar to other tests, but with a single address pool
+    subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+    pool_ = Pool6Ptr(new Pool6(Pool6::TYPE_IA, addr, addr)); // just a single address
+    subnet_->addPool6(pool_);
+    cfg_mgr.addSubnet6(subnet_);
+
+    // Let's create an expired lease
+    DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
+    const uint32_t other_iaid = 3568;
+    const SubnetID other_subnetid = 999;
+    Lease6Ptr lease(new Lease6(Lease6::LEASE_IA_NA, addr, other_duid, other_iaid,
+                               501, 502, 503, 504, other_subnetid, 0));
+    lease->cltt_ = time(NULL) - 500; // Allocated 500 seconds ago
+    lease->valid_lft_ = 495; // Lease was valid for 495 seconds
+    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+    // A client comes along, asking specifically for this address
+    lease = engine->allocateAddress6(subnet_, duid_, iaid_,
+                                     IOAddress(addr.toText()), false);
+
+    // Check that he got that single lease
+    ASSERT_TRUE(lease);
+    EXPECT_EQ(addr.toText(), lease->addr_.toText());
+
+    // Check that the lease is indeed updated in LeaseMgr
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(addr);
+    ASSERT_TRUE(from_mgr);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    detailCompareLease(lease, from_mgr);
+}
+
 }; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
index a812811..38c9555 100644
--- a/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/lease_mgr_unittest.cc
@@ -258,7 +258,7 @@ TEST(Lease4, Lease4Constructor) {
     // ...and a time
     const time_t current_time = time(NULL);
 
-    // Other random constants. 
+    // Other random constants.
     const uint32_t SUBNET_ID = 42;
     const uint32_t VALID_LIFETIME = 500;
 
@@ -605,4 +605,29 @@ TEST(Lease6, OperatorEquals) {
     EXPECT_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 }
+
+// Checks if lease expiration is calculated properly
+TEST(Lease6, Lease6Expired) {
+    const IOAddress addr("2001:db8:1::456");
+    const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
+    const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
+    const uint32_t iaid = 7; // just a number
+    const SubnetID subnet_id = 8; // just another number
+    Lease6 lease(Lease6::LEASE_IA_NA, addr, duid, iaid, 100, 200, 50, 80,
+                               subnet_id);
+
+    // case 1: a second before expiration
+    lease.cltt_ = time(NULL) - 100;
+    lease.valid_lft_ = 101;
+    EXPECT_FALSE(lease.expired());
+
+    // case 2: the lease will expire after this second is concluded
+    lease.cltt_ = time(NULL) - 101;
+    EXPECT_FALSE(lease.expired());
+
+    // case 3: the lease is expired
+    lease.cltt_ = time(NULL) - 102;
+    EXPECT_TRUE(lease.expired());
+}
+
 }; // end of anonymous namespace
diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
index 746ef00..89cacef 100644
--- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
+++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc
@@ -17,6 +17,7 @@
 #include <asiolink/io_address.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/mysql_lease_mgr.h>
+#include <dhcpsrv/tests/test_utils.h>
 
 #include <gtest/gtest.h>
 
@@ -29,6 +30,7 @@
 using namespace isc;
 using namespace isc::asiolink;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace std;
 
 namespace {
@@ -537,48 +539,6 @@ public:
     vector<IOAddress> ioaddress6_;  ///< IOAddress forms of IPv6 addresses
 };
 
-///@{
-/// @brief Test Utilities
-///
-/// The follow are a set of functions used during the tests.
-
-/// @brief Compare two Lease4 structures for equality
-void
-detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) {
-    // Compare address strings.  Comparison of address objects is not used, as
-    // odd things happen when they are different: the EXPECT_EQ macro appears to
-    // call the operator uint32_t() function, which causes an exception to be
-    // thrown for IPv6 addresses.
-    EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
-    EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
-    EXPECT_TRUE(*first->client_id_ == *second->client_id_);
-    EXPECT_EQ(first->valid_lft_, second->valid_lft_);
-    EXPECT_EQ(first->cltt_, second->cltt_);
-    EXPECT_EQ(first->subnet_id_, second->subnet_id_);
-}
-
-/// @brief Compare two Lease6 structures for equality
-void
-detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) {
-    EXPECT_EQ(first->type_, second->type_);
-
-    // Compare address strings.  Comparison of address objects is not used, as
-    // odd things happen when they are different: the EXPECT_EQ macro appears to
-    // call the operator uint32_t() function, which causes an exception to be
-    // thrown for IPv6 addresses.
-    EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
-    EXPECT_EQ(first->prefixlen_, second->prefixlen_);
-    EXPECT_EQ(first->iaid_, second->iaid_);
-    EXPECT_TRUE(*first->duid_ == *second->duid_);
-    EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
-    EXPECT_EQ(first->valid_lft_, second->valid_lft_);
-    EXPECT_EQ(first->cltt_, second->cltt_);
-    EXPECT_EQ(first->subnet_id_, second->subnet_id_);
-}
-
-///@}
-
-
 /// @brief Check that database can be opened
 ///
 /// This test checks if the MySqlLeaseMgr can be instantiated.  This happens
diff --git a/src/lib/dhcpsrv/tests/test_utils.cc b/src/lib/dhcpsrv/tests/test_utils.cc
new file mode 100644
index 0000000..3c69dbe
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/test_utils.cc
@@ -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.
+
+#include "test_utils.h"
+#include <gtest/gtest.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+void
+detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second) {
+    // Compare address strings.  Comparison of address objects is not used, as
+    // odd things happen when they are different: the EXPECT_EQ macro appears to
+    // call the operator uint32_t() function, which causes an exception to be
+    // thrown for IPv6 addresses.
+    EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
+    EXPECT_TRUE(first->hwaddr_ == second->hwaddr_);
+    EXPECT_TRUE(*first->client_id_ == *second->client_id_);
+    EXPECT_EQ(first->valid_lft_, second->valid_lft_);
+    EXPECT_EQ(first->cltt_, second->cltt_);
+    EXPECT_EQ(first->subnet_id_, second->subnet_id_);
+}
+
+void
+detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second) {
+    EXPECT_EQ(first->type_, second->type_);
+
+    // Compare address strings.  Comparison of address objects is not used, as
+    // odd things happen when they are different: the EXPECT_EQ macro appears to
+    // call the operator uint32_t() function, which causes an exception to be
+    // thrown for IPv6 addresses.
+    EXPECT_EQ(first->addr_.toText(), second->addr_.toText());
+    EXPECT_EQ(first->prefixlen_, second->prefixlen_);
+    EXPECT_EQ(first->iaid_, second->iaid_);
+    ASSERT_TRUE(first->duid_);
+    ASSERT_TRUE(second->duid_);
+    EXPECT_TRUE(*first->duid_ == *second->duid_);
+    EXPECT_EQ(first->preferred_lft_, second->preferred_lft_);
+    EXPECT_EQ(first->valid_lft_, second->valid_lft_);
+    EXPECT_EQ(first->cltt_, second->cltt_);
+    EXPECT_EQ(first->subnet_id_, second->subnet_id_);
+}
+
+};
+};
+};
diff --git a/src/lib/dhcpsrv/tests/test_utils.h b/src/lib/dhcpsrv/tests/test_utils.h
new file mode 100644
index 0000000..46df9fc
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/test_utils.h
@@ -0,0 +1,49 @@
+// 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 LIBDHCPSRV_TEST_UTILS_H
+#define LIBDHCPSRV_TEST_UTILS_H
+
+#include <dhcpsrv/lease_mgr.h>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+// @brief performs details comparison between two IPv6 leases
+//
+// @param first first lease to compare
+// @param second second lease to compare
+//
+// This method is intended to be run from gtest tests as it
+// uses gtest macros and possibly reports gtest failures.
+void
+detailCompareLease(const Lease6Ptr& first, const Lease6Ptr& second);
+
+// @brief performs details comparison between two IPv4 leases
+//
+// @param first first lease to compare
+// @param second second lease to compare
+//
+// This method is intended to be run from gtest tests as it
+// uses gtest macros and possibly reports gtest failures.
+void
+detailCompareLease(const Lease4Ptr& first, const Lease4Ptr& second);
+
+
+};
+};
+};
+
+#endif
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index 7778b58..8525842 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -117,6 +117,7 @@ libb10_dns___la_SOURCES += rrparamregistry.h
 libb10_dns___la_SOURCES += rrset.h rrset.cc
 libb10_dns___la_SOURCES += rrttl.h rrttl.cc
 libb10_dns___la_SOURCES += rrtype.cc
+libb10_dns___la_SOURCES += rrcollator.h rrcollator.cc
 libb10_dns___la_SOURCES += question.h question.cc
 libb10_dns___la_SOURCES += serial.h serial.cc
 libb10_dns___la_SOURCES += tsig.h tsig.cc
@@ -124,7 +125,7 @@ libb10_dns___la_SOURCES += tsigerror.h tsigerror.cc
 libb10_dns___la_SOURCES += tsigkey.h tsigkey.cc
 libb10_dns___la_SOURCES += tsigrecord.h tsigrecord.cc
 libb10_dns___la_SOURCES += character_string.h character_string.cc
-libb10_dns___la_SOURCES += master_loader_callbacks.h
+libb10_dns___la_SOURCES += master_loader_callbacks.h master_loader_callbacks.cc
 libb10_dns___la_SOURCES += master_loader.h
 libb10_dns___la_SOURCES += rdata/generic/detail/char_string.h
 libb10_dns___la_SOURCES += rdata/generic/detail/char_string.cc
diff --git a/src/lib/dns/master_lexer.cc b/src/lib/dns/master_lexer.cc
index e6e2c78..e4ddedc 100644
--- a/src/lib/dns/master_lexer.cc
+++ b/src/lib/dns/master_lexer.cc
@@ -37,7 +37,7 @@ using namespace master_lexer_internal;
 
 struct MasterLexer::MasterLexerImpl {
     MasterLexerImpl() : source_(NULL), token_(MasterToken::NOT_STARTED),
-                        paren_count_(0), last_was_eol_(false),
+                        paren_count_(0), last_was_eol_(true),
                         has_previous_(false),
                         previous_paren_count_(0),
                         previous_was_eol_(false)
@@ -127,6 +127,7 @@ MasterLexer::pushSource(const char* filename, std::string* error) {
 
     impl_->source_ = impl_->sources_.back().get();
     impl_->has_previous_ = false;
+    impl_->last_was_eol_ = true;
     return (true);
 }
 
@@ -135,6 +136,7 @@ MasterLexer::pushSource(std::istream& input) {
     impl_->sources_.push_back(InputSourcePtr(new InputSource(input)));
     impl_->source_ = impl_->sources_.back().get();
     impl_->has_previous_ = false;
+    impl_->last_was_eol_ = true;
 }
 
 void
diff --git a/src/lib/dns/master_lexer.h b/src/lib/dns/master_lexer.h
index cdf2866..1aa4255 100644
--- a/src/lib/dns/master_lexer.h
+++ b/src/lib/dns/master_lexer.h
@@ -54,7 +54,8 @@ public:
         END_OF_LINE, ///< End of line detected
         END_OF_FILE, ///< End of file detected
         INITIAL_WS,  ///< White spaces at the beginning of a line after an
-                     ///< end of line (if asked for detecting it)
+                     ///< end of line or at the beginning of file (if asked
+                     //   for detecting it)
         NOVALUE_TYPE_MAX = INITIAL_WS, ///< Max integer corresponding to
                                        /// no-value (type only) types.
                                        /// Mainly for internal use.
diff --git a/src/lib/dns/master_loader.cc b/src/lib/dns/master_loader.cc
index 0a3db39..88846f0 100644
--- a/src/lib/dns/master_loader.cc
+++ b/src/lib/dns/master_loader.cc
@@ -15,18 +15,27 @@
 #include <dns/master_loader.h>
 #include <dns/master_lexer.h>
 #include <dns/name.h>
+#include <dns/rdataclass.h>
 #include <dns/rrttl.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
 #include <dns/rdata.h>
 
+#include <boost/scoped_ptr.hpp>
+#include <boost/algorithm/string/predicate.hpp> // for iequals
+
 #include <string>
 #include <memory>
+#include <vector>
 #include <boost/algorithm/string/predicate.hpp> // for iequals
+#include <boost/shared_ptr.hpp>
 
 using std::string;
 using std::auto_ptr;
+using std::vector;
+using std::pair;
 using boost::algorithm::iequals;
+using boost::shared_ptr;
 
 namespace isc {
 namespace dns {
@@ -55,6 +64,7 @@ public:
                      MasterLoader::Options options) :
         lexer_(),
         zone_origin_(zone_origin),
+        active_origin_(zone_origin),
         zone_class_(zone_class),
         callbacks_(callbacks),
         add_callback_(add_callback),
@@ -63,10 +73,37 @@ public:
         initialized_(false),
         ok_(true),
         many_errors_((options & MANY_ERRORS) != 0),
+        previous_name_(false),
         complete_(false),
-        seen_error_(false)
+        seen_error_(false),
+        warn_rfc1035_ttl_(true)
     {}
 
+    void pushSource(const std::string& filename, const Name& current_origin) {
+        std::string error;
+        if (!lexer_.pushSource(filename.c_str(), &error)) {
+            if (initialized_) {
+                isc_throw(InternalException, error.c_str());
+            } else {
+                // Top-level file
+                reportError("", 0, error);
+                ok_ = false;
+            }
+        }
+        // Store the current status, so we can recover it upon popSource
+        include_info_.push_back(IncludeInfo(current_origin, last_name_));
+        initialized_ = true;
+        previous_name_ = false;
+    }
+
+    void pushStreamSource(std::istream& stream) {
+        lexer_.pushSource(stream);
+        initialized_ = true;
+    }
+
+    bool loadIncremental(size_t count_limit);
+
+private:
     void reportError(const std::string& filename, size_t line,
                      const std::string& reason)
     {
@@ -81,74 +118,238 @@ public:
         }
     }
 
-    void pushSource(const std::string& filename) {
-        std::string error;
-        if (!lexer_.pushSource(filename.c_str(), &error)) {
-            if (initialized_) {
-                isc_throw(InternalException, error.c_str());
-            } else {
-                // Top-level file
-                reportError("", 0, error);
-                ok_ = false;
-            }
-        }
-        initialized_ = true;
-    }
-
     bool popSource() {
         if (lexer_.getSourceCount() == 1) {
             return (false);
         }
         lexer_.popSource();
+        // Restore original origin and last seen name
+
+        // We move in tandem, there's an extra item included during the
+        // initialization, so we can never run out of them
+        assert(!include_info_.empty());
+        const IncludeInfo& info(include_info_.back());
+        active_origin_ = info.first;
+        last_name_ = info.second;
+        include_info_.pop_back();
+        previous_name_ = false;
         return (true);
     }
 
-    void pushStreamSource(std::istream& stream) {
-        lexer_.pushSource(stream);
-        initialized_ = true;
-    }
-
     // Get a string token. Handle it as error if it is not string.
     const string getString() {
         lexer_.getNextToken(MasterToken::STRING).getString(string_token_);
         return (string_token_);
     }
 
-    bool loadIncremental(size_t count_limit);
+    MasterToken handleInitialToken();
+
+    void doOrigin(bool is_optional) {
+        // Parse and create the new origin. It is relative to the previous
+        // one.
+        const MasterToken&
+            name_tok(lexer_.getNextToken(MasterToken::QSTRING, is_optional));
+
+        if (name_tok.getType() == MasterToken::QSTRING ||
+            name_tok.getType() == MasterToken::STRING) {
+
+            const MasterToken::StringRegion&
+                name_string(name_tok.getStringRegion());
+            active_origin_ = Name(name_string.beg, name_string.len,
+                                  &active_origin_);
+            if (name_string.len > 0 &&
+                name_string.beg[name_string.len - 1] != '.') {
+                callbacks_.warning(lexer_.getSourceName(),
+                                   lexer_.getSourceLine(),
+                                   "The new origin is relative, did you really"
+                                   " mean " + active_origin_.toText() + "?");
+            }
+        } else {
+            // If it is not optional, we must not get anything but
+            // a string token.
+            assert(is_optional);
+
+            // We return the newline there. This is because we want to
+            // behave the same if there is or isn't the name, leaving the
+            // newline there.
+            lexer_.ungetToken();
+        }
+    }
 
     void doInclude() {
         // First, get the filename to include
         const string
             filename(lexer_.getNextToken(MasterToken::QSTRING).getString());
 
-        // There could be an origin (or maybe not). So try looking
-        const MasterToken name_tok(lexer_.getNextToken(MasterToken::QSTRING,
-                                                       true));
+        // There optionally can be an origin, that applies before the include.
+        // We need to save the currently active origin before calling
+        // doOrigin(), because it would update active_origin_ while we need
+        // to pass the active origin before recognizing the new origin to
+        // pushSource.  Note: RFC 1035 is not really clear on this: it reads
+        // "regardless of changes... within the included file", but the new
+        // origin is not really specified "within the included file".
+        // Nevertheless, this behavior is probably more likely to be the
+        // intent of the RFC, and it's compatible with BIND 9.
+        const Name current_origin = active_origin_;
+        doOrigin(true);
+
+        pushSource(filename, current_origin);
+    }
 
-        if (name_tok.getType() == MasterToken::QSTRING ||
-            name_tok.getType() == MasterToken::STRING) {
-            // TODO: Handle the origin. Once we complete #2427.
+    // A helper method for loadIncremental(). It parses part of an RR
+    // until it finds the RR type field.  If TTL or RR class is
+    // specified before the RR type, it also recognizes and validates
+    // them.  explicit_ttl will be set to true if this method finds a
+    // valid TTL field.
+    RRType parseRRParams(bool& explicit_ttl, MasterToken rrparam_token) {
+        // Find TTL, class and type.  Both TTL and class are
+        // optional and may occur in any order if they exist. TTL
+        // and class come before type which must exist.
+        //
+        // [<TTL>] [<class>] <type> <RDATA>
+        // [<class>] [<TTL>] <type> <RDATA>
+
+        // named-signzone outputs TTL first, so try parsing it in order
+        // first.
+        if (setCurrentTTL(rrparam_token.getString())) {
+            explicit_ttl = true;
+            rrparam_token = lexer_.getNextToken(MasterToken::STRING);
         } else {
-            // We return the newline there. This is because after we pop
-            // the source, we want to call eatUntilEOL and this would
-            // eat to the next one.
-            lexer_.ungetToken();
+            // If it's not a TTL here, continue and try again
+            // after the RR class below.
+        }
+
+        const MaybeRRClass rrclass =
+            RRClass::createFromText(rrparam_token.getString());
+        if (rrclass) {
+            if (*rrclass != zone_class_) {
+                isc_throw(InternalException, "Class mismatch: " << *rrclass <<
+                          " vs. " << zone_class_);
+            }
+            rrparam_token = lexer_.getNextToken(MasterToken::STRING);
         }
 
-        pushSource(filename);
+        // If we couldn't parse TTL earlier in the stream (above), try
+        // again at current location.
+        if (!explicit_ttl && setCurrentTTL(rrparam_token.getString())) {
+            explicit_ttl = true;
+            rrparam_token = lexer_.getNextToken(MasterToken::STRING);
+        }
+
+        // Return the current string token's value as the RRType.
+        return (RRType(rrparam_token.getString()));
+    }
+
+    // Upper limit check when recognizing a specific TTL value from the
+    // zone file ($TTL, the RR's TTL field, or the SOA minimum).  RFC2181
+    // Section 8 limits the range of TTL values to 2^31-1 (0x7fffffff),
+    // and prohibits transmitting a TTL field exceeding this range.  We
+    // guarantee that by limiting the value at the time of zone
+    // parsing/loading, following what BIND 9 does.  Resetting it to 0
+    // at this point may not be exactly what the RFC states (depending on
+    // the meaning of 'received'), but the end result would be the same (i.e.,
+    // the guarantee on transmission).  Again, we follow the BIND 9's behavior
+    // here.
+    //
+    // post_parsing is true iff this method is called after parsing the entire
+    // RR and the lexer is positioned at the next line.  It's just for
+    // calculating the accurate source line when callback is necessary.
+    void limitTTL(RRTTL& ttl, bool post_parsing) {
+        if (ttl > RRTTL::MAX_TTL()) {
+            const size_t src_line = lexer_.getSourceLine() -
+                (post_parsing ? 1 : 0);
+            callbacks_.warning(lexer_.getSourceName(), src_line,
+                               "TTL " + ttl.toText() + " > MAXTTL, "
+                               "setting to 0 per RFC2181");
+            ttl = RRTTL(0);
+        }
+    }
+
+    // Set/reset the default TTL.  This should be from either $TTL or SOA
+    // minimum TTL (it's the caller's responsibility; this method doesn't
+    // care about where it comes from).  see LimitTTL() for parameter
+    // post_parsing.
+    void setDefaultTTL(const RRTTL& ttl, bool post_parsing) {
+        if (!default_ttl_) {
+            default_ttl_.reset(new RRTTL(ttl));
+        } else {
+            *default_ttl_ = ttl;
+        }
+        limitTTL(*default_ttl_, post_parsing);
+    }
+
+    // Try to set/reset the current TTL from candidate TTL text.  It's possible
+    // it does not actually represent a TTL (which is not immediately
+    // considered an error).  Return true iff it's recognized as a valid TTL
+    // (and only in which case the current TTL is set).
+    bool setCurrentTTL(const string& ttl_txt) {
+        // We use the factory version instead of RRTTL constructor as we
+        // need to expect cases where ttl_txt does not actually represent a TTL
+        // but an RR class or type.
+        const MaybeRRTTL maybe_ttl = RRTTL::createFromText(ttl_txt);
+        if (maybe_ttl) {
+            current_ttl_ = maybe_ttl;
+            limitTTL(*current_ttl_, false);
+            return (true);
+        }
+        return (false);
+    }
+
+    // Determine the TTL of the current RR based on the given parsing context.
+    //
+    // explicit_ttl is true iff the TTL is explicitly specified for that RR
+    // (in which case current_ttl_ is set to that TTL).
+    // rrtype is the type of the current RR, and rdata is its RDATA.  They
+    // only matter if the type is SOA and no available TTL is known.  In this
+    // case the minimum TTL of the SOA will be used as the TTL of that SOA
+    // and the default TTL for subsequent RRs.
+    const RRTTL& getCurrentTTL(bool explicit_ttl, const RRType& rrtype,
+                               const rdata::ConstRdataPtr& rdata) {
+        // We've completed parsing the full of RR, and the lexer is already
+        // positioned at the next line.  If we need to call callback,
+        // we need to adjust the line number.
+        const size_t current_line = lexer_.getSourceLine() - 1;
+
+        if (!current_ttl_ && !default_ttl_) {
+            if (rrtype == RRType::SOA()) {
+                callbacks_.warning(lexer_.getSourceName(), current_line,
+                                   "no TTL specified; "
+                                   "using SOA MINTTL instead");
+                const uint32_t ttl_val =
+                    dynamic_cast<const rdata::generic::SOA&>(*rdata).
+                    getMinimum();
+                setDefaultTTL(RRTTL(ttl_val), true);
+                current_ttl_ = *default_ttl_;
+            } else {
+                // On catching the exception we'll try to reach EOL again,
+                // so we need to unget it now.
+                lexer_.ungetToken();
+                throw InternalException(__FILE__, __LINE__,
+                                        "no TTL specified; load rejected");
+            }
+        } else if (!explicit_ttl && default_ttl_) {
+            current_ttl_ = *default_ttl_;
+        } else if (!explicit_ttl && warn_rfc1035_ttl_) {
+            // Omitted (class and) TTL values are default to the last
+            // explicitly stated values (RFC 1035, Sec. 5.1).
+            callbacks_.warning(lexer_.getSourceName(), current_line,
+                               "using RFC1035 TTL semantics; default to the "
+                               "last explicitly stated TTL");
+            warn_rfc1035_ttl_ = false; // we only warn about this once
+        }
+        assert(current_ttl_);
+        return (*current_ttl_);
     }
 
     void handleDirective(const char* directive, size_t length) {
         if (iequals(directive, "INCLUDE")) {
             doInclude();
         } else if (iequals(directive, "ORIGIN")) {
-            // TODO: Implement
-            isc_throw(isc::NotImplemented,
-                      "Origin directive not implemented yet");
+            doOrigin(false);
+            eatUntilEOL(true);
         } else if (iequals(directive, "TTL")) {
-            // TODO: Implement
-            isc_throw(isc::NotImplemented,
-                      "TTL directive not implemented yet");
+            setDefaultTTL(RRTTL(getString()), false);
+            eatUntilEOL(true);
         } else {
             isc_throw(InternalException, "Unknown directive '" <<
                       string(directive, directive + length) << "'");
@@ -187,9 +388,17 @@ public:
 private:
     MasterLexer lexer_;
     const Name zone_origin_;
+    Name active_origin_; // The origin used during parsing
+                         // (modifiable by $ORIGIN)
+    shared_ptr<Name> last_name_; // Last seen name (for INITAL_WS handling)
     const RRClass zone_class_;
     MasterLoaderCallbacks callbacks_;
     AddRRCallback add_callback_;
+    boost::scoped_ptr<RRTTL> default_ttl_; // Default TTL of RRs used when
+                                           // unspecified.  If NULL no default
+                                           // is known.
+    MaybeRRTTL current_ttl_; // The TTL used most recently.  Initially unset.
+                             // Once set always stores a valid RRTTL.
     const MasterLoader::Options options_;
     const std::string master_file_;
     std::string string_token_;
@@ -197,12 +406,106 @@ private:
     bool ok_;                   // Is it OK to continue loading?
     const bool many_errors_;    // Are many errors allowed (or should we abort
                                 // on the first)
+    // Some info about the outer files from which we include.
+    // The first one is current origin, the second is the last seen name
+    // in that file.
+    typedef pair<Name, shared_ptr<Name> > IncludeInfo;
+    vector<IncludeInfo> include_info_;
+    bool previous_name_; // True if there was a previous name in this file
+                         // (false at the beginning or after an $INCLUDE line)
 public:
     bool complete_;             // All work done.
     bool seen_error_;           // Was there at least one error during the
                                 // load?
+    bool warn_rfc1035_ttl_;     // should warn if implicit TTL determination
+                                // from the previous RR is used.
 };
 
+// A helper method of loadIncremental, parsing the first token of a new line.
+// If it looks like an RR, detect its owner name and return a string token for
+// the next field of the RR.
+// Otherwise, return either END_OF_LINE or END_OF_FILE token depending on
+// whether the loader continues to the next line or completes the load,
+// respectively.  Other corner cases including $-directive handling is done
+// here.
+// For unexpected errors, it throws an exception, which will be handled in
+// loadIncremental.
+MasterToken
+MasterLoader::MasterLoaderImpl::handleInitialToken() {
+    const MasterToken& initial_token =
+        lexer_.getNextToken(MasterLexer::QSTRING | MasterLexer::INITIAL_WS);
+
+    // The most likely case is INITIAL_WS, and then string/qstring.  We
+    // handle them first.
+    if (initial_token.getType() == MasterToken::INITIAL_WS) {
+        const MasterToken& next_token = lexer_.getNextToken();
+        if (next_token.getType() == MasterToken::END_OF_LINE) {
+            return (next_token); // blank line
+        } else if (next_token.getType() == MasterToken::END_OF_FILE) {
+            lexer_.ungetToken(); // handle it in the next iteration.
+            eatUntilEOL(true);  // effectively warn about the unexpected EOF.
+            return (MasterToken(MasterToken::END_OF_LINE));
+        }
+
+        // This means the same name as previous.
+        if (last_name_.get() == NULL) {
+            isc_throw(InternalException, "No previous name to use in "
+                      "place of initial whitespace");
+        } else if (!previous_name_) {
+            callbacks_.warning(lexer_.getSourceName(), lexer_.getSourceLine(),
+                               "Owner name omitted around $INCLUDE, the result "
+                               "might not be as expected");
+        }
+        return (next_token);
+    } else if (initial_token.getType() == MasterToken::STRING ||
+               initial_token.getType() == MasterToken::QSTRING) {
+        // If it is name (or directive), handle it.
+        const MasterToken::StringRegion&
+            name_string(initial_token.getStringRegion());
+
+        if (name_string.len > 0 && name_string.beg[0] == '$') {
+            // This should have either thrown (and the error handler
+            // will read up until the end of line) or read until the
+            // end of line.
+
+            // Exclude the $ from the string on this point.
+            handleDirective(name_string.beg + 1, name_string.len - 1);
+            // So, get to the next line, there's nothing more interesting
+            // in this one.
+            return (MasterToken(MasterToken::END_OF_LINE));
+        }
+
+        // This should be an RR, starting with an owner name.  Construct the
+        // name, and some string token should follow.
+        last_name_.reset(new Name(name_string.beg, name_string.len,
+                                  &active_origin_));
+        previous_name_ = true;
+        return (lexer_.getNextToken(MasterToken::STRING));
+    }
+
+    switch (initial_token.getType()) { // handle less common cases
+    case MasterToken::END_OF_FILE:
+        if (!popSource()) {
+            return (initial_token);
+        } else {
+            // We try to read a token from the popped source
+            // So continue to the next line of that source, but first, make
+            // sure the source is at EOL
+            eatUntilEOL(true);
+            return (MasterToken(MasterToken::END_OF_LINE));
+        }
+    case MasterToken::END_OF_LINE:
+        return (initial_token); // empty line
+    case MasterToken::ERROR:
+        // Error token here.
+        isc_throw(InternalException, initial_token.getErrorText());
+    default:
+        // Some other token (what could that be?)
+        isc_throw(InternalException, "Parser got confused (unexpected "
+                  "token " << initial_token.getType() << ")");
+    }
+}
+
 bool
 MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
     if (count_limit == 0) {
@@ -213,78 +516,37 @@ MasterLoader::MasterLoaderImpl::loadIncremental(size_t count_limit) {
                   "Trying to load when already loaded");
     }
     if (!initialized_) {
-        pushSource(master_file_);
+        pushSource(master_file_, active_origin_);
     }
     size_t count = 0;
     while (ok_ && count < count_limit) {
         try {
-            // Skip all EOLNs (empty lines) and finish on EOF
-            bool empty = true;
-            do {
-                const MasterToken& empty_token(lexer_.getNextToken());
-                if (empty_token.getType() == MasterToken::END_OF_FILE) {
-                    if (!popSource()) {
-                        return (true);
-                    } else {
-                        // We try to read a token from the popped source
-                        // So retry the loop, but first, make sure the source
-                        // is at EOL
-                        eatUntilEOL(true);
-                        continue;
-                    }
-                }
-                empty = empty_token.getType() == MasterToken::END_OF_LINE;
-            } while (empty);
-            // Return the last token, as it was not empty
-            lexer_.ungetToken();
-
-            const MasterToken::StringRegion&
-                name_string(lexer_.getNextToken(MasterToken::QSTRING).
-                            getStringRegion());
-
-            if (name_string.len > 0 && name_string.beg[0] == '$') {
-                // This should have either thrown (and the error handler
-                // will read up until the end of line) or read until the
-                // end of line.
-
-                // Exclude the $ from the string on this point.
-                handleDirective(name_string.beg + 1, name_string.len - 1);
-                // So, get to the next line, there's nothing more interesting
-                // in this one.
-                continue;
+            const MasterToken next_token = handleInitialToken();
+            if (next_token.getType() == MasterToken::END_OF_FILE) {
+                return (true);  // we are done
+            } else if (next_token.getType() == MasterToken::END_OF_LINE) {
+                continue;       // nothing more to do in this line
             }
+            // We are going to parse an RR, have known the owner name,
+            // and are now seeing the next string token in the rest of the RR.
+            assert(next_token.getType() == MasterToken::STRING);
 
-            const Name name(name_string.beg, name_string.len,
-                            &zone_origin_);
-            // TODO: Some more flexibility. We don't allow omitting
-            // anything yet
-
-            // The parameters
-            const RRTTL ttl(getString());
-            const RRClass rrclass(getString());
-            const RRType rrtype(getString());
-
-            // TODO: Some more validation?
-            if (rrclass != zone_class_) {
-                // It doesn't really matter much what type of exception
-                // we throw, we catch it just below.
-                isc_throw(isc::BadValue, "Class mismatch: " << rrclass <<
-                          "vs. " << zone_class_);
-            }
+            bool explicit_ttl = false;
+            const RRType rrtype = parseRRParams(explicit_ttl, next_token);
             // TODO: Check if it is SOA, it should be at the origin.
 
-            const rdata::RdataPtr data(rdata::createRdata(rrtype, rrclass,
-                                                          lexer_,
-                                                          &zone_origin_,
-                                                          options_,
-                                                          callbacks_));
+            const rdata::RdataPtr rdata =
+                rdata::createRdata(rrtype, zone_class_, lexer_,
+                                   &active_origin_, options_, callbacks_);
+
             // In case we get NULL, it means there was error creating
             // the Rdata. The errors should have been reported by
             // callbacks_ already. We need to decide if we want to continue
             // or not.
-            if (data) {
-                add_callback_(name, rrclass, rrtype, ttl, data);
-
+            if (rdata) {
+                add_callback_(*last_name_, zone_class_, rrtype,
+                              getCurrentTTL(explicit_ttl, rrtype, rdata),
+                              rdata);
                 // Good, we loaded another one
                 ++count;
             } else {
diff --git a/src/lib/dns/master_loader_callbacks.cc b/src/lib/dns/master_loader_callbacks.cc
new file mode 100644
index 0000000..434db9b
--- /dev/null
+++ b/src/lib/dns/master_loader_callbacks.cc
@@ -0,0 +1,34 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dns/master_loader_callbacks.h>
+
+#include <string>
+
+namespace isc {
+namespace dns {
+
+namespace {
+void
+nullCallback(const std::string&, size_t, const std::string&) {
+}
+}
+
+MasterLoaderCallbacks
+MasterLoaderCallbacks::getNullCallbacks() {
+    return (MasterLoaderCallbacks(nullCallback, nullCallback));
+}
+
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/master_loader_callbacks.h b/src/lib/dns/master_loader_callbacks.h
index f572194..f9cc18b 100644
--- a/src/lib/dns/master_loader_callbacks.h
+++ b/src/lib/dns/master_loader_callbacks.h
@@ -122,6 +122,16 @@ public:
         warning_(source_name, source_line, reason);
     }
 
+    /// \brief Return a callbacks instance with null callbacks
+    ///
+    /// This is a convenience wrapper to generate a
+    /// \c MasterLoaderCallbacks object with both callbacks being nothing.
+    /// This will be useful for applications that only need to run
+    /// \c MasterLoader and get the end result.
+    ///
+    /// \throw None
+    static MasterLoaderCallbacks getNullCallbacks();
+
 private:
     IssueCallback error_, warning_;
 };
diff --git a/src/lib/dns/python/pydnspp.cc b/src/lib/dns/python/pydnspp.cc
index 23ed463..6d1bd89 100644
--- a/src/lib/dns/python/pydnspp.cc
+++ b/src/lib/dns/python/pydnspp.cc
@@ -65,21 +65,13 @@ namespace {
 
 bool
 initModulePart_EDNS(PyObject* mod) {
-    // We initialize the static description object with PyType_Ready(),
-    // then add it to the module. This is not just a check! (leaving
-    // this out results in segmentation faults)
-    //
     // After the type has been initialized, we initialize any exceptions
     // that are defined in the wrapper for this class, and add constants
     // to the type, if any
 
-    if (PyType_Ready(&edns_type) < 0) {
+    if (!initClass(edns_type, "EDNS", mod)) {
         return (false);
     }
-    Py_INCREF(&edns_type);
-    void* p = &edns_type;
-    PyModule_AddObject(mod, "EDNS", static_cast<PyObject*>(p));
-
     addClassVariable(edns_type, "SUPPORTED_VERSION",
                      Py_BuildValue("B", EDNS::SUPPORTED_VERSION));
 
@@ -88,14 +80,9 @@ initModulePart_EDNS(PyObject* mod) {
 
 bool
 initModulePart_Message(PyObject* mod) {
-    if (PyType_Ready(&message_type) < 0) {
-        return (false);
-    }
-    void* p = &message_type;
-    if (PyModule_AddObject(mod, "Message", static_cast<PyObject*>(p)) < 0) {
+    if (!initClass(message_type, "Message", mod)) {
         return (false);
     }
-    Py_INCREF(&message_type);
 
     try {
         //
@@ -186,32 +173,25 @@ initModulePart_Message(PyObject* mod) {
 
 bool
 initModulePart_MessageRenderer(PyObject* mod) {
-    if (PyType_Ready(&messagerenderer_type) < 0) {
+    if (!initClass(messagerenderer_type, "MessageRenderer", mod)) {
         return (false);
     }
-    Py_INCREF(&messagerenderer_type);
 
     addClassVariable(messagerenderer_type, "CASE_INSENSITIVE",
                      Py_BuildValue("I", MessageRenderer::CASE_INSENSITIVE));
     addClassVariable(messagerenderer_type, "CASE_SENSITIVE",
                      Py_BuildValue("I", MessageRenderer::CASE_SENSITIVE));
 
-    PyModule_AddObject(mod, "MessageRenderer",
-                       reinterpret_cast<PyObject*>(&messagerenderer_type));
 
     return (true);
 }
 
 bool
-initModulePart_Name(PyObject* mod) {
-
-    //
-    // NameComparisonResult
-    //
-    if (PyType_Ready(&name_comparison_result_type) < 0) {
+initModulePart_NameComparisonResult(PyObject* mod) {
+    if (!initClass(name_comparison_result_type,
+                   "NameComparisonResult", mod)) {
         return (false);
     }
-    Py_INCREF(&name_comparison_result_type);
 
     // Add the enums to the module
     po_NameRelation = Py_BuildValue("{i:s,i:s,i:s,i:s}",
@@ -231,17 +211,14 @@ initModulePart_Name(PyObject* mod) {
     addClassVariable(name_comparison_result_type, "COMMONANCESTOR",
                      Py_BuildValue("I", NameComparisonResult::COMMONANCESTOR));
 
-    PyModule_AddObject(mod, "NameComparisonResult",
-        reinterpret_cast<PyObject*>(&name_comparison_result_type));
-
-    //
-    // Name
-    //
+    return (true);
+}
 
-    if (PyType_Ready(&name_type) < 0) {
+bool
+initModulePart_Name(PyObject* mod) {
+    if (!initClass(name_type, "Name", mod)) {
         return (false);
     }
-    Py_INCREF(&name_type);
 
     // Add the constants to the module
     addClassVariable(name_type, "MAX_WIRE",
@@ -260,51 +237,56 @@ initModulePart_Name(PyObject* mod) {
     addClassVariable(name_type, "ROOT_NAME",
                      createNameObject(Name::ROOT_NAME()));
 
-    PyModule_AddObject(mod, "Name",
-                       reinterpret_cast<PyObject*>(&name_type));
-
-
     // Add the exceptions to the module
-    po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
-    PyModule_AddObject(mod, "EmptyLabel", po_EmptyLabel);
+    try {
+        po_EmptyLabel = PyErr_NewException("pydnspp.EmptyLabel", NULL, NULL);
+        PyObjectContainer(po_EmptyLabel).installToModule(mod, "EmptyLabel");
 
-    po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
-    PyModule_AddObject(mod, "TooLongName", po_TooLongName);
+        po_TooLongName = PyErr_NewException("pydnspp.TooLongName", NULL, NULL);
+        PyObjectContainer(po_TooLongName).installToModule(mod, "TooLongName");
 
-    po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
-    PyModule_AddObject(mod, "TooLongLabel", po_TooLongLabel);
+        po_TooLongLabel = PyErr_NewException("pydnspp.TooLongLabel", NULL, NULL);
+        PyObjectContainer(po_TooLongLabel).installToModule(mod, "TooLongLabel");
 
-    po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
-    PyModule_AddObject(mod, "BadLabelType", po_BadLabelType);
+        po_BadLabelType = PyErr_NewException("pydnspp.BadLabelType", NULL, NULL);
+        PyObjectContainer(po_BadLabelType).installToModule(mod, "BadLabelType");
 
-    po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
-    PyModule_AddObject(mod, "BadEscape", po_BadEscape);
+        po_BadEscape = PyErr_NewException("pydnspp.BadEscape", NULL, NULL);
+        PyObjectContainer(po_BadEscape).installToModule(mod, "BadEscape");
 
-    po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL, NULL);
-    PyModule_AddObject(mod, "IncompleteName", po_IncompleteName);
+        po_IncompleteName = PyErr_NewException("pydnspp.IncompleteName", NULL,
+                                               NULL);
+        PyObjectContainer(po_IncompleteName).installToModule(mod, "IncompleteName");
 
-    po_InvalidBufferPosition =
-        PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidBufferPosition", po_InvalidBufferPosition);
+        po_InvalidBufferPosition =
+            PyErr_NewException("pydnspp.InvalidBufferPosition", NULL, NULL);
+        PyObjectContainer(po_InvalidBufferPosition).installToModule(
+            mod, "InvalidBufferPosition");
 
-    // This one could have gone into the message_python.cc file, but is
-    // already needed here.
-    po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR",
-                                              NULL, NULL);
-    PyModule_AddObject(mod, "DNSMessageFORMERR", po_DNSMessageFORMERR);
+        // This one could have gone into the message_python.cc file, but is
+        // already needed here.
+        po_DNSMessageFORMERR = PyErr_NewException("pydnspp.DNSMessageFORMERR",
+                                                  NULL, NULL);
+        PyObjectContainer(po_DNSMessageFORMERR).installToModule(
+            mod, "DNSMessageFORMERR");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in Name initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in Name initialization");
+        return (false);
+    }
 
     return (true);
 }
 
 bool
 initModulePart_Opcode(PyObject* mod) {
-    if (PyType_Ready(&opcode_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&opcode_type);
-    void* p = &opcode_type;
-    if (PyModule_AddObject(mod, "Opcode", static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&opcode_type);
+    if (!initClass(opcode_type, "Opcode", mod)) {
         return (false);
     }
 
@@ -346,25 +328,12 @@ initModulePart_Opcode(PyObject* mod) {
 
 bool
 initModulePart_Question(PyObject* mod) {
-    if (PyType_Ready(&question_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&question_type);
-    PyModule_AddObject(mod, "Question",
-                       reinterpret_cast<PyObject*>(&question_type));
-
-    return (true);
+    return (initClass(question_type, "Question", mod));
 }
 
 bool
 initModulePart_Rcode(PyObject* mod) {
-    if (PyType_Ready(&rcode_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&rcode_type);
-    void* p = &rcode_type;
-    if (PyModule_AddObject(mod, "Rcode", static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&rcode_type);
+    if (!initClass(rcode_type, "Rcode", mod)) {
         return (false);
     }
 
@@ -408,126 +377,168 @@ initModulePart_Rcode(PyObject* mod) {
 
 bool
 initModulePart_Rdata(PyObject* mod) {
-    if (PyType_Ready(&rdata_type) < 0) {
+    if (!initClass(rdata_type, "Rdata", mod)) {
         return (false);
     }
-    Py_INCREF(&rdata_type);
-    PyModule_AddObject(mod, "Rdata",
-                       reinterpret_cast<PyObject*>(&rdata_type));
 
     // Add the exceptions to the class
-    po_InvalidRdataLength = PyErr_NewException("pydnspp.InvalidRdataLength",
-                                               NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRdataLength", po_InvalidRdataLength);
-
-    po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText",
-                                             NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRdataText", po_InvalidRdataText);
-
-    po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong",
-                                              NULL, NULL);
-    PyModule_AddObject(mod, "CharStringTooLong", po_CharStringTooLong);
-
+    try {
+        po_InvalidRdataLength =
+            PyErr_NewException("pydnspp.InvalidRdataLength", NULL, NULL);
+        PyObjectContainer(po_InvalidRdataLength).installToModule(
+            mod, "InvalidRdataLength");
+
+        po_InvalidRdataText = PyErr_NewException("pydnspp.InvalidRdataText",
+                                                 NULL, NULL);
+        PyObjectContainer(po_InvalidRdataText).installToModule(
+            mod, "InvalidRdataText");
+
+        po_CharStringTooLong = PyErr_NewException("pydnspp.CharStringTooLong",
+                                                  NULL, NULL);
+        PyObjectContainer(po_CharStringTooLong).installToModule(
+            mod, "CharStringTooLong");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in Rdata initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in Rdata initialization");
+        return (false);
+    }
 
     return (true);
 }
 
 bool
 initModulePart_RRClass(PyObject* mod) {
-    po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass",
-                                           NULL, NULL);
-    Py_INCREF(po_InvalidRRClass);
-    PyModule_AddObject(mod, "InvalidRRClass", po_InvalidRRClass);
-    po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass",
-                                              NULL, NULL);
-    Py_INCREF(po_IncompleteRRClass);
-    PyModule_AddObject(mod, "IncompleteRRClass", po_IncompleteRRClass);
+    if (!initClass(rrclass_type, "RRClass", mod)) {
+        return (false);
+    }
 
-    if (PyType_Ready(&rrclass_type) < 0) {
+    try {
+        po_InvalidRRClass = PyErr_NewException("pydnspp.InvalidRRClass",
+                                               NULL, NULL);
+        PyObjectContainer(po_InvalidRRClass).installToModule(
+            mod, "InvalidRRClass");
+
+        po_IncompleteRRClass = PyErr_NewException("pydnspp.IncompleteRRClass",
+                                                  NULL, NULL);
+        PyObjectContainer(po_IncompleteRRClass).installToModule(
+            mod, "IncompleteRRClass");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in RRClass initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in RRClass initialization");
         return (false);
     }
-    Py_INCREF(&rrclass_type);
-    PyModule_AddObject(mod, "RRClass",
-                       reinterpret_cast<PyObject*>(&rrclass_type));
 
     return (true);
 }
 
 bool
 initModulePart_RRset(PyObject* mod) {
-    po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
-    PyModule_AddObject(mod, "EmptyRRset", po_EmptyRRset);
+    if (!initClass(rrset_type, "RRset", mod)) {
+        return (false);
+    }
 
-    // NameComparisonResult
-    if (PyType_Ready(&rrset_type) < 0) {
+    try {
+        po_EmptyRRset = PyErr_NewException("pydnspp.EmptyRRset", NULL, NULL);
+        PyObjectContainer(po_EmptyRRset).installToModule(mod, "EmptyRRset");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in RRset initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in RRset initialization");
         return (false);
     }
-    Py_INCREF(&rrset_type);
-    PyModule_AddObject(mod, "RRset",
-                       reinterpret_cast<PyObject*>(&rrset_type));
 
     return (true);
 }
 
 bool
 initModulePart_RRTTL(PyObject* mod) {
-    po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRRTTL", po_InvalidRRTTL);
-    po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL",
-                                            NULL, NULL);
-    PyModule_AddObject(mod, "IncompleteRRTTL", po_IncompleteRRTTL);
+    if (!initClass(rrttl_type, "RRTTL", mod)) {
+        return (false);
+    }
+
+    try {
+        po_InvalidRRTTL = PyErr_NewException("pydnspp.InvalidRRTTL",
+                                             NULL, NULL);
+        PyObjectContainer(po_InvalidRRTTL).installToModule(mod,
+                                                           "InvalidRRTTL");
 
-    if (PyType_Ready(&rrttl_type) < 0) {
+        po_IncompleteRRTTL = PyErr_NewException("pydnspp.IncompleteRRTTL",
+                                                NULL, NULL);
+        PyObjectContainer(po_IncompleteRRTTL).installToModule(
+            mod, "IncompleteRRTTL");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in RRTTL initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in RRTTL initialization");
         return (false);
     }
-    Py_INCREF(&rrttl_type);
-    PyModule_AddObject(mod, "RRTTL",
-                       reinterpret_cast<PyObject*>(&rrttl_type));
 
     return (true);
 }
 
 bool
 initModulePart_RRType(PyObject* mod) {
-    // Add the exceptions to the module
-    po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType", NULL, NULL);
-    PyModule_AddObject(mod, "InvalidRRType", po_InvalidRRType);
-    po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType",
-                                             NULL, NULL);
-    PyModule_AddObject(mod, "IncompleteRRType", po_IncompleteRRType);
+    if (!initClass(rrtype_type, "RRType", mod)) {
+        return (false);
+    }
+
+    try {
+        po_InvalidRRType = PyErr_NewException("pydnspp.InvalidRRType",
+                                              NULL, NULL);
+        PyObjectContainer(po_InvalidRRType).installToModule(mod,
+                                                            "InvalidRRType");
 
-    if (PyType_Ready(&rrtype_type) < 0) {
+        po_IncompleteRRType = PyErr_NewException("pydnspp.IncompleteRRType",
+                                                 NULL, NULL);
+        PyObjectContainer(po_IncompleteRRType).installToModule(
+            mod, "IncompleteRRType");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in RRType initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (false);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in RRType initialization");
         return (false);
     }
-    Py_INCREF(&rrtype_type);
-    PyModule_AddObject(mod, "RRType",
-                       reinterpret_cast<PyObject*>(&rrtype_type));
 
     return (true);
 }
 
 bool
 initModulePart_Serial(PyObject* mod) {
-    if (PyType_Ready(&serial_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&serial_type);
-    PyModule_AddObject(mod, "Serial",
-                       reinterpret_cast<PyObject*>(&serial_type));
-
-    return (true);
+    return (initClass(serial_type, "Serial", mod));
 }
 
 bool
 initModulePart_TSIGError(PyObject* mod) {
-    if (PyType_Ready(&tsigerror_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigerror_type;
-    if (PyModule_AddObject(mod, "TSIGError", static_cast<PyObject*>(p)) < 0) {
+    if (!initClass(tsigerror_type, "TSIGError", mod)) {
         return (false);
     }
-    Py_INCREF(&tsigerror_type);
 
     try {
         // Constant class variables
@@ -595,14 +606,9 @@ initModulePart_TSIGError(PyObject* mod) {
 
 bool
 initModulePart_TSIGKey(PyObject* mod) {
-    if (PyType_Ready(&tsigkey_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigkey_type;
-    if (PyModule_AddObject(mod, "TSIGKey", static_cast<PyObject*>(p)) != 0) {
+    if (!initClass(tsigkey_type, "TSIGKey", mod)) {
         return (false);
     }
-    Py_INCREF(&tsigkey_type);
 
     try {
         // Constant class variables
@@ -635,14 +641,7 @@ initModulePart_TSIGKey(PyObject* mod) {
 
 bool
 initModulePart_TSIGKeyRing(PyObject* mod) {
-    if (PyType_Ready(&tsigkeyring_type) < 0) {
-        return (false);
-    }
-    Py_INCREF(&tsigkeyring_type);
-    void* p = &tsigkeyring_type;
-    if (PyModule_AddObject(mod, "TSIGKeyRing",
-                           static_cast<PyObject*>(p)) != 0) {
-        Py_DECREF(&tsigkeyring_type);
+    if (!initClass(tsigkeyring_type, "TSIGKeyRing", mod)) {
         return (false);
     }
 
@@ -658,15 +657,9 @@ initModulePart_TSIGKeyRing(PyObject* mod) {
 
 bool
 initModulePart_TSIGContext(PyObject* mod) {
-    if (PyType_Ready(&tsigcontext_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigcontext_type;
-    if (PyModule_AddObject(mod, "TSIGContext",
-                           static_cast<PyObject*>(p)) < 0) {
+    if (!initClass(tsigcontext_type, "TSIGContext", mod)) {
         return (false);
     }
-    Py_INCREF(&tsigcontext_type);
 
     try {
         // Class specific exceptions
@@ -707,28 +700,14 @@ initModulePart_TSIGContext(PyObject* mod) {
 
 bool
 initModulePart_TSIG(PyObject* mod) {
-    if (PyType_Ready(&tsig_type) < 0) {
-        return (false);
-    }
-    void* p = &tsig_type;
-    if (PyModule_AddObject(mod, "TSIG", static_cast<PyObject*>(p)) < 0) {
-        return (false);
-    }
-    Py_INCREF(&tsig_type);
-
-    return (true);
+    return (initClass(tsig_type, "TSIG", mod));
 }
 
 bool
 initModulePart_TSIGRecord(PyObject* mod) {
-    if (PyType_Ready(&tsigrecord_type) < 0) {
-        return (false);
-    }
-    void* p = &tsigrecord_type;
-    if (PyModule_AddObject(mod, "TSIGRecord", static_cast<PyObject*>(p)) < 0) {
+    if (!initClass(tsigrecord_type, "TSIGRecord", mod)) {
         return (false);
     }
-    Py_INCREF(&tsigrecord_type);
 
     try {
         // Constant class variables
@@ -773,16 +752,38 @@ PyInit_pydnspp(void) {
         return (NULL);
     }
 
-    // Add the exceptions to the class
-    po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
-    PyModule_AddObject(mod, "IscException", po_IscException);
-
-    po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
-                                             NULL, NULL);
-    PyModule_AddObject(mod, "InvalidParameter", po_InvalidParameter);
+    try {
+        // Add the exceptions to the class
+        po_IscException = PyErr_NewException("pydnspp.IscException", NULL, NULL);
+        PyObjectContainer(po_IscException).installToModule(mod, "IscException");
+
+        po_InvalidOperation = PyErr_NewException("pydnspp.InvalidOperation",
+                                                 NULL, NULL);
+        PyObjectContainer(po_InvalidOperation).installToModule(
+            mod, "InvalidOperation");
+
+        po_InvalidParameter = PyErr_NewException("pydnspp.InvalidParameter",
+                                                 NULL, NULL);
+        PyObjectContainer(po_InvalidParameter).installToModule(
+            mod, "InvalidParameter");
+    } catch (const std::exception& ex) {
+        const std::string ex_what =
+            "Unexpected failure in pydnspp initialization: " +
+            std::string(ex.what());
+        PyErr_SetString(po_IscException, ex_what.c_str());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(PyExc_SystemError,
+                        "Unexpected failure in pydnspp initialization");
+        return (NULL);
+    }
 
     // for each part included above, we call its specific initializer
 
+    if (!initModulePart_NameComparisonResult(mod)) {
+        return (NULL);
+    }
+
     if (!initModulePart_Name(mod)) {
         return (NULL);
     }
diff --git a/src/lib/dns/python/pydnspp_common.cc b/src/lib/dns/python/pydnspp_common.cc
index 8e623c5..e9d62e0 100644
--- a/src/lib/dns/python/pydnspp_common.cc
+++ b/src/lib/dns/python/pydnspp_common.cc
@@ -47,6 +47,7 @@ namespace dns {
 namespace python {
 // For our 'general' isc::Exceptions
 PyObject* po_IscException;
+PyObject* po_InvalidOperation;
 PyObject* po_InvalidParameter;
 
 // For our own isc::dns::Exception
@@ -91,6 +92,22 @@ addClassVariable(PyTypeObject& c, const char* name, PyObject* obj) {
     }
     return (PyDict_SetItemString(c.tp_dict, name, obj));
 }
+
+bool
+initClass(PyTypeObject& type, const char* name, PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    //
+    void* p = &type;
+    if (PyType_Ready(&type) < 0 ||
+        PyModule_AddObject(mod, name, static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&type);
+    return (true);
+}
+
 }
 }
 }
diff --git a/src/lib/dns/python/pydnspp_common.h b/src/lib/dns/python/pydnspp_common.h
index 5ca1cd8..4095f54 100644
--- a/src/lib/dns/python/pydnspp_common.h
+++ b/src/lib/dns/python/pydnspp_common.h
@@ -28,6 +28,7 @@ namespace dns {
 namespace python {
 // For our 'general' isc::Exceptions
 extern PyObject* po_IscException;
+extern PyObject* po_InvalidOperation;
 extern PyObject* po_InvalidParameter;
 
 // For our own isc::dns::Exception
@@ -47,6 +48,18 @@ int readDataFromSequence(uint8_t *data, size_t len, PyObject* sequence);
 
 int addClassVariable(PyTypeObject& c, const char* name, PyObject* obj);
 
+/// \brief Initialize a wrapped class type, and add to module
+///
+/// The type object is initalized, and its refcount is increased after
+/// successful addition to the module.
+///
+/// \param type The type object to initialize
+/// \param name The python name of the class to add
+/// \param mod The python module to add the class to
+///
+/// \return true on success, false on failure
+bool initClass(PyTypeObject& type, const char* name, PyObject* mod);
+
 // Short term workaround for unifying the return type of tp_hash
 #if PY_MINOR_VERSION < 2
 typedef long Py_hash_t;
diff --git a/src/lib/dns/rdata.h b/src/lib/dns/rdata.h
index 4cd63cc..3fe0c74 100644
--- a/src/lib/dns/rdata.h
+++ b/src/lib/dns/rdata.h
@@ -60,7 +60,7 @@ public:
 
 ///
 /// \brief A standard DNS module exception that is thrown if RDATA parser
-/// parser encounters a character-string (as defined in RFC1035) exceeding
+/// encounters a character-string (as defined in RFC1035) exceeding
 /// the maximum allowable length (\c MAX_CHARSTRING_LEN).
 ///
 class CharStringTooLong : public Exception {
diff --git a/src/lib/dns/rdata/generic/detail/char_string.cc b/src/lib/dns/rdata/generic/detail/char_string.cc
index fb4c9b4..e2857b3 100644
--- a/src/lib/dns/rdata/generic/detail/char_string.cc
+++ b/src/lib/dns/rdata/generic/detail/char_string.cc
@@ -57,8 +57,8 @@ decimalToNumber(const char* s, const char* s_end) {
 }
 
 void
-strToCharString(const MasterToken::StringRegion& str_region,
-                CharString& result)
+stringToCharString(const MasterToken::StringRegion& str_region,
+                   CharString& result)
 {
     // make a space for the 1-byte length field; filled in at the end
     result.push_back(0);
@@ -91,6 +91,29 @@ strToCharString(const MasterToken::StringRegion& str_region,
     result[0] = result.size() - 1;
 }
 
+std::string
+charStringToString(const CharString& char_string) {
+    std::string s;
+    for (CharString::const_iterator it = char_string.begin() + 1;
+         it != char_string.end(); ++it) {
+        const uint8_t ch = *it;
+        if ((ch < 0x20) || (ch >= 0x7f)) {
+            // convert to escaped \xxx (decimal) format
+            s.push_back('\\');
+            s.push_back('0' + ((ch / 100) % 10));
+            s.push_back('0' + ((ch / 10) % 10));
+            s.push_back('0' + (ch % 10));
+            continue;
+        }
+        if ((ch == '"') || (ch == ';') || (ch == '\\')) {
+            s.push_back('\\');
+        }
+        s.push_back(ch);
+    }
+
+    return (s);
+}
+
 } // end of detail
 } // end of generic
 } // end of rdata
diff --git a/src/lib/dns/rdata/generic/detail/char_string.h b/src/lib/dns/rdata/generic/detail/char_string.h
index 702af04..4a146db 100644
--- a/src/lib/dns/rdata/generic/detail/char_string.h
+++ b/src/lib/dns/rdata/generic/detail/char_string.h
@@ -17,6 +17,7 @@
 
 #include <dns/master_lexer.h>
 
+#include <string>
 #include <vector>
 #include <stdint.h>
 
@@ -48,8 +49,22 @@ typedef std::vector<uint8_t> CharString;
 /// \brief str_region A string that represents a character-string.
 /// \brief result A placeholder vector where the resulting data are to be
 /// stored.  Expected to be empty, but it's not checked.
-void strToCharString(const MasterToken::StringRegion& str_region,
-                     CharString& result);
+void stringToCharString(const MasterToken::StringRegion& str_region,
+                        CharString& result);
+
+/// \brief Convert a CharString into a textual DNS character-string.
+///
+/// This method converts a binary 8-bit representation of a DNS
+/// character string into a textual string representation, escaping any
+/// special characters in the process. For example, characters like
+/// double-quotes, semi-colon and backspace are prefixed with backspace
+/// character, and characters not in the printable range of [0x20, 0x7e]
+/// (inclusive) are converted to the \xxx 3-digit decimal
+/// representation.
+///
+/// \param char_string The \c CharString to convert.
+/// \return A string representation of \c char_string.
+std::string charStringToString(const CharString& char_string);
 
 } // namespace detail
 } // namespace generic
diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h
index d1916e3..b48d109 100644
--- a/src/lib/dns/rdata/generic/detail/txt_like.h
+++ b/src/lib/dns/rdata/generic/detail/txt_like.h
@@ -119,7 +119,7 @@ private:
                 break;
             }
             string_list_.push_back(std::vector<uint8_t>());
-            strToCharString(token.getStringRegion(), string_list_.back());
+            stringToCharString(token.getStringRegion(), string_list_.back());
         }
 
         // Let upper layer handle eol/eof.
@@ -177,18 +177,14 @@ public:
     toText() const {
         std::string s;
 
-        // XXX: this implementation is not entirely correct.  for example, it
-        // should escape double-quotes if they appear in the character string.
         for (std::vector<std::vector<uint8_t> >::const_iterator it =
-                 string_list_.begin();
-             it != string_list_.end();
-             ++it)
+                 string_list_.begin(); it != string_list_.end(); ++it)
         {
             if (!s.empty()) {
                 s.push_back(' ');
             }
             s.push_back('"');
-            s.insert(s.end(), (*it).begin() + 1, (*it).end());
+            s.append(charStringToString(*it));
             s.push_back('"');
         }
 
diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc
index f2b9627..3ff2f08 100644
--- a/src/lib/dns/rdata/generic/soa_6.cc
+++ b/src/lib/dns/rdata/generic/soa_6.cc
@@ -16,6 +16,7 @@
 
 #include <string>
 
+#include <boost/static_assert.hpp>
 #include <boost/lexical_cast.hpp>
 
 #include <exceptions/exceptions.h>
@@ -112,6 +113,16 @@ SOA::getSerial() const {
     return (Serial(b.readUint32()));
 }
 
+uint32_t
+SOA::getMinimum() const {
+    // Make sure the buffer access is safe.
+    BOOST_STATIC_ASSERT(sizeof(numdata_) ==
+                        sizeof(uint32_t) * 4 + sizeof(uint32_t));
+
+    InputBuffer b(&numdata_[sizeof(uint32_t) * 4], sizeof(uint32_t));
+    return (b.readUint32());
+}
+
 string
 SOA::toText() const {
     InputBuffer b(numdata_, sizeof(numdata_));
diff --git a/src/lib/dns/rdata/generic/soa_6.h b/src/lib/dns/rdata/generic/soa_6.h
index 2c180b2..d736666 100644
--- a/src/lib/dns/rdata/generic/soa_6.h
+++ b/src/lib/dns/rdata/generic/soa_6.h
@@ -35,8 +35,12 @@ public:
     SOA(const Name& mname, const Name& rname, uint32_t serial,
         uint32_t refresh, uint32_t retry, uint32_t expire,
         uint32_t minimum);
+
     /// \brief Returns the serial stored in the SOA.
     Serial getSerial() const;
+
+    /// brief Returns the minimum TTL field value of the SOA.
+    uint32_t getMinimum() const;
 private:
     /// Note: this is a prototype version; we may reconsider
     /// this representation later.
diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h
index 70d6b78..1ff4163 100644
--- a/src/lib/dns/rrclass-placeholder.h
+++ b/src/lib/dns/rrclass-placeholder.h
@@ -22,6 +22,8 @@
 
 #include <exceptions/exceptions.h>
 
+#include <boost/optional.hpp>
+
 namespace isc {
 namespace util {
 class InputBuffer;
@@ -33,6 +35,16 @@ namespace dns {
 // forward declarations
 class AbstractMessageRenderer;
 
+class RRClass; // forward declaration to define MaybeRRClass.
+
+/// \brief A shortcut for a compound type to represent RRClass-or-not.
+///
+/// A value of this type can be interpreted in a boolean context, whose
+/// value is \c true if and only if it contains a valid RRClass object.
+/// And, if it contains a valid RRClass object, its value is accessible
+/// using \c operator*, just like a bare pointer to \c RRClass.
+typedef boost::optional<RRClass> MaybeRRClass;
+
 ///
 /// \brief A standard DNS module exception that is thrown if an RRClass object
 /// is being constructed from an unrecognized string.
@@ -123,8 +135,8 @@ public:
     /// If the given string is not recognized as a valid representation of
     /// an RR class, an exception of class \c InvalidRRClass will be thrown.
     ///
-    /// \param classstr A string representation of the \c RRClass
-    explicit RRClass(const std::string& classstr);
+    /// \param class_str A string representation of the \c RRClass
+    explicit RRClass(const std::string& class_str);
     /// Constructor from wire-format data.
     ///
     /// The \c buffer parameter normally stores a complete DNS message
@@ -136,6 +148,45 @@ public:
     ///
     /// \param buffer A buffer storing the wire format data.
     explicit RRClass(isc::util::InputBuffer& buffer);
+
+    /// A separate factory of RRClass from text.
+    ///
+    /// This static method is similar to the constructor that takes a
+    /// string object, but works as a factory and reports parsing
+    /// failure in the form of the return value.  Normally the
+    /// constructor version should suffice, but in some cases the caller
+    /// may have to expect mixture of valid and invalid input, and may
+    /// want to minimize the overhead of possible exception handling.
+    /// This version is provided for such purpose.
+    ///
+    /// For the format of the \c class_str argument, see the
+    /// <code>RRClass(const std::string&)</code> constructor.
+    ///
+    /// If the given text represents a valid RRClass, it returns a
+    /// \c MaybeRRClass object that stores a corresponding \c RRClass
+    /// object, which is accessible via \c operator*().  In this case
+    /// the returned object will be interpreted as \c true in a boolean
+    /// context.  If the given text does not represent a valid RRClass,
+    /// it returns a \c MaybeRRClass object which is interpreted as
+    /// \c false in a boolean context.
+    ///
+    /// One main purpose of this function is to minimize the overhead
+    /// when the given text does not represent a valid RR class.  For
+    /// this reason this function intentionally omits the capability of
+    /// delivering a detailed reason for the parse failure, such as in the
+    /// \c want() string when exception is thrown from the constructor
+    /// (it will internally require a creation of string object, which
+    /// is relatively expensive).  If such detailed information is
+    /// necessary, the constructor version should be used to catch the
+    /// resulting exception.
+    ///
+    /// This function never throws the \c InvalidRRClass exception.
+    ///
+    /// \param class_str A string representation of the \c RRClass.
+    /// \return A MaybeRRClass object either storing an RRClass object
+    /// for the given text or a \c false value.
+    static MaybeRRClass createFromText(const std::string& class_str);
+
     ///
     /// We use the default copy constructor intentionally.
     //@}
diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc
index ac5823c..76a2e73 100644
--- a/src/lib/dns/rrclass.cc
+++ b/src/lib/dns/rrclass.cc
@@ -30,8 +30,11 @@ using namespace isc::util;
 namespace isc {
 namespace dns {
 
-RRClass::RRClass(const std::string& classstr) {
-    classcode_ = RRParamRegistry::getRegistry().textToClassCode(classstr);
+RRClass::RRClass(const std::string& class_str) {
+    if (!RRParamRegistry::getRegistry().textToClassCode(class_str, classcode_)) {
+        isc_throw(InvalidRRClass,
+                  "Unrecognized RR class string: " + class_str);
+    }
 }
 
 RRClass::RRClass(InputBuffer& buffer) {
@@ -56,6 +59,16 @@ RRClass::toWire(AbstractMessageRenderer& renderer) const {
     renderer.writeUint16(classcode_);
 }
 
+MaybeRRClass
+RRClass::createFromText(const string& class_str) {
+    uint16_t class_code;
+    if (RRParamRegistry::getRegistry().textToClassCode(class_str,
+                                                       class_code)) {
+        return (MaybeRRClass(class_code));
+    }
+    return (MaybeRRClass());
+}
+
 ostream&
 operator<<(ostream& os, const RRClass& rrclass) {
     os << rrclass.toText();
diff --git a/src/lib/dns/rrcollator.cc b/src/lib/dns/rrcollator.cc
new file mode 100644
index 0000000..4b12222
--- /dev/null
+++ b/src/lib/dns/rrcollator.cc
@@ -0,0 +1,110 @@
+// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+// include this first to check the header is self-contained.
+#include <dns/rrcollator.h>
+
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+
+#include <boost/bind.hpp>
+
+#include <algorithm>
+
+namespace isc {
+namespace dns {
+using namespace rdata;
+
+class RRCollator::Impl {
+public:
+    Impl(const AddRRsetCallback& callback) : callback_(callback) {}
+
+    void addRR(const Name& name, const RRClass& rrclass,
+               const RRType& rrtype, const RRTTL& rrttl,
+               const RdataPtr& rdata);
+
+    RRsetPtr current_rrset_;
+    AddRRsetCallback callback_;
+};
+
+namespace {
+inline bool
+isSameType(RRType type1, const ConstRdataPtr& rdata1,
+           const ConstRRsetPtr& rrset)
+{
+    if (type1 != rrset->getType()) {
+        return (false);
+    }
+    if (type1 == RRType::RRSIG()) {
+        RdataIteratorPtr rit = rrset->getRdataIterator();
+        return (dynamic_cast<const generic::RRSIG&>(*rdata1).typeCovered()
+                == dynamic_cast<const generic::RRSIG&>(
+                    rit->getCurrent()).typeCovered());
+    }
+    return (true);
+}
+}
+
+void
+RRCollator::Impl::addRR(const Name& name, const RRClass& rrclass,
+                        const RRType& rrtype, const RRTTL& rrttl,
+                        const RdataPtr& rdata)
+{
+    if (current_rrset_ && (!isSameType(rrtype, rdata, current_rrset_) ||
+                           current_rrset_->getClass() != rrclass ||
+                           current_rrset_->getName() != name)) {
+        callback_(current_rrset_);
+        current_rrset_.reset();
+    }
+
+    if (!current_rrset_) {
+        current_rrset_ = RRsetPtr(new RRset(name, rrclass, rrtype, rrttl));
+    } else if (current_rrset_->getTTL() != rrttl) {
+        // RRs with different TTLs are given.  Smaller TTL should win.
+        current_rrset_->setTTL(std::min(current_rrset_->getTTL(), rrttl));
+    }
+    current_rrset_->addRdata(rdata);
+}
+
+RRCollator::RRCollator(const AddRRsetCallback& callback) :
+    impl_(new Impl(callback))
+{}
+
+RRCollator::~RRCollator() {
+    delete impl_;
+}
+
+AddRRCallback
+RRCollator::getCallback() {
+    return (boost::bind(&RRCollator::Impl::addRR, this->impl_,
+                        _1, _2, _3, _4, _5));
+}
+
+void
+RRCollator::flush() {
+    if (impl_->current_rrset_) {
+        impl_->callback_(impl_->current_rrset_);
+        impl_->current_rrset_.reset();
+    }
+}
+
+} // end namespace dns
+} // end namespace isc
diff --git a/src/lib/dns/rrcollator.h b/src/lib/dns/rrcollator.h
new file mode 100644
index 0000000..3a9e0aa
--- /dev/null
+++ b/src/lib/dns/rrcollator.h
@@ -0,0 +1,133 @@
+// 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 RRCOLLATOR_H
+#define RRCOLLATOR_H 1
+
+#include <dns/master_loader_callbacks.h>
+#include <dns/rrset.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/function.hpp>
+
+namespace isc {
+namespace dns {
+
+/// \brief A converter from a stream of RRs to a stream of collated RRsets
+///
+/// This class is mainly intended to be a helper used as an adaptor for
+/// user applications of the \c MasterLoader class; it works as a callback
+/// for \c MasterLoader, buffers given RRs from the loader, collating
+/// consecutive RRs that belong to the same RRset (ones having the same
+/// owner name, RR type and class), and produces a stream of RRsets through
+/// its own callback.  RRSIGs are also separated if their type covered fields
+/// have different values even if the owner name and RR class are the same.
+///
+/// It also "normalizes" TTLs of the RR; if collated RRs have different TTLs,
+/// this class guarantees that the TTL of the resulting RRsets has the
+/// smallest TTL among them.
+///
+/// The conversion will be useful for applications of \c MasterLoader because
+/// many of this library have interfaces that take an RRset object (or
+/// a pointer to it).  Note, however, that this class doesn't guarantee that
+/// all RRs that would belong to the same RRset are collated into the same
+/// single RRset.  In fact, it can only collate RRs that are consecutive
+/// in the original stream; once it encounters an RR of a different RRset,
+/// any subsequent RRs of the previous RRset will form a separate RRset object.
+///
+/// This class is non-copyable; it's partially for the convenience of internal
+/// implementation details, but it actually doesn't make sense to copy
+/// an object of this class, if not harmful, for the intended usage of
+/// the class.
+class RRCollator : boost::noncopyable {
+public:
+    /// \brief Callback functor type for \c RRCollator.
+    ///
+    /// This type of callback is given to an \c RRCollator object on its
+    /// construction, and will be called for each collated RRset built in
+    /// the \c RRCollator.
+    ///
+    /// \param rrset The collated RRset.
+    typedef boost::function<void(const RRsetPtr& rrset)> AddRRsetCallback;
+
+    /// \brief Constructor.
+    ///
+    /// \throw std::bad_alloc Internal memory allocation fails.  This should
+    /// be very rare.
+    ///
+    /// \param callback The callback functor to be called for each collated
+    /// RRset.
+    RRCollator(const AddRRsetCallback& callback);
+
+    /// \brief Destructor.
+    ///
+    /// It only performs trivial internal cleanup.  In particular, even if
+    /// it still has a buffered RRset it will be simply discarded.  This is
+    /// because the given callback could throw an exception, and it's
+    /// impossible to predict how this class is used (to see if it's a very
+    /// rare case where propagating an exception from a destructor is
+    /// justified).  Instead, the application needs to make sure that
+    /// \c flush() is called before the object of this class is destroyed.
+    ///
+    /// \throw None
+    ~RRCollator();
+
+    /// \brief Call the callback on the remaining RRset, if any.
+    ///
+    /// This method is expected to be called that it's supposed all RRs have
+    /// been passed to this class object.  Since there is no explicit
+    /// indicator of the end of the stream, the user of this class needs to
+    /// explicitly call this method to call the callback for the last buffered
+    /// RRset (see also the destructor's description).
+    ///
+    /// If there is no buffered RRset, this method does nothing.  It can happen
+    /// if it's called without receiving any RRs, or called more than once.
+    ///
+    /// It propagates any exception thrown from the callback; otherwise it
+    /// doesn't throw anything.
+    void flush();
+
+    /// \brief Return \c MasterLoader compatible callback.
+    ///
+    /// This method returns a functor in the form of \c AddRRCallback
+    /// that works as an adaptor between \c MasterLoader and an application
+    /// that needs to get a stream of RRsets.  When the returned callback
+    /// is called, this \c RRCollator object accepts the corresponding RR,
+    /// and collates it with other RRs of the same RRset if necessary.
+    /// Every time the \c RRCollator object encounters an RR of a different
+    /// RRset, it calls the callback passed to the constructor with the RRset
+    /// built so far.
+    ///
+    /// Like \c flush(), this \c AddRRCallback functor propagates any exception
+    /// thrown from the callback.
+    ///
+    /// This method is expected to be called only once for a given
+    /// \c RRCollator object.  It doesn't prohibit duplicate calls, but
+    /// returned functor objects internally refer to the same \c RRCollator
+    /// object, and calling the both callbacks randomly will just cause
+    /// confusion.
+    AddRRCallback getCallback();
+
+private:
+    class Impl;
+    Impl* impl_;
+};
+
+} // namespace dns
+} // namespace isc
+#endif  // RRCOLLATOR_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc
index 16ec23c..5960759 100644
--- a/src/lib/dns/rrparamregistry-placeholder.cc
+++ b/src/lib/dns/rrparamregistry-placeholder.cc
@@ -421,14 +421,15 @@ removeParam(uint16_t code, MC& codemap, MS& stringmap) {
     return (false);
 }
 
-template <typename PT, typename MS, typename ET>
-inline uint16_t
-textToCode(const string& code_str, MS& stringmap) {
+template <typename PT, typename MS>
+inline bool
+textToCode(const string& code_str, MS& stringmap, uint16_t& ret_code) {
     typename MS::const_iterator found;
 
     found = stringmap.find(code_str);
     if (found != stringmap.end()) {
-        return (found->second->code_);
+        ret_code = found->second->code_;
+        return (true);
     }
 
     size_t l = code_str.size();
@@ -441,10 +442,12 @@ textToCode(const string& code_str, MS& stringmap) {
                                           l - PT::UNKNOWN_PREFIXLEN()));
         iss >> dec >> code;
         if (iss.rdstate() == ios::eofbit && code <= PT::MAX_CODE) {
-            return (code);
+            ret_code = code;
+            return (true);
         }
     }
-    isc_throw(ET, "Unrecognized RR parameter string: " + code_str);
+
+    return (false);
 }
 
 template <typename PT, typename MC>
@@ -475,10 +478,12 @@ RRParamRegistry::removeType(uint16_t code) {
                                                      impl_->str2typemap));
 }
 
-uint16_t
-RRParamRegistry::textToTypeCode(const string& type_string) const {
-    return (textToCode<RRTypeParam, StrRRTypeMap,
-            InvalidRRType>(type_string, impl_->str2typemap));
+bool
+RRParamRegistry::textToTypeCode(const string& type_string,
+                                uint16_t& type_code) const
+{
+    return (textToCode<RRTypeParam, StrRRTypeMap>
+            (type_string, impl_->str2typemap, type_code));
 }
 
 string
@@ -499,10 +504,12 @@ RRParamRegistry::removeClass(uint16_t code) {
                                                        impl_->str2classmap));
 }
 
-uint16_t
-RRParamRegistry::textToClassCode(const string& class_string) const {
-    return (textToCode<RRClassParam, StrRRClassMap,
-            InvalidRRClass>(class_string, impl_->str2classmap));
+bool
+RRParamRegistry::textToClassCode(const string& class_string,
+                                 uint16_t& class_code) const
+{
+    return (textToCode<RRClassParam, StrRRClassMap>
+            (class_string, impl_->str2classmap, class_code));
 }
 
 string
diff --git a/src/lib/dns/rrparamregistry.h b/src/lib/dns/rrparamregistry.h
index 56ae981..bf86436 100644
--- a/src/lib/dns/rrparamregistry.h
+++ b/src/lib/dns/rrparamregistry.h
@@ -384,16 +384,22 @@ public:
     /// \brief Convert a textual representation of an RR type to the
     /// corresponding 16-bit integer code.
     ///
-    /// This method searches the \c RRParamRegistry for the mapping from the
-    /// given textual representation of RR type to the corresponding integer
-    /// code.  If a mapping is found, it returns the associated type code;
-    /// otherwise, if the given string is in the form of "TYPEnnnn", it returns
-    /// the corresponding number as the type code; otherwise, it throws an
-    /// exception of class \c InvalidRRType.
+    /// This method searches the \c RRParamRegistry for the mapping from
+    /// the given textual representation of RR type to the corresponding
+    /// integer code. If a mapping is found, it returns true with the
+    /// associated type code in \c type_code; otherwise, if the given
+    /// string is in the form of "TYPEnnnn", it returns true with the
+    /// corresponding number as the type code in \c type_code;
+    /// otherwise, it returns false and \c type_code is untouched.
+    ///
+    /// It returns \c false and avoids throwing an exception in the case
+    /// of an error to avoid the exception overhead in some situations.
     ///
     /// \param type_string The textual representation of the RR type.
-    /// \return The RR type code for \c type_string.
-    uint16_t textToTypeCode(const std::string& type_string) const;
+    /// \param type_code Returns the RR type code in this argument.
+    /// \return true if conversion is successful, false otherwise.
+    bool textToTypeCode(const std::string& type_string,
+                        uint16_t& type_code) const;
 
     /// \brief Convert type code into its textual representation.
     ///
@@ -415,16 +421,23 @@ public:
     /// \brief Convert a textual representation of an RR class to the
     /// corresponding 16-bit integer code.
     ///
-    /// This method searches the \c RRParamRegistry for the mapping from the
-    /// given textual representation of RR class to the corresponding integer
-    /// code.  If a mapping is found, it returns the associated class code;
-    /// otherwise, if the given string is in the form of "CLASSnnnn", it returns
-    /// the corresponding number as the class code; otherwise, it throws an
-    /// exception of class \c InvalidRRClass.
+    /// This method searches the \c RRParamRegistry for the mapping from
+    /// the given textual representation of RR class to the
+    /// corresponding integer code.  If a mapping is found, it returns
+    /// true with the associated class code in \c class_code; otherwise,
+    /// if the given string is in the form of "CLASSnnnn", it returns
+    /// true with the corresponding number as the class code in
+    /// \c class_code; otherwise, it returns false and \c class_code is
+    /// untouched.
+    ///
+    /// It returns \c false and avoids throwing an exception in the case
+    /// of an error to avoid the exception overhead in some situations.
     ///
     /// \param class_string The textual representation of the RR class.
-    /// \return The RR class code for \c class_string.
-    uint16_t textToClassCode(const std::string& class_string) const;
+    /// \param class_code Returns the RR class code in this argument.
+    /// \return true if conversion is successful, false otherwise.
+    bool textToClassCode(const std::string& class_string,
+                         uint16_t& class_code) const;
 
     /// \brief Convert class code into its textual representation.
     ///
diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc
index c47ec1f..e7f8441 100644
--- a/src/lib/dns/rrttl.cc
+++ b/src/lib/dns/rrttl.cc
@@ -57,9 +57,14 @@ Unit units[] = {
 namespace isc {
 namespace dns {
 
-RRTTL::RRTTL(const std::string& ttlstr) {
+namespace {
+bool
+parseTTLString(const string& ttlstr, uint32_t& ttlval, string* error_txt) {
     if (ttlstr.empty()) {
-        isc_throw(InvalidRRTTL, "Empty TTL string");
+        if (error_txt != NULL) {
+            *error_txt = "Empty TTL string";
+        }
+        return (false);
     }
     // We use a larger data type during the computation. This is because
     // some compilers don't fail when out of range, so we check the range
@@ -80,8 +85,10 @@ RRTTL::RRTTL(const std::string& ttlstr) {
             if (unit == end) {
                 if (units_mode) {
                     // We had some units before. The last one is missing unit.
-                    isc_throw(InvalidRRTTL, "Missing the last unit: " <<
-                              ttlstr);
+                    if (error_txt != NULL) {
+                        *error_txt = "Missing the last unit: " + ttlstr;
+                    }
+                    return (false);
                 } else {
                     // Case without any units at all. Just convert and store
                     // it.
@@ -102,12 +109,18 @@ RRTTL::RRTTL(const std::string& ttlstr) {
                 }
             }
             if (!found) {
-                isc_throw(InvalidRRTTL, "Unknown unit used: " << *unit <<
-                          " in: " << ttlstr);
+                if (error_txt != NULL) {
+                    *error_txt = "Unknown unit used: " +
+                        boost::lexical_cast<string>(*unit) + " in: " + ttlstr;
+                }
+                return (false);
             }
             // Now extract the number.
             if (unit == pos) {
-                isc_throw(InvalidRRTTL, "Missing number in TTL: " << ttlstr);
+                if (error_txt != NULL) {
+                    *error_txt = "Missing number in TTL: " + ttlstr;
+                }
+                return (false);
             }
             const int64_t value = boost::lexical_cast<int64_t>(string(pos,
                                                                       unit));
@@ -118,21 +131,48 @@ RRTTL::RRTTL(const std::string& ttlstr) {
             // there's no need to continue).
             if (value < 0 || value > 0xffffffff || val < 0 ||
                 val > 0xffffffff) {
-                isc_throw(InvalidRRTTL, "Part of TTL out of range: " <<
-                          ttlstr);
+                if (error_txt != NULL) {
+                    *error_txt = "Part of TTL out of range: "  + ttlstr;
+                }
+                return (false);
             }
             // Move to after the unit.
             pos = unit + 1;
         }
     } catch (const boost::bad_lexical_cast&) {
-        isc_throw(InvalidRRTTL, "invalid TTL: " << ttlstr);
+        if (error_txt != NULL) {
+            *error_txt = "invalid TTL: " + ttlstr;
+        }
+        return (false);
     }
 
     if (val >= 0 && val <= 0xffffffff) {
-        ttlval_ = val;
+        ttlval = val;
     } else {
-        isc_throw(InvalidRRTTL, "TTL out of range: " << ttlstr);
+        if (error_txt != NULL) {
+            *error_txt = "TTL out of range: " + ttlstr;
+        }
+        return (false);
+    }
+
+    return (true);
+}
+}
+
+RRTTL::RRTTL(const std::string& ttlstr) {
+    string error_txt;
+    if (!parseTTLString(ttlstr, ttlval_, &error_txt)) {
+        isc_throw(InvalidRRTTL, error_txt);
+    }
+}
+
+MaybeRRTTL
+RRTTL::createFromText(const string& ttlstr) {
+    uint32_t ttlval;
+    if (parseTTLString(ttlstr, ttlval, NULL)) {
+        return (MaybeRRTTL(ttlval));
     }
+    return (MaybeRRTTL());
 }
 
 RRTTL::RRTTL(InputBuffer& buffer) {
diff --git a/src/lib/dns/rrttl.h b/src/lib/dns/rrttl.h
index 5acd3b1..23d57f4 100644
--- a/src/lib/dns/rrttl.h
+++ b/src/lib/dns/rrttl.h
@@ -15,10 +15,12 @@
 #ifndef RRTTL_H
 #define RRTTL_H 1
 
-#include <stdint.h>
-
 #include <exceptions/exceptions.h>
 
+#include <boost/optional.hpp>
+
+#include <stdint.h>
+
 namespace isc {
 namespace util {
 class InputBuffer;
@@ -30,6 +32,16 @@ namespace dns {
 // forward declarations
 class AbstractMessageRenderer;
 
+class RRTTL;                    // forward declaration to define MaybeRRTTL
+
+/// \brief A shortcut for a compound type to represent RRTTL-or-not.
+///
+/// A value of this type can be interpreted in a boolean context, whose
+/// value is \c true if and only if it contains a valid RRTTL object.
+/// And, if it contains a valid RRTTL object, its value is accessible
+/// using \c operator*, just like a bare pointer to \c RRTTL.
+typedef boost::optional<RRTTL> MaybeRRTTL;
+
 ///
 /// \brief A standard DNS module exception that is thrown if an RRTTL object
 /// is being constructed from an unrecognized string.
@@ -61,7 +73,7 @@ public:
 class RRTTL {
 public:
     ///
-    /// \name Constructors and Destructor
+    /// \name Constructors, Factory and Destructor
     ///
     /// Note: We use the default copy constructor and the default copy
     /// assignment operator intentionally.
@@ -72,6 +84,7 @@ public:
     ///
     /// \param ttlval An 32-bit integer of the RRTTL.
     explicit RRTTL(uint32_t ttlval) : ttlval_(ttlval) {}
+
     /// Constructor from a string.
     ///
     /// It accepts either a decimal number, specifying number of seconds. Or,
@@ -87,6 +100,7 @@ public:
     /// \throw InvalidRRTTL in case the string is not recognized as valid
     ///     TTL representation.
     explicit RRTTL(const std::string& ttlstr);
+
     /// Constructor from wire-format data.
     ///
     /// The \c buffer parameter normally stores a complete DNS message
@@ -98,6 +112,39 @@ public:
     ///
     /// \param buffer A buffer storing the wire format data.
     explicit RRTTL(isc::util::InputBuffer& buffer);
+
+    /// A separate factory of RRTTL from text.
+    ///
+    /// This static method is similar to the constructor that takes a string
+    /// object, but works as a factory and reports parsing failure in the
+    /// form of the return value.  Normally the constructor version should
+    /// suffice, but in some cases the caller may have to expect mixture of
+    /// valid and invalid input, and may want to minimize the overhead of
+    /// possible exception handling.   This version is provided for such
+    /// purpose.
+    ///
+    /// If the given text represents a valid RRTTL, it returns a \c MaybeRRTTL
+    /// object that stores a corresponding \c RRTTL object, which is
+    /// accessible via \c operator*().  In this case the returned object will
+    /// be interpreted as \c true in a boolean context.  If the given text
+    /// does not represent a valid RRTTL, it returns a \c MaybeRRTTL object
+    /// which is interpreted as \c false in a boolean context.
+    ///
+    /// One main purpose of this function is to minimize the overhead
+    /// when the given text does not represent a valid RR TTL.  For this
+    /// reason this function intentionally omits the capability of delivering
+    /// a detailed reason for the parse failure, such as in the \c want()
+    /// string when exception is thrown from the constructor (it will
+    /// internally require a creation of string object, which is relatively
+    /// expensive).  If such detailed information is necessary, the constructor
+    /// version should be used to catch the resulting exception.
+    ///
+    /// This function never throws the \c InvalidRRTTL exception.
+    ///
+    /// \param ttlstr A string representation of the \c RRTTL.
+    /// \return An MaybeRRTTL object either storing an RRTTL object for
+    /// the given text or a \c false value.
+    static MaybeRRTTL createFromText(const std::string& ttlstr);
     ///
     //@}
 
@@ -236,6 +283,22 @@ public:
     { return (ttlval_ > other.ttlval_); }
     //@}
 
+    ///
+    /// \name Protocol constants
+    ///
+    //@{
+    /// \brief The TTL of the max allowable value, per RFC2181 Section 8.
+    ///
+    /// The max value is the largest unsigned 31 bit integer, 2^31-1.
+    ///
+    /// \note At the moment an RRTTL object can have a value larger than
+    /// this limit.  We may revisit it in a future version.
+    static const RRTTL& MAX_TTL() {
+        static const RRTTL max_ttl(0x7fffffff);
+        return (max_ttl);
+    }
+    //@}
+
 private:
     uint32_t ttlval_;
 };
diff --git a/src/lib/dns/rrtype.cc b/src/lib/dns/rrtype.cc
index 4ef4e67..84892bf 100644
--- a/src/lib/dns/rrtype.cc
+++ b/src/lib/dns/rrtype.cc
@@ -31,8 +31,11 @@ using isc::dns::RRType;
 namespace isc {
 namespace dns {
 
-RRType::RRType(const std::string& typestr) {
-    typecode_ = RRParamRegistry::getRegistry().textToTypeCode(typestr);
+RRType::RRType(const std::string& type_str) {
+    if (!RRParamRegistry::getRegistry().textToTypeCode(type_str, typecode_)) {
+        isc_throw(InvalidRRType,
+                  "Unrecognized RR type string: " + type_str);
+    }
 }
 
 RRType::RRType(InputBuffer& buffer) {
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 5f2ae5c..df42720 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -33,6 +33,7 @@ run_unittests_SOURCES += name_unittest.cc
 run_unittests_SOURCES += nsec3hash_unittest.cc
 run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc
 run_unittests_SOURCES += rrttl_unittest.cc
+run_unittests_SOURCES += rrcollator_unittest.cc
 run_unittests_SOURCES += opcode_unittest.cc
 run_unittests_SOURCES += rcode_unittest.cc
 run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
diff --git a/src/lib/dns/tests/master_lexer_state_unittest.cc b/src/lib/dns/tests/master_lexer_state_unittest.cc
index 846c4c2..2be43db 100644
--- a/src/lib/dns/tests/master_lexer_state_unittest.cc
+++ b/src/lib/dns/tests/master_lexer_state_unittest.cc
@@ -190,13 +190,16 @@ TEST_F(MasterLexerStateTest, unbalancedParentheses) {
 }
 
 TEST_F(MasterLexerStateTest, startToComment) {
-    // Begin with 'start', skip space, then encounter a comment.  Skip
+    // Begin with 'start', detect space, then encounter a comment.  Skip
     // the rest of the line, and recognize the new line.  Note that the
     // second ';' is simply ignored.
     ss << "  ;a;\n";
     ss << ";a;";           // Likewise, but the comment ends with EOF.
     lexer.pushSource(ss);
 
+    // Initial whitespace (asked for in common_options)
+    EXPECT_EQ(s_null, State::start(lexer, common_options));
+    EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType());
     // Comment ending with EOL
     EXPECT_EQ(s_null, State::start(lexer, common_options));
     EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType());
diff --git a/src/lib/dns/tests/master_lexer_unittest.cc b/src/lib/dns/tests/master_lexer_unittest.cc
index 42ed3fb..62ad79d 100644
--- a/src/lib/dns/tests/master_lexer_unittest.cc
+++ b/src/lib/dns/tests/master_lexer_unittest.cc
@@ -238,10 +238,8 @@ TEST_F(MasterLexerTest, ungetToken) {
 // Check ungetting token without overriding the start method. We also
 // check it works well with changing options between the calls.
 TEST_F(MasterLexerTest, ungetRealOptions) {
-    ss << "\n    \n";
+    ss << "    \n";
     lexer.pushSource(ss);
-    // Skip the first newline
-    EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType());
 
     // If we call it the usual way, it skips up to the newline and returns
     // it
@@ -254,6 +252,22 @@ TEST_F(MasterLexerTest, ungetRealOptions) {
               lexer.getNextToken(MasterLexer::INITIAL_WS).getType());
 }
 
+// Check the initial whitespace is found even in the first line of included
+// file
+TEST_F(MasterLexerTest, includeAndInitialWS) {
+    ss << "    \n";
+    lexer.pushSource(ss);
+
+    stringstream ss2;
+    ss2 << "    \n";
+
+    EXPECT_EQ(MasterToken::INITIAL_WS,
+              lexer.getNextToken(MasterLexer::INITIAL_WS).getType());
+    lexer.pushSource(ss2);
+    EXPECT_EQ(MasterToken::INITIAL_WS,
+              lexer.getNextToken(MasterLexer::INITIAL_WS).getType());
+}
+
 // Test only one token can be ungotten
 TEST_F(MasterLexerTest, ungetTwice) {
     ss << "\n";
diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc
index bfbf9fa..051c662 100644
--- a/src/lib/dns/tests/master_loader_unittest.cc
+++ b/src/lib/dns/tests/master_loader_unittest.cc
@@ -17,11 +17,14 @@
 #include <dns/rrtype.h>
 #include <dns/rrset.h>
 #include <dns/rrclass.h>
+#include <dns/rrttl.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
 
 #include <gtest/gtest.h>
+
 #include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
 #include <boost/scoped_ptr.hpp>
 
 #include <string>
@@ -35,6 +38,7 @@ using std::string;
 using std::list;
 using std::stringstream;
 using std::endl;
+using boost::lexical_cast;
 
 namespace {
 class MasterLoaderTest : public ::testing::Test {
@@ -46,6 +50,11 @@ public:
                                &warnings_, _1, _2, _3))
     {}
 
+    void TearDown() {
+        // Check there are no more RRs we didn't expect
+        EXPECT_TRUE(rrsets_.empty());
+    }
+
     /// Concatenate file, line, and reason, and add it to either errors
     /// or warnings
     void callback(vector<string>* target, const std::string& file, size_t line,
@@ -102,7 +111,8 @@ public:
 
     // Check the next RR in the ones produced by the loader
     // Other than passed arguments are checked to be the default for the tests
-    void checkRR(const string& name, const RRType& type, const string& data) {
+    void checkRR(const string& name, const RRType& type, const string& data,
+                 const RRTTL& rrttl = RRTTL(3600)) {
         ASSERT_FALSE(rrsets_.empty());
         RRsetPtr current = rrsets_.front();
         rrsets_.pop_front();
@@ -110,6 +120,7 @@ public:
         EXPECT_EQ(Name(name), current->getName());
         EXPECT_EQ(type, current->getType());
         EXPECT_EQ(RRClass::IN(), current->getClass());
+        EXPECT_EQ(rrttl, current->getTTL());
         ASSERT_EQ(1, current->getRdataCount());
         EXPECT_EQ(0, isc::dns::rdata::createRdata(type, RRClass::IN(), data)->
                   compare(current->getRdataIterator()->getCurrent()));
@@ -121,6 +132,11 @@ public:
                 "1234 3600 1800 2419200 7200");
         checkRR("example.org", RRType::NS(), "ns1.example.org.");
         checkRR("www.example.org", RRType::A(), "192.0.2.1");
+        checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
+    }
+
+    void checkARR(const string& name) {
+        checkRR(name, RRType::A(), "192.0.2.1");
     }
 
     MasterLoaderCallbacks callbacks_;
@@ -179,6 +195,66 @@ TEST_F(MasterLoaderTest, include) {
     }
 }
 
+// A commonly used helper to check callback message.
+void
+checkCallbackMessage(const string& actual_msg, const string& expected_msg,
+                     size_t expected_line) {
+    // The actual message should begin with the expected message.
+    EXPECT_EQ(0, actual_msg.find(expected_msg)) << "actual message: " <<
+                                                actual_msg << " expected: " <<
+                                                expected_msg;
+
+    // and it should end with "...:<line_num>]"
+    const string line_desc = ":" + lexical_cast<string>(expected_line) + "]";
+    EXPECT_EQ(actual_msg.size() - line_desc.size(),
+              actual_msg.find(line_desc)) << "Expected on line " <<
+        expected_line;
+}
+
+TEST_F(MasterLoaderTest, origin) {
+    // Various forms of the directive
+    const char* origins[] = {
+        "$origin",
+        "$ORIGIN",
+        "$Origin",
+        "$OrigiN",
+        "\"$ORIGIN\"",
+        NULL
+    };
+    for (const char** origin = origins; *origin != NULL; ++origin) {
+        SCOPED_TRACE(*origin);
+
+        clear();
+        const string directive = *origin;
+        const string input =
+            "@  1H  IN  A   192.0.2.1\n" +
+            directive + " sub.example.org.\n"
+            "\"www\"    1H  IN  A   192.0.2.1\n" +
+            // Relative name in the origin
+            directive + " relative\n"
+            "@  1H  IN  A   192.0.2.1\n"
+            // Origin is _not_ used here (absolute name)
+            "noorigin.example.org.  60M IN  A   192.0.2.1\n";
+        stringstream ss(input);
+        setLoader(ss, Name("example.org."), RRClass::IN(),
+                  MasterLoader::MANY_ERRORS);
+
+        loader_->load();
+        EXPECT_TRUE(loader_->loadedSucessfully());
+        EXPECT_TRUE(errors_.empty());
+        // There's a relative origin in it, we warn about that.
+        EXPECT_EQ(1, warnings_.size());
+        checkCallbackMessage(warnings_.at(0),
+                             "The new origin is relative, did you really mean "
+                             "relative.sub.example.org.?", 4);
+
+        checkARR("example.org");
+        checkARR("www.sub.example.org");
+        checkARR("relative.sub.example.org");
+        checkARR("noorigin.example.org");
+    }
+}
+
 // Test the source is correctly popped even after error
 TEST_F(MasterLoaderTest, popAfterError) {
     const string include_str = "$include " TEST_DATA_SRCDIR
@@ -244,6 +320,7 @@ TEST_F(MasterLoaderTest, incrementalLoad) {
     EXPECT_TRUE(warnings_.empty());
 
     checkRR("www.example.org", RRType::A(), "192.0.2.1");
+    checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
 }
 
 // Try loading from file that doesn't exist. There should be single error
@@ -267,29 +344,90 @@ TEST_F(MasterLoaderTest, invalidFile) {
 
 struct ErrorCase {
     const char* const line;    // The broken line in master file
+    const char* const reason;  // If non NULL, the reason string
     const char* const problem; // Description of the problem for SCOPED_TRACE
 } const error_cases[] = {
-    { "www...   3600    IN  A   192.0.2.1", "Invalid name" },
-    { "www      FORTNIGHT   IN  A   192.0.2.1", "Invalid TTL" },
-    { "www      3600    XX  A   192.0.2.1", "Invalid class" },
-    { "www      3600    IN  A   bad_ip", "Invalid Rdata" },
-    { "www      3600    IN", "Unexpected EOLN" },
-    { "www      3600    CH  TXT nothing", "Class mismatch" },
-    { "www      \"3600\"  IN  A   192.0.2.1", "Quoted TTL" },
-    { "www      3600    \"IN\"  A   192.0.2.1", "Quoted class" },
-    { "www      3600    IN  \"A\"   192.0.2.1", "Quoted type" },
-    { "unbalanced)paren 3600    IN  A   192.0.2.1", "Token error 1" },
-    { "www  3600    unbalanced)paren    A   192.0.2.1", "Token error 2" },
+    { "www...   3600    IN  A   192.0.2.1", NULL, "Invalid name" },
+    { "www      FORTNIGHT   IN  A   192.0.2.1", NULL, "Invalid TTL" },
+    { "www      3600    XX  A   192.0.2.1", NULL, "Invalid class" },
+    { "www      3600    IN  A   bad_ip", NULL, "Invalid Rdata" },
+
+    // Parameter ordering errors
+    { "www      IN      A   3600 192.168.2.7",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Incorrect order of class, TTL and type" },
+    { "www      A       IN  3600 192.168.2.8",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Incorrect order of class, TTL and type" },
+    { "www      3600    A   IN   192.168.2.7",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Incorrect order of class, TTL and type" },
+    { "www      A       3600 IN  192.168.2.8",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Incorrect order of class, TTL and type" },
+
+    // Missing type and Rdata
+    { "www", "unexpected end of input", "Missing type and Rdata" },
+    { "www 3600", "unexpected end of input", "Missing type and Rdata" },
+    { "www IN", "unexpected end of input", "Missing type and Rdata" },
+    { "www 3600 IN", "unexpected end of input", "Missing type and Rdata" },
+    { "www IN 3600", "unexpected end of input", "Missing type and Rdata" },
+
+    // Missing Rdata
+    { "www A",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Missing Rdata" },
+    { "www 3600 A",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Missing Rdata" },
+    { "www IN A",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Missing Rdata" },
+    { "www 3600 IN A",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Missing Rdata" },
+    { "www IN 3600 A",
+      "createRdata from text failed: IN/A RDATA construction from text failed",
+      "Missing Rdata" },
+
+    { "www      3600    IN", NULL, "Unexpected EOLN" },
+    { "www      3600    CH  TXT nothing", "Class mismatch: CH vs. IN",
+      "Class mismatch" },
+    { "www      \"3600\"  IN  A   192.0.2.1", NULL, "Quoted TTL" },
+    { "www      3600    \"IN\"  A   192.0.2.1", NULL, "Quoted class" },
+    { "www      3600    IN  \"A\"   192.0.2.1", NULL, "Quoted type" },
+    { "unbalanced)paren 3600    IN  A   192.0.2.1", NULL, "Token error 1" },
+    { "www  3600    unbalanced)paren    A   192.0.2.1", NULL,
+      "Token error 2" },
     // Check the unknown directive. The rest looks like ordinary RR,
     // so we see the $ is actually special.
-    { "$UNKNOWN 3600    IN  A   192.0.2.1", "Unknown $ directive" },
-    { "$INCLUD " TEST_DATA_SRCDIR "/example.org", "Include too short" },
-    { "$INCLUDES " TEST_DATA_SRCDIR "/example.org", "Include too long" },
-    { "$INCLUDE", "Missing include path" },
-    { "$INCLUDE /file/not/found", "Include file not found" },
-    { "$INCLUDE /file/not/found and here goes bunch of garbage",
-        "Include file not found and garbage at the end of line" },
-    { NULL, NULL }
+    { "$UNKNOWN 3600    IN  A   192.0.2.1", NULL, "Unknown $ directive" },
+    { "$INCLUD " TEST_DATA_SRCDIR "/example.org", "Unknown directive 'INCLUD'",
+        "Include too short" },
+    { "$INCLUDES " TEST_DATA_SRCDIR "/example.org",
+        "Unknown directive 'INCLUDES'", "Include too long" },
+    { "$INCLUDE", "unexpected end of input", "Missing include path" },
+    // The following two error messages are system dependant, omitting
+    { "$INCLUDE /file/not/found", NULL, "Include file not found" },
+    { "$INCLUDE /file/not/found example.org. and here goes bunch of garbage",
+        NULL, "Include file not found and garbage at the end of line" },
+    { "$ORIGIN", "unexpected end of input", "Missing origin name" },
+    { "$ORIGIN invalid...name", "duplicate period in invalid...name",
+        "Invalid name for origin" },
+    { "$ORIGIN )brokentoken", "unbalanced parentheses",
+        "Broken token in origin" },
+    { "$ORIGIN example.org. garbage", "Extra tokens at the end of line",
+        "Garbage after origin" },
+    { "$ORIGI name.", "Unknown directive 'ORIGI'", "$ORIGIN too short" },
+    { "$ORIGINAL name.", "Unknown directive 'ORIGINAL'", "$ORIGIN too long" },
+    { "$TTL 100 extra-garbage", "Extra tokens at the end of line",
+      "$TTL with extra token" },
+    { "$TTL", "unexpected end of input", "missing TTL" },
+    { "$TTL No-ttl", "Unknown unit used: N in: No-ttl", "bad TTL" },
+    { "$TTL \"100\"", "invalid TTL: \"100\"", "bad TTL, quoted" },
+    { "$TT 100", "Unknown directive 'TT'", "bad directive, too short" },
+    { "$TTLLIKE 100", "Unknown directive 'TTLLIKE'", "bad directive, extra" },
+    { NULL, NULL, NULL }
 };
 
 // Test a broken zone is handled properly. We test several problems,
@@ -309,6 +447,9 @@ TEST_F(MasterLoaderTest, brokenZone) {
             EXPECT_THROW(loader_->load(), MasterLoaderError);
             EXPECT_FALSE(loader_->loadedSucessfully());
             EXPECT_EQ(1, errors_.size());
+            if (ec->reason != NULL) {
+                checkCallbackMessage(errors_.at(0), ec->reason, 2);
+            }
             EXPECT_TRUE(warnings_.empty());
 
             checkRR("example.org", RRType::SOA(), "ns1.example.org. "
@@ -363,7 +504,7 @@ TEST_F(MasterLoaderTest, includeWithGarbage) {
     // Include an origin (example.org) because we expect it to be handled
     // soon and we don't want it to break here.
     const string include_str("$INCLUDE " TEST_DATA_SRCDIR
-                             "/example.org example.org bunch of other stuff\n"
+                             "/example.org example.org. bunch of other stuff\n"
                              "www 3600 IN AAAA 2001:db8::1\n");
     stringstream zone_stream(include_str);
     setLoader(zone_stream, Name("example.org."), RRClass::IN(),
@@ -372,6 +513,7 @@ TEST_F(MasterLoaderTest, includeWithGarbage) {
     EXPECT_NO_THROW(loader_->load());
     EXPECT_FALSE(loader_->loadedSucessfully());
     ASSERT_EQ(1, errors_.size());
+    checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1);
     // It says something about extra tokens at the end
     EXPECT_NE(string::npos, errors_[0].find("Extra"));
     EXPECT_TRUE(warnings_.empty());
@@ -379,6 +521,289 @@ TEST_F(MasterLoaderTest, includeWithGarbage) {
     checkRR("www.example.org", RRType::AAAA(), "2001:db8::1");
 }
 
+// Check we error about garbage at the end of $ORIGIN line (but the line
+// works).
+TEST_F(MasterLoaderTest, originWithGarbage) {
+    const string origin_str = "$ORIGIN www.example.org. More garbage here\n"
+        "@  1H  IN  A   192.0.2.1\n";
+    stringstream ss(origin_str);
+    setLoader(ss, Name("example.org."), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    EXPECT_NO_THROW(loader_->load());
+    EXPECT_FALSE(loader_->loadedSucessfully());
+    ASSERT_EQ(1, errors_.size());
+    checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1);
+    EXPECT_TRUE(warnings_.empty());
+    checkARR("www.example.org");
+}
+
+// Test we can pass both file to include and the origin to switch
+TEST_F(MasterLoaderTest, includeAndOrigin) {
+    // First, switch origin to something else, so we can check it is
+    // switched back.
+    const string include_string = "$ORIGIN www.example.org.\n"
+        "@  1H  IN  A   192.0.2.1\n"
+        // Then include the file with data and switch origin back
+        "$INCLUDE " TEST_DATA_SRCDIR "/example.org example.org.\n"
+        // Another RR to see we fall back to the previous origin.
+        "www    1H  IN  A   192.0.2.1\n";
+    stringstream ss(include_string);
+    setLoader(ss, Name("example.org"), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    // Successfully load the data
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    EXPECT_TRUE(errors_.empty());
+    EXPECT_TRUE(warnings_.empty());
+    // And check it's the correct data
+    checkARR("www.example.org");
+    checkBasicRRs();
+    checkARR("www.www.example.org");
+}
+
+// Like above, but the origin after include is bogus. The whole line should
+// be rejected.
+TEST_F(MasterLoaderTest, includeAndBadOrigin) {
+    const string include_string =
+        "$INCLUDE " TEST_DATA_SRCDIR "/example.org example..org.\n"
+        // Another RR to see the switch survives after we exit include
+        "www    1H  IN  A   192.0.2.1\n";
+    stringstream ss(include_string);
+    setLoader(ss, Name("example.org"), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    loader_->load();
+    EXPECT_FALSE(loader_->loadedSucessfully());
+    EXPECT_EQ(1, errors_.size());
+    checkCallbackMessage(errors_.at(0), "duplicate period in example..org.",
+                         1);
+    EXPECT_TRUE(warnings_.empty());
+    // And check it's the correct data
+    checkARR("www.example.org");
+}
+
+// Check the origin doesn't get outside of the included file.
+TEST_F(MasterLoaderTest, includeOriginRestore) {
+    const string include_string =
+        "$INCLUDE " TEST_DATA_SRCDIR "/origincheck.txt\n"
+        "@  1H  IN  A   192.0.2.1\n";
+    stringstream ss(include_string);
+    setLoader(ss, Name("example.org"), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    // Successfully load the data
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    EXPECT_TRUE(errors_.empty());
+    EXPECT_TRUE(warnings_.empty());
+    // And check it's the correct data
+    checkARR("www.example.org");
+    checkARR("example.org");
+}
+
+// Check we restore the last name for initial whitespace when returning from
+// include. But we do produce a warning if there's one just ofter the include.
+TEST_F(MasterLoaderTest, includeAndInitialWS) {
+    const string include_string = "xyz  1H  IN  A   192.0.2.1\n"
+        "$INCLUDE " TEST_DATA_SRCDIR "/example.org\n"
+        "   1H  IN  A   192.0.2.1\n";
+    stringstream ss(include_string);
+    setLoader(ss, Name("example.org"), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    // Successfully load the data
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    EXPECT_TRUE(errors_.empty());
+    EXPECT_EQ(1, warnings_.size());
+    checkCallbackMessage(warnings_.at(0),
+                         "Owner name omitted around $INCLUDE, the result might "
+                         "not be as expected", 3);
+    checkARR("xyz.example.org");
+    checkBasicRRs();
+    checkARR("xyz.example.org");
+}
+
+// Test for "$TTL"
+TEST_F(MasterLoaderTest, ttlDirective) {
+    stringstream zone_stream;
+
+    // Set the default TTL with $TTL followed by an RR omitting the TTL
+    zone_stream << "$TTL 1800\nexample.org. IN A 192.0.2.1\n";
+    // $TTL can be quoted.  Also testing the case of $TTL being changed.
+    zone_stream << "\"$TTL\" 100\na.example.org. IN A 192.0.2.2\n";
+    // Extended TTL form is accepted.
+    zone_stream << "$TTL 1H\nb.example.org. IN A 192.0.2.3\n";
+    // Matching is case insensitive.
+    zone_stream << "$tTl 360\nc.example.org. IN A 192.0.2.4\n";
+    // Maximum allowable TTL
+    zone_stream << "$TTL 2147483647\nd.example.org. IN A 192.0.2.5\n";
+
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    checkRR("example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
+    checkRR("a.example.org", RRType::A(), "192.0.2.2", RRTTL(100));
+    checkRR("b.example.org", RRType::A(), "192.0.2.3", RRTTL(3600));
+    checkRR("c.example.org", RRType::A(), "192.0.2.4", RRTTL(360));
+    checkRR("d.example.org", RRType::A(), "192.0.2.5", RRTTL(2147483647));
+}
+
+TEST_F(MasterLoaderTest, ttlFromSOA) {
+    // No $TTL, and the SOA doesn't have an explicit TTL field.  Its minimum
+    // TTL field will be used as the RR's TTL, and it'll be used as the
+    // default TTL for others.
+    stringstream zone_stream("example.org. IN SOA . . 0 0 0 0 1800\n"
+                             "a.example.org. IN A 192.0.2.1\n");
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(1800));
+    checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
+
+    // The use of SOA minimum TTL should have caused a warning.
+    EXPECT_EQ(1, warnings_.size());
+    checkCallbackMessage(warnings_.at(0),
+                         "no TTL specified; using SOA MINTTL instead", 1);
+}
+
+TEST_F(MasterLoaderTest, ttlFromPrevious) {
+    // No available default TTL.  2nd and 3rd RR will use the TTL of the
+    // 1st RR.  This will result in a warning, but only for the first time.
+    stringstream zone_stream("a.example.org. 1800 IN A 192.0.2.1\n"
+                             "b.example.org. IN A 192.0.2.2\n"
+                             "c.example.org. IN A 192.0.2.3\n");
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
+    checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
+    checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(1800));
+
+    EXPECT_EQ(1, warnings_.size());
+    checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
+}
+
+TEST_F(MasterLoaderTest, RRParamsOrdering) {
+    // We test the order and existence of TTL, class and type. See
+    // MasterLoader::MasterLoaderImpl::parseRRParams() for ordering.
+
+    stringstream zone_stream;
+    // <TTL> <class> <type> <RDATA>
+    zone_stream << "a.example.org. 1800 IN A 192.0.2.1\n";
+    // <type> <RDATA>
+    zone_stream << "b.example.org. A 192.0.2.2\n";
+    // <class> <TTL> <type> <RDATA>
+    zone_stream << "c.example.org. IN 3600 A 192.0.2.3\n";
+    // <TTL> <type> <RDATA>
+    zone_stream << "d.example.org. 7200 A 192.0.2.4\n";
+    // <class> <type> <RDATA>
+    zone_stream << "e.example.org. IN A 192.0.2.5\n";
+
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800));
+    checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
+    checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(3600));
+    checkRR("d.example.org", RRType::A(), "192.0.2.4", RRTTL(7200));
+    checkRR("e.example.org", RRType::A(), "192.0.2.5", RRTTL(7200));
+
+    EXPECT_EQ(1, warnings_.size());
+    checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
+}
+
+TEST_F(MasterLoaderTest, ttlFromPreviousSOA) {
+    // Mixture of the previous two cases: SOA has explicit TTL, followed by
+    // an RR without an explicit TTL.  In this case the minimum TTL won't be
+    // recognized as the "default TTL".
+    stringstream zone_stream("example.org. 100 IN SOA . . 0 0 0 0 1800\n"
+                             "a.example.org. IN A 192.0.2.1\n");
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+
+    checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(100));
+    checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(100));
+
+    EXPECT_EQ(1, warnings_.size());
+    checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2);
+}
+
+TEST_F(MasterLoaderTest, ttlUnknown) {
+    // No available TTL is known for the first RR.
+    stringstream zone_stream("a.example.org. IN A 192.0.2.1\n");
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+    EXPECT_THROW(loader_->load(), MasterLoaderError);
+}
+
+TEST_F(MasterLoaderTest, ttlUnknownAndContinue) {
+    stringstream zone_stream("a.example.org. IN A 192.0.2.1\n"
+                             "b.example.org. 1800 IN A 192.0.2.2\n");
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    loader_->load();
+    EXPECT_FALSE(loader_->loadedSucessfully());
+    checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800));
+
+    EXPECT_TRUE(warnings_.empty());
+    EXPECT_EQ(1, errors_.size());
+    checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1);
+}
+
+TEST_F(MasterLoaderTest, ttlUnknownAndEOF) {
+    // Similar to the previous case, but the input will be abruptly terminated
+    // after the offending RR.  This will cause an additional warning.
+    stringstream zone_stream("a.example.org. IN A 192.0.2.1");
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    loader_->load();
+    EXPECT_FALSE(loader_->loadedSucessfully());
+    EXPECT_TRUE(rrsets_.empty());
+
+    EXPECT_EQ(1, errors_.size());
+    checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1);
+
+    // RDATA implementation can complain about it, too.  To be independent of
+    // its details, we focus on the very last warning.
+    EXPECT_FALSE(warnings_.empty());
+    checkCallbackMessage(*warnings_.rbegin(), "File does not end with newline",
+                         1);
+}
+
+TEST_F(MasterLoaderTest, ttlOverflow) {
+    stringstream zone_stream;
+    zone_stream << "example.org. IN SOA . . 0 0 0 0 2147483648\n";
+    zone_stream << "$TTL 3600\n"; // reset to an in-range value
+    zone_stream << "$TTL 2147483649\n" << "a.example.org. IN A 192.0.2.1\n";
+    zone_stream << "$TTL 3600\n"; // reset to an in-range value
+    zone_stream << "b.example.org. 2147483650 IN A 192.0.2.2\n";
+    setLoader(zone_stream, Name("example.org."), RRClass::IN(),
+              MasterLoader::DEFAULT);
+
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    EXPECT_EQ(3, rrsets_.size());
+
+    checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 2147483648", RRTTL(0));
+    checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(0));
+    checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(0));
+
+    EXPECT_EQ(4, warnings_.size());
+    checkCallbackMessage(warnings_.at(1),
+                         "TTL 2147483648 > MAXTTL, setting to 0 per RFC2181",
+                         1);
+    checkCallbackMessage(warnings_.at(2),
+                         "TTL 2147483649 > MAXTTL, setting to 0 per RFC2181",
+                         3);
+    checkCallbackMessage(warnings_.at(3),
+                         "TTL 2147483650 > MAXTTL, setting to 0 per RFC2181",
+                         6);
+}
+
 // Test the constructor rejects empty add callback.
 TEST_F(MasterLoaderTest, emptyCallback) {
     EXPECT_THROW(MasterLoader(TEST_DATA_SRCDIR "/example.org",
@@ -398,6 +823,9 @@ TEST_F(MasterLoaderTest, loadTwice) {
 
     loader_->load();
     EXPECT_THROW(loader_->load(), isc::InvalidOperation);
+    // Don't check them, they are not interesting, so suppress the error
+    // at TearDown
+    rrsets_.clear();
 }
 
 // Load 0 items should be rejected
@@ -419,11 +847,44 @@ TEST_F(MasterLoaderTest, noEOLN) {
 
     loader_->load();
     EXPECT_TRUE(loader_->loadedSucessfully());
-    EXPECT_TRUE(errors_.empty()) << errors_[0];
+    EXPECT_TRUE(errors_.empty());
     // There should be one warning about the EOLN
     EXPECT_EQ(1, warnings_.size());
     checkRR("example.org", RRType::SOA(), "ns1.example.org. "
             "admin.example.org. 1234 3600 1800 2419200 7200");
 }
 
+// Test it rejects when we don't have the previous name to use in place of
+// initial whitespace
+TEST_F(MasterLoaderTest, noPreviousName) {
+    const string input("    1H  IN  A   192.0.2.1\n");
+    stringstream ss(input);
+    setLoader(ss, Name("example.org."), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    loader_->load();
+    EXPECT_FALSE(loader_->loadedSucessfully());
+    EXPECT_EQ(1, errors_.size());
+    checkCallbackMessage(errors_.at(0), "No previous name to use in place of "
+                         "initial whitespace", 1);
+    EXPECT_TRUE(warnings_.empty());
+}
+
+// Check we warn if the first RR in an included file has omitted name
+TEST_F(MasterLoaderTest, previousInInclude) {
+    const string input("www 1H  IN  A   192.0.2.1\n"
+                       "$INCLUDE " TEST_DATA_SRCDIR "/omitcheck.txt\n");
+    stringstream ss(input);
+    setLoader(ss, Name("example.org"), RRClass::IN(),
+              MasterLoader::MANY_ERRORS);
+    loader_->load();
+    EXPECT_TRUE(loader_->loadedSucessfully());
+    EXPECT_TRUE(errors_.empty());
+    // There should be one warning about the EOLN
+    EXPECT_EQ(1, warnings_.size());
+    checkCallbackMessage(warnings_.at(0), "Owner name omitted around "
+                         "$INCLUDE, the result might not be as expected", 1);
+    checkARR("www.example.org");
+    checkARR("www.example.org");
+}
+
 }
diff --git a/src/lib/dns/tests/rdata_char_string_unittest.cc b/src/lib/dns/tests/rdata_char_string_unittest.cc
index 9d23622..44bc979 100644
--- a/src/lib/dns/tests/rdata_char_string_unittest.cc
+++ b/src/lib/dns/tests/rdata_char_string_unittest.cc
@@ -25,7 +25,8 @@
 using namespace isc::dns;
 using namespace isc::dns::rdata;
 using isc::dns::rdata::generic::detail::CharString;
-using isc::dns::rdata::generic::detail::strToCharString;
+using isc::dns::rdata::generic::detail::stringToCharString;
+using isc::dns::rdata::generic::detail::charStringToString;
 using isc::util::unittests::matchWireData;
 
 namespace {
@@ -60,19 +61,19 @@ createStringRegion(const std::string& str) {
 TEST_F(CharStringTest, normalConversion) {
     uint8_t tmp[3];             // placeholder for expected sequence
 
-    strToCharString(str_region, chstr);
+    stringToCharString(str_region, chstr);
     matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size());
 
     // Empty string
     chstr.clear();
-    strToCharString(createStringRegion(""), chstr);
+    stringToCharString(createStringRegion(""), chstr);
     tmp[0] = 0;
     matchWireData(tmp, 1, &chstr[0], chstr.size());
 
     // Possible largest char string
     chstr.clear();
     std::string long_str(255, 'x');
-    strToCharString(createStringRegion(long_str), chstr);
+    stringToCharString(createStringRegion(long_str), chstr);
     std::vector<uint8_t> expected;
     expected.push_back(255);    // len of char string
     expected.insert(expected.end(), long_str.begin(), long_str.end());
@@ -83,32 +84,32 @@ TEST_F(CharStringTest, normalConversion) {
     chstr.clear();
     long_str.at(254) = '\\';    // replace the last 'x' with '\'
     long_str.append("120");     // 'x' = 120
-    strToCharString(createStringRegion(long_str), chstr);
+    stringToCharString(createStringRegion(long_str), chstr);
     matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size());
 
     // Escaped '\'
     chstr.clear();
     tmp[0] = 1;
     tmp[1] = '\\';
-    strToCharString(createStringRegion("\\\\"), chstr);
+    stringToCharString(createStringRegion("\\\\"), chstr);
     matchWireData(tmp, 2, &chstr[0], chstr.size());
 
     // Boundary values for \DDD
     chstr.clear();
     tmp[0] = 1;
     tmp[1] = 0;
-    strToCharString(createStringRegion("\\000"), chstr);
+    stringToCharString(createStringRegion("\\000"), chstr);
     matchWireData(tmp, 2, &chstr[0], chstr.size());
 
     chstr.clear();
-    strToCharString(createStringRegion("\\255"), chstr);
+    stringToCharString(createStringRegion("\\255"), chstr);
     tmp[0] = 1;
     tmp[1] = 255;
     matchWireData(tmp, 2, &chstr[0], chstr.size());
 
     // Another digit follows DDD; it shouldn't cause confusion
     chstr.clear();
-    strToCharString(createStringRegion("\\2550"), chstr);
+    stringToCharString(createStringRegion("\\2550"), chstr);
     tmp[0] = 2;                 // string len is now 2
     tmp[2] = '0';
     matchWireData(tmp, 3, &chstr[0], chstr.size());
@@ -116,13 +117,13 @@ TEST_F(CharStringTest, normalConversion) {
 
 TEST_F(CharStringTest, badConversion) {
     // string cannot exceed 255 bytes
-    EXPECT_THROW(strToCharString(createStringRegion(std::string(256, 'a')),
-                                 chstr),
+    EXPECT_THROW(stringToCharString(createStringRegion(std::string(256, 'a')),
+                                    chstr),
                  CharStringTooLong);
 
     // input string ending with (non escaped) '\'
     chstr.clear();
-    EXPECT_THROW(strToCharString(createStringRegion("foo\\"), chstr),
+    EXPECT_THROW(stringToCharString(createStringRegion("foo\\"), chstr),
                  InvalidRdataText);
 }
 
@@ -130,18 +131,44 @@ TEST_F(CharStringTest, badDDD) {
     // Check various type of bad form of \DDD
 
     // Not a number
-    EXPECT_THROW(strToCharString(createStringRegion("\\1a2"), chstr),
+    EXPECT_THROW(stringToCharString(createStringRegion("\\1a2"), chstr),
                  InvalidRdataText);
-    EXPECT_THROW(strToCharString(createStringRegion("\\12a"), chstr),
+    EXPECT_THROW(stringToCharString(createStringRegion("\\12a"), chstr),
                  InvalidRdataText);
 
     // Not in the range of uint8_t
-    EXPECT_THROW(strToCharString(createStringRegion("\\256"), chstr),
+    EXPECT_THROW(stringToCharString(createStringRegion("\\256"), chstr),
                  InvalidRdataText);
 
     // Short buffer
-    EXPECT_THROW(strToCharString(createStringRegion("\\42"), chstr),
+    EXPECT_THROW(stringToCharString(createStringRegion("\\42"), chstr),
                  InvalidRdataText);
 }
 
+const struct TestData {
+    const char *data;
+    const char *expected;
+} conversion_data[] = {
+    {"Test\"Test", "Test\\\"Test"},
+    {"Test;Test", "Test\\;Test"},
+    {"Test\\Test", "Test\\\\Test"},
+    {"Test\x1fTest", "Test\\031Test"},
+    {"Test ~ Test", "Test ~ Test"},
+    {"Test\x7fTest", "Test\\127Test"},
+    {NULL, NULL}
+};
+
+TEST_F(CharStringTest, charStringToString) {
+    for (const TestData* cur = conversion_data; cur->data != NULL; ++cur) {
+        uint8_t idata[32];
+        size_t length = std::strlen(cur->data);
+        // length (1 byte) + string (length bytes)
+        assert(sizeof(idata) > length);
+        idata[0] = static_cast<uint8_t>(length);
+        std::memcpy(idata + 1, cur->data, length);
+        const CharString test_data(idata, idata + length + 1);
+        EXPECT_EQ(cur->expected, charStringToString(test_data));
+    }
+}
+
 } // unnamed namespace
diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc
index 2df711c..52f0601 100644
--- a/src/lib/dns/tests/rdata_soa_unittest.cc
+++ b/src/lib/dns/tests/rdata_soa_unittest.cc
@@ -32,12 +32,14 @@ using namespace isc::dns::rdata;
 
 namespace {
 class Rdata_SOA_Test : public RdataTest {
-    // there's nothing to specialize
+protected:
+    Rdata_SOA_Test() : rdata_soa(Name("ns.example.com"),
+                                 Name("root.example.com"),
+                                 2010012601, 3600, 300, 3600000, 1200)
+    {}
+    const generic::SOA rdata_soa;
 };
 
-const generic::SOA rdata_soa(Name("ns.example.com"), Name("root.example.com"),
-                             2010012601, 3600, 300, 3600000, 1200);
-
 TEST_F(Rdata_SOA_Test, createFromText) {
     //TBD
 }
@@ -86,4 +88,13 @@ TEST_F(Rdata_SOA_Test, getSerial) {
     EXPECT_EQ(2010012601, rdata_soa.getSerial().getValue());
 }
 
+TEST_F(Rdata_SOA_Test, getMinimum) {
+    EXPECT_EQ(1200, rdata_soa.getMinimum());
+
+    // Also check with a very large number (with the MSB being 1).
+    EXPECT_EQ(2154848336u, generic::SOA(Name("ns.example.com"),
+                                        Name("root.example.com"),
+                                        0, 0, 0, 0, 0x80706050).getMinimum());
+}
+
 }
diff --git a/src/lib/dns/tests/rdata_txt_like_unittest.cc b/src/lib/dns/tests/rdata_txt_like_unittest.cc
index b0a572d..ef9bdfe 100644
--- a/src/lib/dns/tests/rdata_txt_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_txt_like_unittest.cc
@@ -53,17 +53,11 @@ const uint8_t wiredata_txt_like[] = {
 
 const uint8_t wiredata_nulltxt[] = { 0 };
 
-// For lexer-based constructor
-void
-dummyCallback(const string&, size_t, const string&) {
-}
-
 template<class TXT_LIKE>
 class Rdata_TXT_LIKE_Test : public RdataTest {
 protected:
     Rdata_TXT_LIKE_Test() :
-        callback(boost::bind(&dummyCallback, _1, _2, _3)),
-        loader_cb(callback, callback),
+        loader_cb(MasterLoaderCallbacks::getNullCallbacks()),
         wiredata_longesttxt(256, 'a'),
         rdata_txt_like("Test-String"),
         rdata_txt_like_empty("\"\""),
@@ -72,9 +66,6 @@ protected:
         wiredata_longesttxt[0] = 255; // adjust length
     }
 
-private:
-    const MasterLoaderCallbacks::IssueCallback callback;
-
 protected:
     MasterLoaderCallbacks loader_cb;
     vector<uint8_t> wiredata_longesttxt;
@@ -343,6 +334,24 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) {
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, toText) {
     EXPECT_EQ("\"Test-String\"", this->rdata_txt_like.toText());
+    EXPECT_EQ("\"\"", this->rdata_txt_like_empty.toText());
+    EXPECT_EQ("\"Test-String\"", this->rdata_txt_like_quoted.toText());
+
+    // Check escape behavior
+    const TypeParam double_quotes("Test-String\"Test-String\"");
+    EXPECT_EQ("\"Test-String\\\"Test-String\\\"\"", double_quotes.toText());
+    const TypeParam semicolon("Test-String\\;Test-String");
+    EXPECT_EQ("\"Test-String\\;Test-String\"", semicolon.toText());
+    const TypeParam backslash("Test-String\\\\Test-String");
+    EXPECT_EQ("\"Test-String\\\\Test-String\"", backslash.toText());
+    const TypeParam before_x20("Test-String\\031Test-String");
+    EXPECT_EQ("\"Test-String\\031Test-String\"", before_x20.toText());
+    const TypeParam from_x20_to_x7e("\"Test-String ~ Test-String\"");
+    EXPECT_EQ("\"Test-String ~ Test-String\"", from_x20_to_x7e.toText());
+    const TypeParam from_x20_to_x7e_2("Test-String\\032\\126\\032Test-String");
+    EXPECT_EQ("\"Test-String ~ Test-String\"", from_x20_to_x7e_2.toText());
+    const TypeParam after_x7e("Test-String\\127Test-String");
+    EXPECT_EQ("\"Test-String\\127Test-String\"", after_x7e.toText());
 }
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) {
diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc
index 7f0dd65..95f50e7 100644
--- a/src/lib/dns/tests/rdata_unittest.cc
+++ b/src/lib/dns/tests/rdata_unittest.cc
@@ -60,10 +60,6 @@ RdataTest::rdataFactoryFromFile(const RRType& rrtype, const RRClass& rrclass,
 
 namespace test {
 
-void
-dummyCallback(const string&, size_t, const string&) {
-}
-
 RdataPtr
 createRdataUsingLexer(const RRType& rrtype, const RRClass& rrclass,
                       const std::string& str)
@@ -72,9 +68,8 @@ createRdataUsingLexer(const RRType& rrtype, const RRClass& rrclass,
     MasterLexer lexer;
     lexer.pushSource(ss);
 
-    const MasterLoaderCallbacks::IssueCallback callback
-        (boost::bind(&dummyCallback, _1, _2, _3));
-    MasterLoaderCallbacks callbacks(callback, callback);
+    MasterLoaderCallbacks callbacks =
+        MasterLoaderCallbacks::getNullCallbacks();
     const Name origin("example.org.");
 
     return (createRdata(rrtype, rrclass, lexer, &origin,
diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc
index 6156be5..11f1c54 100644
--- a/src/lib/dns/tests/rrclass_unittest.cc
+++ b/src/lib/dns/tests/rrclass_unittest.cc
@@ -59,7 +59,7 @@ RRClassTest::rrclassFactoryFromWire(const char* datafile) {
     return (RRClass(buffer));
 }
 
-TEST_F(RRClassTest, fromText) {
+TEST_F(RRClassTest, fromTextConstructor) {
     EXPECT_EQ("IN", RRClass("IN").toText());
     EXPECT_EQ("CH", RRClass("CH").toText());
 
@@ -96,6 +96,14 @@ TEST_F(RRClassTest, toText) {
     EXPECT_EQ("CLASS65000", RRClass(65000).toText());
 }
 
+TEST_F(RRClassTest, createFromText) {
+    const MaybeRRClass rrclass("IN");
+    EXPECT_TRUE(rrclass);
+    EXPECT_EQ("IN", rrclass->toText());
+    EXPECT_TRUE(RRClass::createFromText("CH"));
+    EXPECT_FALSE(RRClass::createFromText("ZZ"));
+}
+
 TEST_F(RRClassTest, toWireBuffer) {
     rrclass_1.toWire(obuffer);
     rrclass_0x80.toWire(obuffer);
diff --git a/src/lib/dns/tests/rrcollator_unittest.cc b/src/lib/dns/tests/rrcollator_unittest.cc
new file mode 100644
index 0000000..e66f87c
--- /dev/null
+++ b/src/lib/dns/tests/rrcollator_unittest.cc
@@ -0,0 +1,214 @@
+// 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/name.h>
+#include <dns/master_loader.h>
+#include <dns/master_loader_callbacks.h>
+#include <dns/rrclass.h>
+#include <dns/rrcollator.h>
+#include <dns/rdata.h>
+#include <dns/rrset.h>
+#include <dns/rrttl.h>
+
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+
+#include <sstream>
+#include <vector>
+
+using std::vector;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+
+namespace {
+
+typedef RRCollator::AddRRsetCallback AddRRsetCallback;
+
+void
+addRRset(const RRsetPtr& rrset, vector<ConstRRsetPtr>* to_append,
+         const bool* do_throw) {
+    if (*do_throw) {
+        isc_throw(isc::Unexpected, "faked failure");
+    }
+    to_append->push_back(rrset);
+}
+
+class RRCollatorTest : public ::testing::Test {
+protected:
+    RRCollatorTest() :
+        origin_("example.com"), rrclass_(RRClass::IN()), rrttl_(3600),
+        throw_from_callback_(false),
+        collator_(boost::bind(addRRset, _1, &rrsets_, &throw_from_callback_)),
+        rr_callback_(collator_.getCallback()),
+        a_rdata1_(createRdata(RRType::A(), rrclass_, "192.0.2.1")),
+        a_rdata2_(createRdata(RRType::A(), rrclass_, "192.0.2.2")),
+        txt_rdata_(createRdata(RRType::TXT(), rrclass_, "test")),
+        sig_rdata1_(createRdata(RRType::RRSIG(), rrclass_,
+                                "A 5 3 3600 20000101000000 20000201000000 "
+                                "12345 example.com. FAKE\n")),
+        sig_rdata2_(createRdata(RRType::RRSIG(), rrclass_,
+                                "NS 5 3 3600 20000101000000 20000201000000 "
+                                "12345 example.com. FAKE\n"))
+    {}
+
+    void checkRRset(const Name& expected_name, const RRClass& expected_class,
+                    const RRType& expected_type, const RRTTL& expected_ttl,
+                    const vector<ConstRdataPtr>& expected_rdatas) {
+        SCOPED_TRACE(expected_name.toText(true) + "/" +
+                     expected_class.toText() + "/" + expected_type.toText());
+
+        // This test always clears rrsets_ to confirm RRsets are added
+        // one-by-one
+        ASSERT_EQ(1, rrsets_.size());
+
+        ConstRRsetPtr actual = rrsets_[0];
+        EXPECT_EQ(expected_name, actual->getName());
+        EXPECT_EQ(expected_class, actual->getClass());
+        EXPECT_EQ(expected_type, actual->getType());
+        EXPECT_EQ(expected_ttl, actual->getTTL());
+        ASSERT_EQ(expected_rdatas.size(), actual->getRdataCount());
+        vector<ConstRdataPtr>::const_iterator it = expected_rdatas.begin();
+        for (RdataIteratorPtr rit = actual->getRdataIterator();
+             !rit->isLast();
+             rit->next()) {
+            EXPECT_EQ(0, rit->getCurrent().compare(**it));
+            ++it;
+        }
+
+        rrsets_.clear();
+    }
+
+    const Name origin_;
+    const RRClass rrclass_;
+    const RRTTL rrttl_;
+    vector<ConstRRsetPtr> rrsets_;
+    bool throw_from_callback_;
+    RRCollator collator_;
+    AddRRCallback rr_callback_;
+    const RdataPtr a_rdata1_, a_rdata2_, txt_rdata_, sig_rdata1_, sig_rdata2_;
+    vector<ConstRdataPtr> rdatas_; // placeholder for expected data
+};
+
+TEST_F(RRCollatorTest, basicCases) {
+    // Add two RRs belonging to the same RRset.  These will be buffered.
+    rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata1_);
+    EXPECT_TRUE(rrsets_.empty()); // not yet given as an RRset
+    rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata2_);
+    EXPECT_TRUE(rrsets_.empty()); // still not given
+
+    // Add another type of RR.  This completes the construction of the A RRset,
+    // which will be given via the callback.
+    rr_callback_(origin_, rrclass_, RRType::TXT(), rrttl_, txt_rdata_);
+    rdatas_.push_back(a_rdata1_);
+    rdatas_.push_back(a_rdata2_);
+    checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_);
+
+    // Add the same type of RR but of different name.  This should make another
+    // callback for the previous TXT RR.
+    rr_callback_(Name("txt.example.com"), rrclass_, RRType::TXT(), rrttl_,
+                 txt_rdata_);
+    rdatas_.clear();
+    rdatas_.push_back(txt_rdata_);
+    checkRRset(origin_, rrclass_, RRType::TXT(), rrttl_, rdatas_);
+
+    // Add the same type and name of RR but of different class (rare case
+    // in practice)
+    rr_callback_(Name("txt.example.com"), RRClass::CH(), RRType::TXT(), rrttl_,
+                 txt_rdata_);
+    rdatas_.clear();
+    rdatas_.push_back(txt_rdata_);
+    checkRRset(Name("txt.example.com"), rrclass_, RRType::TXT(), rrttl_,
+               rdatas_);
+
+    // Tell the collator we are done, then we'll see the last RR as an RRset.
+    collator_.flush();
+    checkRRset(Name("txt.example.com"), RRClass::CH(), RRType::TXT(), rrttl_,
+               rdatas_);
+
+    // Redundant flush() will be no-op.
+    collator_.flush();
+    EXPECT_TRUE(rrsets_.empty());
+}
+
+TEST_F(RRCollatorTest, minTTLFirst) {
+    // RRs of the same RRset but has different TTLs.  The first RR has
+    // the smaller TTL, which should be used for the TTL of the RRset.
+    rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(10), a_rdata1_);
+    rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(20), a_rdata2_);
+    rdatas_.push_back(a_rdata1_);
+    rdatas_.push_back(a_rdata2_);
+    collator_.flush();
+    checkRRset(origin_, rrclass_, RRType::A(), RRTTL(10), rdatas_);
+}
+
+TEST_F(RRCollatorTest, maxTTLFirst) {
+    // RRs of the same RRset but has different TTLs.  The second RR has
+    // the smaller TTL, which should be used for the TTL of the RRset.
+    rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(20), a_rdata1_);
+    rr_callback_(origin_, rrclass_, RRType::A(), RRTTL(10), a_rdata2_);
+    rdatas_.push_back(a_rdata1_);
+    rdatas_.push_back(a_rdata2_);
+    collator_.flush();
+    checkRRset(origin_, rrclass_, RRType::A(), RRTTL(10), rdatas_);
+}
+
+TEST_F(RRCollatorTest, addRRSIGs) {
+    // RRSIG is special; they are also distinguished by their covered types.
+    rr_callback_(origin_, rrclass_, RRType::RRSIG(), rrttl_, sig_rdata1_);
+    rr_callback_(origin_, rrclass_, RRType::RRSIG(), rrttl_, sig_rdata2_);
+
+    rdatas_.push_back(sig_rdata1_);
+    checkRRset(origin_, rrclass_, RRType::RRSIG(), rrttl_, rdatas_);
+}
+
+TEST_F(RRCollatorTest, emptyFlush) {
+    collator_.flush();
+    EXPECT_TRUE(rrsets_.empty());
+}
+
+TEST_F(RRCollatorTest, throwFromCallback) {
+    // Adding an A RR
+    rr_callback_(origin_, rrclass_, RRType::A(), rrttl_, a_rdata1_);
+
+    // Adding a TXT RR, which would trigger RRset callback, but in this test
+    // it throws.  The added TXT RR will be effectively lost.
+    throw_from_callback_ = true;
+    EXPECT_THROW(rr_callback_(origin_, rrclass_, RRType::TXT(), rrttl_,
+                              txt_rdata_), isc::Unexpected);
+
+    // We'll only see the A RR.
+    throw_from_callback_ = false;
+    collator_.flush();
+    rdatas_.push_back(a_rdata1_);
+    checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_);
+}
+
+TEST_F(RRCollatorTest, withMasterLoader) {
+    // Test a simple case with MasterLoader.  There shouldn't be anything
+    // special, but that's the mainly intended usage of the collator, so we
+    // check it explicitly.
+    std::istringstream ss("example.com. 3600 IN A 192.0.2.1\n");
+    MasterLoader loader(ss, origin_, rrclass_,
+                        MasterLoaderCallbacks::getNullCallbacks(),
+                        collator_.getCallback());
+    loader.load();
+    collator_.flush();
+    rdatas_.push_back(a_rdata1_);
+    checkRRset(origin_, rrclass_, RRType::A(), rrttl_, rdatas_);
+}
+
+}
diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc
index 0ae0a76..08e0af1 100644
--- a/src/lib/dns/tests/rrparamregistry_unittest.cc
+++ b/src/lib/dns/tests/rrparamregistry_unittest.cc
@@ -157,10 +157,6 @@ TEST_F(RRParamRegistryTest, addRemoveFactory) {
                      RRType(test_type_code)));
 }
 
-void
-dummyCallback(const string&, size_t, const string&) {
-}
-
 RdataPtr
 createRdataHelper(const std::string& str) {
     boost::scoped_ptr<AbstractRdataFactory> rdf(new TestRdataFactory);
@@ -169,9 +165,7 @@ createRdataHelper(const std::string& str) {
     MasterLexer lexer;
     lexer.pushSource(ss);
 
-    const MasterLoaderCallbacks::IssueCallback callback
-        (boost::bind(&dummyCallback, _1, _2, _3));
-    MasterLoaderCallbacks callbacks(callback, callback);
+    MasterLoaderCallbacks callbacks(MasterLoaderCallbacks::getNullCallbacks());
     const Name origin("example.org.");
 
     return (rdf->create(lexer, &origin,
diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc
index fe9c55c..8897102 100644
--- a/src/lib/dns/tests/rrttl_unittest.cc
+++ b/src/lib/dns/tests/rrttl_unittest.cc
@@ -20,6 +20,8 @@
 
 #include <dns/tests/unittest_util.h>
 
+#include <boost/scoped_ptr.hpp>
+
 using namespace std;
 using namespace isc;
 using namespace isc::dns;
@@ -85,6 +87,17 @@ TEST_F(RRTTLTest, fromText) {
     EXPECT_THROW(RRTTL("4294967296"), InvalidRRTTL); // must be 32-bit
 }
 
+TEST_F(RRTTLTest, createFromText) {
+    // It returns an actual RRTT iff the given text is recognized as a
+    // valid RR TTL.
+    MaybeRRTTL maybe_ttl = RRTTL::createFromText("3600");
+    EXPECT_TRUE(maybe_ttl);
+    EXPECT_EQ(RRTTL(3600), *maybe_ttl);
+
+    maybe_ttl = RRTTL::createFromText("bad");
+    EXPECT_FALSE(maybe_ttl);
+}
+
 void
 checkUnit(unsigned multiply, char suffix) {
     SCOPED_TRACE(string("Unit check with suffix ") + suffix);
@@ -252,6 +265,10 @@ TEST_F(RRTTLTest, gthan) {
     EXPECT_FALSE(ttl_small > ttl_large);
 }
 
+TEST_F(RRTTLTest, maxTTL) {
+    EXPECT_EQ((1u << 31) - 1, RRTTL::MAX_TTL().getValue());
+}
+
 // test operator<<.  We simply confirm it appends the result of toText().
 TEST_F(RRTTLTest, LeftShiftOperator) {
     ostringstream oss;
diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am
index 9494117..52acb7c 100644
--- a/src/lib/dns/tests/testdata/Makefile.am
+++ b/src/lib/dns/tests/testdata/Makefile.am
@@ -172,6 +172,8 @@ EXTRA_DIST += tsig_verify7.spec tsig_verify8.spec tsig_verify9.spec
 EXTRA_DIST += tsig_verify10.spec
 EXTRA_DIST += example.org
 EXTRA_DIST += broken.zone
+EXTRA_DIST += origincheck.txt
+EXTRA_DIST += omitcheck.txt
 
 .spec.wire:
 	$(PYTHON) $(top_builddir)/src/lib/util/python/gen_wiredata.py -o $@ $<
diff --git a/src/lib/dns/tests/testdata/example.org b/src/lib/dns/tests/testdata/example.org
index 2f2edf3..4163fc0 100644
--- a/src/lib/dns/tests/testdata/example.org
+++ b/src/lib/dns/tests/testdata/example.org
@@ -13,3 +13,5 @@ example.org.        3600    IN  SOA ( ; The SOA, split across lines for testing
 
 ; Some empty lines here. They are to make sure the loader can skip them.
 www                 3600    IN  A 192.0.2.1 ; Test a relative name as well.
+                    3600    IN  AAAA    2001:db8::1 ; And initial whitespace hanling
+         ; Here be just some space, no RRs
diff --git a/src/lib/dns/tests/testdata/omitcheck.txt b/src/lib/dns/tests/testdata/omitcheck.txt
new file mode 100644
index 0000000..580cab4
--- /dev/null
+++ b/src/lib/dns/tests/testdata/omitcheck.txt
@@ -0,0 +1 @@
+    1H  IN  A   192.0.2.1
diff --git a/src/lib/dns/tests/testdata/origincheck.txt b/src/lib/dns/tests/testdata/origincheck.txt
new file mode 100644
index 0000000..c370ed2
--- /dev/null
+++ b/src/lib/dns/tests/testdata/origincheck.txt
@@ -0,0 +1,5 @@
+; We change the origin here. We want to check it is not propagated
+; outside of the included file.
+$ORIGIN www.example.org.
+
+@   1H  IN  A   192.0.2.1
diff --git a/src/lib/log/log_messages.mes b/src/lib/log/log_messages.mes
index f150f39..4bc8c98 100644
--- a/src/lib/log/log_messages.mes
+++ b/src/lib/log/log_messages.mes
@@ -97,7 +97,7 @@ There may be several reasons why this message may appear:
 - The message ID has been mis-spelled in the local message file.
 
 - The program outputting the message may not use that particular message
-(e.g. it originates in a module not used by the program.)
+(e.g. it originates in a module not used by the program).
 
 - The local file was written for an earlier version of the BIND 10 software
 and the later version no longer generates that message.
@@ -131,7 +131,7 @@ version of BIND 10.
 % LOG_READING_LOCAL_FILE reading local message file %1
 This is an informational message output by BIND 10 when it starts to read
 a local message file.  (A local message file may replace the text of
-one of more messages; the ID of the message will not be changed though.)
+one or more messages; the ID of the message will not be changed though.)
 
 % LOG_READ_ERROR error reading from message file %1: %2
 The specified error was encountered reading from the named message file.
diff --git a/src/lib/log/logger_manager_impl.cc b/src/lib/log/logger_manager_impl.cc
index 6862d0c..711eae9 100644
--- a/src/lib/log/logger_manager_impl.cc
+++ b/src/lib/log/logger_manager_impl.cc
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <config.h>
+
 #include <algorithm>
 #include <iostream>
 
@@ -21,6 +23,7 @@
 #include <log4cplus/consoleappender.h>
 #include <log4cplus/fileappender.h>
 #include <log4cplus/syslogappender.h>
+#include <log4cplus/helpers/loglog.h>
 
 #include <log/logger.h>
 #include <log/logger_support.h>
@@ -196,6 +199,13 @@ void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
 {
     log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
 
+    // Disable log4cplus' own logging, unless --enable-debug was
+    // specified to configure. Note that this does not change
+    // LogLog's levels (that is still just INFO).
+#ifndef ENABLE_DEBUG
+    log4cplus::helpers::LogLog::getLogLog()->setQuietMode(true);
+#endif
+
     // Set the log4cplus root to not output anything - effectively we are
     // ignoring it.
     log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);
diff --git a/src/lib/log/tests/.gitignore b/src/lib/log/tests/.gitignore
index b0e45b9..e7e12d6 100644
--- a/src/lib/log/tests/.gitignore
+++ b/src/lib/log/tests/.gitignore
@@ -1,3 +1,5 @@
+/buffer_logger_test
+/buffer_logger_test.sh
 /console_test.sh
 /destination_test.sh
 /init_logger_test
diff --git a/src/lib/python/isc/datasrc/Makefile.am b/src/lib/python/isc/datasrc/Makefile.am
index 84da788..28c87ac 100644
--- a/src/lib/python/isc/datasrc/Makefile.am
+++ b/src/lib/python/isc/datasrc/Makefile.am
@@ -2,7 +2,7 @@ SUBDIRS = . tests
 
 # old data, should be removed in the near future once conversion is done
 pythondir = $(pyexecdir)/isc/datasrc
-python_PYTHON = __init__.py master.py sqlite3_ds.py
+python_PYTHON = __init__.py sqlite3_ds.py
 
 
 # new data
@@ -20,6 +20,7 @@ datasrc_la_SOURCES += updater_python.cc updater_python.h
 datasrc_la_SOURCES += journal_reader_python.cc journal_reader_python.h
 datasrc_la_SOURCES += configurableclientlist_python.cc
 datasrc_la_SOURCES += configurableclientlist_python.h
+datasrc_la_SOURCES += zone_loader_python.cc zone_loader_python.h
 
 datasrc_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
 datasrc_la_CXXFLAGS = $(AM_CXXFLAGS) $(PYTHON_CXXFLAGS)
@@ -35,6 +36,7 @@ EXTRA_DIST += finder_inc.cc
 EXTRA_DIST += iterator_inc.cc
 EXTRA_DIST += updater_inc.cc
 EXTRA_DIST += journal_reader_inc.cc
+EXTRA_DIST += zone_loader_inc.cc
 
 CLEANDIRS = __pycache__
 
diff --git a/src/lib/python/isc/datasrc/__init__.py b/src/lib/python/isc/datasrc/__init__.py
index 7ebd918..6e0f5ae 100644
--- a/src/lib/python/isc/datasrc/__init__.py
+++ b/src/lib/python/isc/datasrc/__init__.py
@@ -2,7 +2,7 @@ import sys
 import os
 
 # The datasource factory loader uses dlopen, as does python
-# for its modules. Some dynamic linkers do not play nice if 
+# for its modules. Some dynamic linkers do not play nice if
 # modules are not loaded with RTLD_GLOBAL, a symptom of which
 # is that exceptions are not recognized by type. So to make
 # sure this doesn't happen, we temporarily set RTLD_GLOBAL
@@ -31,5 +31,4 @@ else:
 sys.setdlopenflags(flags)
 
 from isc.datasrc.sqlite3_ds import *
-from isc.datasrc.master import *
 
diff --git a/src/lib/python/isc/datasrc/client_python.cc b/src/lib/python/isc/datasrc/client_python.cc
index d0206d1..f360445 100644
--- a/src/lib/python/isc/datasrc/client_python.cc
+++ b/src/lib/python/isc/datasrc/client_python.cc
@@ -407,6 +407,16 @@ wrapDataSourceClient(DataSourceClient* client,
     return (container.release());
 }
 
+DataSourceClient&
+PyDataSourceClient_ToDataSourceClient(PyObject* client_obj) {
+    if (client_obj == NULL) {
+        isc_throw(PyCPPWrapperException,
+                  "argument NULL in DataSourceClient PyObject conversion");
+    }
+    s_DataSourceClient* client = static_cast<s_DataSourceClient*>(client_obj);
+    return (*client->client);
+}
+
 } // namespace python
 } // namespace datasrc
 } // namespace isc
diff --git a/src/lib/python/isc/datasrc/client_python.h b/src/lib/python/isc/datasrc/client_python.h
index 71aee8b..e700fde 100644
--- a/src/lib/python/isc/datasrc/client_python.h
+++ b/src/lib/python/isc/datasrc/client_python.h
@@ -44,6 +44,17 @@ wrapDataSourceClient(DataSourceClient* client,
                      LifeKeeper>& life_keeper = boost::shared_ptr<ClientList::
                      FindResult::LifeKeeper>());
 
+/// \brief Returns a reference to the DataSourceClient object contained
+///        in the given Python object.
+///
+/// \note The given object MUST be of type DataSourceClient; this can be
+///       checked with the right call to ParseTuple("O!")
+///
+/// \param client_obj Python object holding the DataSourceClient
+/// \return reference to the DataSourceClient object
+DataSourceClient&
+PyDataSourceClient_ToDataSourceClient(PyObject* client_obj);
+
 } // namespace python
 } // namespace datasrc
 } // namespace isc
diff --git a/src/lib/python/isc/datasrc/datasrc.cc b/src/lib/python/isc/datasrc/datasrc.cc
index 533a08c..37c9baa 100644
--- a/src/lib/python/isc/datasrc/datasrc.cc
+++ b/src/lib/python/isc/datasrc/datasrc.cc
@@ -29,6 +29,7 @@
 #include "updater_python.h"
 #include "journal_reader_python.h"
 #include "configurableclientlist_python.h"
+#include "zone_loader_python.h"
 
 #include <util/python/pycppwrapper_util.h>
 #include <dns/python/pydnspp_common.h>
@@ -181,6 +182,23 @@ initModulePart_ZoneIterator(PyObject* mod) {
 }
 
 bool
+initModulePart_ZoneLoader(PyObject* mod) {
+    // We initialize the static description object with PyType_Ready(),
+    // then add it to the module. This is not just a check! (leaving
+    // this out results in segmentation faults)
+    if (PyType_Ready(&zone_loader_type) < 0) {
+        return (false);
+    }
+    void* p = &zone_loader_type;
+    if (PyModule_AddObject(mod, "ZoneLoader", static_cast<PyObject*>(p)) < 0) {
+        return (false);
+    }
+    Py_INCREF(&zone_loader_type);
+
+    return (true);
+}
+
+bool
 initModulePart_ZoneUpdater(PyObject* mod) {
     // We initialize the static description object with PyType_Ready(),
     // then add it to the module. This is not just a check! (leaving
@@ -234,8 +252,9 @@ initModulePart_ZoneJournalReader(PyObject* mod) {
 }
 
 PyObject* po_DataSourceError;
-PyObject* po_OutOfZone;
+PyObject* po_MasterFileError;
 PyObject* po_NotImplemented;
+PyObject* po_OutOfZone;
 
 PyModuleDef iscDataSrc = {
     { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
@@ -260,6 +279,26 @@ PyInit_datasrc(void) {
         return (NULL);
     }
 
+    try {
+        po_DataSourceError = PyErr_NewException("isc.datasrc.Error", NULL,
+                                                NULL);
+        PyObjectContainer(po_DataSourceError).installToModule(mod, "Error");
+        po_MasterFileError = PyErr_NewException("isc.datasrc.MasterFileError",
+                                                po_DataSourceError, NULL);
+        PyObjectContainer(po_MasterFileError).
+            installToModule(mod, "MasterFileError");
+        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,
+                                                             "NotImplemented");
+
+    } catch (...) {
+        Py_DECREF(mod);
+        return (NULL);
+    }
+
     if (!initModulePart_DataSourceClient(mod)) {
         Py_DECREF(mod);
         return (NULL);
@@ -290,17 +329,7 @@ PyInit_datasrc(void) {
         return (NULL);
     }
 
-    try {
-        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,
-                                                             "NotImplemented");
-    } catch (...) {
+    if (!initModulePart_ZoneLoader(mod)) {
         Py_DECREF(mod);
         return (NULL);
     }
diff --git a/src/lib/python/isc/datasrc/master.py b/src/lib/python/isc/datasrc/master.py
deleted file mode 100644
index 2c22190..0000000
--- a/src/lib/python/isc/datasrc/master.py
+++ /dev/null
@@ -1,616 +0,0 @@
-# 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.
-
-import sys, re, string
-import time
-import os
-#########################################################################
-# define exceptions
-#########################################################################
-class MasterFileError(Exception):
-    pass
-
-#########################################################################
-# pop: remove the first word from a line
-# input: a line
-# returns: first word, rest of the line
-#########################################################################
-def pop(line):
-    list = line.split()
-    first, rest = '', ''
-    if len(list) != 0:
-        first = list[0]
-    if len(list) > 1:
-        rest = ' '.join(list[1:])
-    return first, rest
-
-#########################################################################
-# cleanup: removes excess content from zone file data, including comments
-# and extra whitespace
-# input:
-#   line of text
-# returns:
-#   the same line, with comments removed, leading and trailing
-#   whitespace removed, and all other whitespace compressed to
-#   single spaces
-#########################################################################
-decomment = re.compile('^\s*((?:[^;"]|"[^"]*")*)\s*(?:|;.*)$')
-# Regular expression explained:
-# First, ignore any whitespace at the start. Then take the content,
-# each bit is either a harmless character (no ; nor ") or a string -
-# sequence between " " not containing double quotes. Then there may
-# be a comment at the end.
-def cleanup(s):
-    global decomment
-    s = s.strip().expandtabs()
-    s = decomment.search(s).group(1)
-    return ' '.join(s.split())
-
-#########################################################################
-# istype: check whether a string is a known RR type.
-# returns: boolean
-#########################################################################
-rrtypes = set(['a', 'aaaa', 'afsdb', 'apl', 'cert', 'cname', 'dhcid',
-               'dlv', 'dname', 'dnskey', 'ds', 'gpos', 'hinfo', 'hip',
-               'ipseckey', 'isdn', 'key', 'kx', 'loc', 'mb', 'md',
-               'mf', 'mg', 'minfo', 'mr', 'mx', 'naptr', 'ns', 'nsap',
-               'nsap-ptr', 'nsec', 'nsec3', 'nsec3param', 'null',
-               'nxt', 'opt', 'ptr', 'px', 'rp', 'rrsig', 'rt', 'sig',
-               'soa', 'spf', 'srv', 'sshfp', 'tkey', 'tsig', 'txt',
-               'x25', 'wks'])
-def istype(s):
-    global rrtypes
-    if s.lower() in rrtypes:
-        return True
-    else:
-        return False
-
-#########################################################################
-# isclass: check whether a string is a known RR class.  (only 'IN' is
-# supported, but the others must still be recognizable.)
-# returns: boolean
-#########################################################################
-rrclasses = set(['in', 'ch', 'chaos', 'hs', 'hesiod'])
-def isclass(s):
-    global rrclasses
-    if s.lower() in rrclasses:
-        return True
-    else:
-        return False
-
-#########################################################################
-# isname: check whether a string is a valid DNS name.
-# returns: boolean
-#########################################################################
-name_regex = re.compile('[-\w\$\d\/*]+(?:\.[-\w\$\d\/]+)*\.?')
-def isname(s):
-    global name_regex
-    if s == '.' or name_regex.match(s):
-        return True
-    else:
-        return False
-
-#########################################################################
-# isttl: check whether a string is a valid TTL specifier.
-# returns: boolean
-#########################################################################
-ttl_regex = re.compile('([0-9]+[wdhms]?)+$', re.I)
-def isttl(s):
-    global ttl_regex
-    if ttl_regex.match(s):
-        return True
-    else:
-        return False
-
-#########################################################################
-# parse_ttl: convert a TTL field into an integer TTL value
-# (multiplying as needed for minutes, hours, etc.)
-# input:
-#   string
-# returns:
-#   int
-# throws:
-#   MasterFileError
-#########################################################################
-def parse_ttl(s):
-    sum = 0
-    if not isttl(s):
-        raise MasterFileError('Invalid TTL: ' + s)
-    for ttl_expr in re.findall('\d+[wdhms]?', s, re.I):
-        if ttl_expr.isdigit():
-            ttl = int(ttl_expr)
-            sum += ttl
-            continue
-        ttl = int(ttl_expr[:-1])
-        suffix = ttl_expr[-1].lower()
-        if suffix == 'w':
-            ttl *= 604800
-        elif suffix == 'd':
-            ttl *= 86400
-        elif suffix == 'h':
-            ttl *= 3600
-        elif suffix == 'm':
-            ttl *= 60
-        sum += ttl
-    return str(sum)
-
-#########################################################################
-# records: generator function to return complete RRs from the zone file,
-# combining lines when necessary because of parentheses
-# input:
-#   descriptor for a zone master file (returned from openzone)
-# yields:
-#   complete RR
-#########################################################################
-def records(input):
-    record = []
-    complete = True
-    paren = 0
-    size = 0
-    for line in input:
-        size += len(line)
-        list = cleanup(line).split()
-        for word in list:
-            if paren == 0:
-                left, p, right = word.partition('(')
-                if p == '(':
-                    if left: record.append(left)
-                    if right: record.append(right)
-                    paren += 1
-                else:
-                    record.append(word)
-            else:
-                left, p, right = word.partition(')')
-                if p == ')':
-                    if left: record.append(left)
-                    if right: record.append(right)
-                    paren -= 1
-                else:
-                    record.append(word)
-
-        if paren == 1 or not record:
-            continue
-        
-        ret = ' '.join(record)
-        record = []
-        oldsize = size
-        size = 0
-        yield ret, oldsize
-
-#########################################################################
-# define the MasterFile class for reading zone master files
-#########################################################################
-class MasterFile:
-    __rrclass = 'IN'
-    __maxttl = 0x7fffffff
-    __ttl = ''
-    __lastttl = ''
-    __zonefile = ''
-    __name = ''
-    __file_level = 0
-    __file_type = ""
-    __init_time = time.time()
-    __records_num = 0
-
-    def __init__(self, filename, initial_origin = '', verbose = False):
-        self.__initial_origin = initial_origin
-        self.__origin = initial_origin
-        self.__datafile = filename
-
-        try:
-            self.__zonefile = open(filename, 'r')
-        except:
-            raise MasterFileError("Could not open " + filename)
-        self.__filesize = os.fstat(self.__zonefile.fileno()).st_size
-
-        self.__cur = 0
-        self.__numback = 0
-        self.__verbose = verbose
-        try:
-            self.__zonefile = open(filename, 'r')
-        except:
-            raise MasterFileError("Could not open " + filename)
-
-    def __status(self):
-        interval = time.time() - MasterFile.__init_time
-        if self.__filesize == 0:
-            percent = 100
-        else:
-            percent = (self.__cur * 100)/self.__filesize
-
-        sys.stdout.write("\r" + (80 * " "))
-        sys.stdout.write("\r%d RR(s) loaded in %.2f second(s) (%.2f%% of %s%s)"\
-                % (MasterFile.__records_num, interval, percent, MasterFile.__file_type, self.__datafile))
-
-    def __del__(self):
-        if self.__zonefile:
-            self.__zonefile.close()
-    ########################################################################
-    # check if the zonename is relative
-    # no then return
-    # yes , sets the relative domain name to the stated name
-    #######################################################################
-    def __statedname(self, name, record):
-        if name[-1] != '.':
-            if not self.__origin:
-                raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
-            elif self.__origin == '.':
-                name += '.'
-            else:
-                name += '.' + self.__origin
-        return name
-    #####################################################################
-    # handle $ORIGIN, $TTL and $GENERATE directives
-    # (currently only $ORIGIN and $TTL are implemented)
-    # input:
-    #   a line from a zone file
-    # returns:
-    #   a boolean indicating whether a directive was found
-    # throws:
-    #   MasterFileError
-    #########################################################################
-    def __directive(self, s):
-        first, more = pop(s)
-        second, more = pop(more)
-        if re.match('\$origin', first, re.I):
-            if not second or not isname(second):
-                raise MasterFileError('Invalid $ORIGIN')
-            if more:
-                raise MasterFileError('Invalid $ORIGIN')
-            if second[-1] == '.':
-                self.__origin = second
-            elif not self.__origin:
-                raise MasterFileError("$ORIGIN is not absolute in record: %s" % s)
-            elif self.__origin != '.':
-                self.__origin = second + '.' + self.__origin
-            else:
-                self.__origin = second + '.'
-            return True
-        elif re.match('\$ttl', first, re.I):
-            if not second or not isttl(second):
-                raise MasterFileError('Invalid TTL: "' + second + '"')
-            if more:
-                raise MasterFileError('Invalid $TTL statement')
-            MasterFile.__ttl = parse_ttl(second)
-            if int(MasterFile.__ttl) > self.__maxttl:
-                raise MasterFileError('TTL too high: ' + second)
-            return True
-        elif re.match('\$generate', first, re.I):
-            raise MasterFileError('$GENERATE not yet implemented')
-        else:
-            return False
-
-    #########################################################################
-    # handle $INCLUDE directives
-    # input:
-    #   a line from a zone file
-    # returns:
-    #   the parsed output of the included file, if any, or an empty array
-    # throws:
-    #   MasterFileError
-    #########################################################################
-    __include_syntax1 = re.compile('\s+(\S+)(?:\s+(\S+))?$', re.I)
-    __include_syntax2 = re.compile('\s+"([^"]+)"(?:\s+(\S+))?$', re.I)
-    __include_syntax3 = re.compile("\s+'([^']+)'(?:\s+(\S+))?$", re.I)
-    def __include(self, s):
-        if not s.lower().startswith('$include'):
-            return "", ""
-        s = s[len('$include'):]
-        m = self.__include_syntax1.match(s)
-        if not m:
-            m = self.__include_syntax2.match(s)
-        if not m:
-            m = self.__include_syntax3.match(s)
-        if not m:
-            raise MasterFileError('Invalid $include format')
-        file = m.group(1)
-        if m.group(2):
-            if not isname(m.group(2)):
-                raise MasterFileError('Invalid $include format (invalid origin)')
-            origin = self.__statedname(m.group(2), s)
-        else:
-            origin = self.__origin
-        return file, origin
-
-    #########################################################################
-    # try parsing an RR on the assumption that the type is specified in
-    # field 4, and name, ttl and class are in fields 1-3
-    # are all specified, with type in field 4
-    # input:
-    #   a record to parse, and the most recent name found in prior records
-    # returns:
-    #   empty list if parse failed, else name, ttl, class, type, rdata
-    #########################################################################
-    def __four(self, record, curname):
-        ret = ''
-        list = record.split()
-        if len(list) <= 4:
-            return ret
-        if istype(list[3]):
-            if isclass(list[2]) and isttl(list[1]) and isname(list[0]):
-                name, ttl, rrclass, rrtype = list[0:4]
-                ttl = parse_ttl(ttl)
-                MasterFile.__lastttl = ttl or MasterFile.__lastttl
-                rdata = ' '.join(list[4:])
-                ret = name, ttl, rrclass, rrtype, rdata
-            elif isclass(list[1]) and isttl(list[2]) and isname(list[0]):
-                name, rrclass, ttl, rrtype = list[0:4]
-                ttl = parse_ttl(ttl)
-                MasterFile.__lastttl = ttl or MasterFile.__lastttl
-                rdata = ' '.join(list[4:])
-                ret = name, ttl, rrclass, rrtype, rdata
-        return ret
-
-    #########################################################################
-    # try parsing an RR on the assumption that the type is specified
-    # in field 3, and one of name, ttl, or class has been omitted
-    # input:
-    #   a record to parse, and the most recent name found in prior records
-    # returns:
-    #   empty list if parse failed, else name, ttl, class, type, rdata
-    #########################################################################
-    def __getttl(self):
-        return MasterFile.__ttl or MasterFile.__lastttl
-
-    def __three(self, record, curname):
-        ret = ''
-        list = record.split()
-        if len(list) <= 3:
-            return ret
-        if istype(list[2]) and not istype(list[1]):
-            if isclass(list[1]) and not isttl(list[0]) and isname(list[0]):
-                rrclass = list[1]
-                ttl = self.__getttl()
-                name = list[0]
-            elif not isclass(list[1]) and isttl(list[1]) and not isclass(list[0]) and isname(list[0]):
-                rrclass = self.__rrclass
-                ttl = parse_ttl(list[1])
-                MasterFile.__lastttl = ttl or MasterFile.__lastttl
-                name = list[0]
-            elif curname and isclass(list[1]) and isttl(list[0]):
-                rrclass = list[1]
-                ttl = parse_ttl(list[0])
-                MasterFile.__lastttl = ttl or MasterFile.__lastttl
-                name = curname
-            elif curname and isttl(list[1]) and isclass(list[0]):
-                rrclass = list[0]
-                ttl = parse_ttl(list[1])
-                MasterFile.__lastttl = ttl or MasterFile.__lastttl
-                name = curname
-            else:
-                return ret
-            rrtype = list[2]
-            rdata = ' '.join(list[3:])
-            ret = name, ttl, rrclass, rrtype, rdata
-        return ret
-
-    #########################################################################
-    # try parsing an RR on the assumption that the type is specified in
-    # field 2, and field 1 is either name or ttl
-    # input:
-    #   a record to parse, and the most recent name found in prior records
-    # returns:
-    #   empty list if parse failed, else name, ttl, class, type, rdata
-    # throws:
-    #   MasterFileError
-    #########################################################################
-    def __two(self, record, curname):
-        ret = ''
-        list = record.split()
-        if len(list) <= 2:
-            return ret
-        if istype(list[1]):
-            rrclass = self.__rrclass
-            rrtype = list[1]
-            if list[0].lower() == 'rrsig':
-                name = curname
-                ttl = self.__getttl()
-                rrtype = list[0]
-                rdata = ' '.join(list[1:])
-            elif isttl(list[0]):
-                ttl = parse_ttl(list[0])
-                name = curname
-                rdata = ' '.join(list[2:])
-            elif isclass(list[0]):
-                ttl = self.__getttl()
-                name = curname
-                rdata = ' '.join(list[2:])
-            elif isname(list[0]):
-                name = list[0]
-                ttl = self.__getttl()
-                rdata = ' '.join(list[2:])
-            else:
-                raise MasterFileError("Cannot parse RR: " + record)
-
-            ret = name, ttl, rrclass, rrtype, rdata
-        return ret
-
-    ########################################################################
-    #close verbose
-    ######################################################################
-    def closeverbose(self):
-        self.__status()
-
-    #########################################################################
-    # zonedata: generator function to parse a zone master file and return
-    # each RR as a (name, ttl, type, class, rdata) tuple
-    #########################################################################
-    def zonedata(self):
-        name = ''
-        last_status = 0.0
-        flag = 1
-
-        for record, size in records(self.__zonefile):
-            if self.__verbose:
-                now = time.time()
-                if flag == 1:
-                    self.__status()
-                    flag = 0
-                if now - last_status >= 1.0:
-                    self.__status()
-                    last_status = now
-
-            self.__cur += size
-            if self.__directive(record):
-                continue
-
-            incl, suborigin = self.__include(record)
-            if incl:
-                if self.__filesize == 0:
-                    percent = 100
-                else:
-                    percent = (self.__cur * 100)/self.__filesize
-                if self.__verbose:
-                    sys.stdout.write("\r" + (80 * " "))
-                    sys.stdout.write("\rIncluding \"%s\" from \"%s\"\n" % (incl, self.__datafile))
-                MasterFile.__file_level += 1
-                MasterFile.__file_type = "included "
-                sub = MasterFile(incl, suborigin, self.__verbose)
-
-                for rrname, ttl, rrclass, rrtype, rdata in sub.zonedata():
-                    yield (rrname, ttl, rrclass, rrtype, rdata)
-                if self.__verbose:
-                    sub.closeverbose()
-                MasterFile.__file_level -= 1
-                if MasterFile.__file_level == 0:
-                    MasterFile.__file_type = ""
-                del sub
-                continue
-
-            # replace @ with origin
-            rl = record.split()
-            if rl[0] == '@':
-                rl[0] = self.__origin
-                if not self.__origin:
-                    raise MasterFileError("Cannot parse RR, No $ORIGIN: " + record)
-                record = ' '.join(rl)
-
-            result = self.__four(record, name)
-
-            if not result:
-                result = self.__three(record, name)
-
-            if not result:
-                result = self.__two(record, name)
-
-            if not result:
-                first, rdata = pop(record)
-                if istype(first):
-                    result = name, self.__getttl(), self.__rrclass, first, rdata
-
-            if not result:
-                raise MasterFileError("Cannot parse RR: " + record)
-
-            name, ttl, rrclass, rrtype, rdata = result
-            name = self.__statedname(name, record)
-
-            if rrclass.lower() != 'in':
-                raise MasterFileError("CH and HS zones not supported")
-
-            # add origin to rdata containing names, if necessary
-            if rrtype.lower() in ('cname', 'dname', 'ns', 'ptr'):
-                if not isname(rdata):
-                    raise MasterFileError("Invalid " + rrtype + ": " + rdata)
-                rdata = self.__statedname(rdata, record)
-
-            if rrtype.lower() == 'soa':
-                soa = rdata.split()
-                if len(soa) < 2 or not isname(soa[0]) or not isname(soa[1]):
-                    raise MasterFileError("Invalid " + rrtype + ": " + rdata)
-                soa[0] = self.__statedname(soa[0], record)
-                soa[1] = self.__statedname(soa[1], record)
-                if not MasterFile.__ttl and not ttl:
-                    MasterFile.__ttl = MasterFile.__ttl or parse_ttl(soa[-1])
-                    ttl = MasterFile.__ttl
-
-                for index in range(3, len(soa)):
-                    if isttl(soa[index]):
-                        soa[index] = parse_ttl(soa[index])
-                    else :
-                        raise MasterFileError("No TTL specified; in soa record!")
-                rdata = ' '.join(soa)
-
-            if not ttl:
-                raise MasterFileError("No TTL specified; zone rejected")
-
-            if rrtype.lower() == 'mx':
-                mx = rdata.split()
-                if len(mx) != 2 or not isname(mx[1]):
-                    raise MasterFileError("Invalid " + rrtype + ": " + rdata)
-                if mx[1][-1] != '.':
-                    mx[1] += '.' + self.__origin
-                    rdata = ' '.join(mx)
-            MasterFile.__records_num += 1
-            yield (name, ttl, rrclass, rrtype, rdata)
-
-    #########################################################################
-    # zonename: scans zone data for an SOA record, returns its name, restores
-    # the zone file to its prior state
-    #########################################################################
-    def zonename(self):
-        if self.__name:
-            return self.__name
-        old_origin = self.__origin
-        self.__origin = self.__initial_origin
-        cur_value = self.__cur
-        old_location = self.__zonefile.tell()
-        old_verbose = self.__verbose
-        self.__verbose = False
-        self.__zonefile.seek(0)
-
-        for name, ttl, rrclass, rrtype, rdata in self.zonedata():
-            if rrtype.lower() == 'soa':
-                break
-        self.__zonefile.seek(old_location)
-        self.__origin = old_origin
-        self.__cur = cur_value
-        if rrtype.lower() != 'soa':
-            raise MasterFileError("No SOA found")
-        self.__name = name
-        self.__verbose = old_verbose
-        return name
-
-    #########################################################################
-    # reset: reset the state of the master file
-    #########################################################################
-    def reset(self):
-        self.__zonefile.seek(0)
-        self.__origin = self.__initial_origin
-        MasterFile.__ttl = ''
-        MasterFile.__lastttl = ''
-
-#########################################################################
-# main: used for testing; parse a zone file and print out each record
-# broken up into separate name, ttl, class, type, and rdata files
-#########################################################################
-def main():
-    try:
-        file = sys.argv[1]
-    except:
-        file = 'testfile'
-    master = MasterFile(file, '.')
-    print ('zone name: ' + master.zonename())
-    print ('---------------------')
-    for name, ttl, rrclass, rrtype, rdata in master.zonedata():
-        print ('name: ' + name)
-        print ('ttl: ' + ttl)
-        print ('rrclass: ' + rrclass)
-        print ('rrtype: ' + rrtype)
-        print ('rdata: ' + rdata)
-        print ('---------------------')
-    del master
-
-if __name__ == "__main__":
-    main()
diff --git a/src/lib/python/isc/datasrc/tests/.gitignore b/src/lib/python/isc/datasrc/tests/.gitignore
index 58ea8cd..0d94a86 100644
--- a/src/lib/python/isc/datasrc/tests/.gitignore
+++ b/src/lib/python/isc/datasrc/tests/.gitignore
@@ -1 +1,2 @@
 /*.sqlite3.copied
+/zoneloadertest.sqlite3
diff --git a/src/lib/python/isc/datasrc/tests/Makefile.am b/src/lib/python/isc/datasrc/tests/Makefile.am
index 34af092..c16d295 100644
--- a/src/lib/python/isc/datasrc/tests/Makefile.am
+++ b/src/lib/python/isc/datasrc/tests/Makefile.am
@@ -1,15 +1,18 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
-# old tests, TODO remove or change to use new API?
-#PYTESTS = master_test.py
-PYTESTS =  datasrc_test.py sqlite3_ds_test.py clientlist_test.py
+PYTESTS =  datasrc_test.py sqlite3_ds_test.py
+PYTESTS += clientlist_test.py zone_loader_test.py
 EXTRA_DIST = $(PYTESTS)
 
 EXTRA_DIST += testdata/brokendb.sqlite3
 EXTRA_DIST += testdata/example.com.sqlite3
+EXTRA_DIST += testdata/example.com.source.sqlite3
 EXTRA_DIST += testdata/newschema.sqlite3
 EXTRA_DIST += testdata/oldschema.sqlite3
 EXTRA_DIST += testdata/new_minor_schema.sqlite3
+EXTRA_DIST += testdata/example.com
+EXTRA_DIST += testdata/example.com.ch
 CLEANFILES = $(abs_builddir)/rwtest.sqlite3.copied
+CLEANFILES += $(abs_builddir)/zoneloadertest.sqlite3
 
 # If necessary (rare cases), explicitly specify paths to dynamic libraries
 # required by loadable python modules.
@@ -24,6 +27,7 @@ LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/data
 endif
 
 # test using command-line arguments, so use check-local target instead of TESTS
+# We need to define B10_FROM_BUILD for datasrc loadable modules
 check-local:
 if ENABLE_PYTHON_COVERAGE
 	touch $(abs_top_srcdir)/.coverage
diff --git a/src/lib/python/isc/datasrc/tests/master_test.py b/src/lib/python/isc/datasrc/tests/master_test.py
deleted file mode 100644
index c65858e..0000000
--- a/src/lib/python/isc/datasrc/tests/master_test.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# 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.
-
-from isc.datasrc.master import *
-import unittest
-
-class TestTTL(unittest.TestCase):
-    def test_ttl(self):
-        self.assertTrue(isttl('3600'))
-        self.assertTrue(isttl('1W'))
-        self.assertTrue(isttl('1w'))
-        self.assertTrue(isttl('2D'))
-        self.assertTrue(isttl('2d'))
-        self.assertTrue(isttl('30M'))
-        self.assertTrue(isttl('30m'))
-        self.assertTrue(isttl('10S'))
-        self.assertTrue(isttl('10s'))
-        self.assertTrue(isttl('2W1D'))
-        self.assertFalse(isttl('not a ttl'))
-        self.assertFalse(isttl('1X'))
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com b/src/lib/python/isc/datasrc/tests/testdata/example.com
new file mode 100644
index 0000000..24e22e1
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/testdata/example.com
@@ -0,0 +1,8 @@
+example.com.         1000  IN  SOA a.dns.example.com. mail.example.com. 1 1 1 1 1
+example.com.         1000  IN  NS  a.dns.example.com.
+example.com.         1000  IN  NS  b.dns.example.com.
+example.com.         1000  IN  NS  c.dns.example.com.
+a.dns.example.com.   1000  IN  A    1.1.1.1
+b.dns.example.com.   1000  IN  A    3.3.3.3
+b.dns.example.com.   1000  IN  AAAA 4:4::4:4
+b.dns.example.com.   1000  IN  AAAA 5:5::5:5
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com.ch b/src/lib/python/isc/datasrc/tests/testdata/example.com.ch
new file mode 100644
index 0000000..95c0e9a
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/testdata/example.com.ch
@@ -0,0 +1,8 @@
+example.com.         1000  CH  SOA a.dns.example.com. mail.example.com. 1 1 1 1 1
+example.com.         1000  CH  NS  a.dns.example.com.
+example.com.         1000  CH  NS  b.dns.example.com.
+example.com.         1000  CH  NS  c.dns.example.com.
+a.dns.example.com.   1000  CH  A    1.1.1.1
+b.dns.example.com.   1000  CH  A    3.3.3.3
+b.dns.example.com.   1000  CH  AAAA 4:4::4:4
+b.dns.example.com.   1000  CH  AAAA 5:5::5:5
diff --git a/src/lib/python/isc/datasrc/tests/testdata/example.com.source.sqlite3 b/src/lib/python/isc/datasrc/tests/testdata/example.com.source.sqlite3
new file mode 100644
index 0000000..d4de6da
Binary files /dev/null and b/src/lib/python/isc/datasrc/tests/testdata/example.com.source.sqlite3 differ
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 9c71cb5..8ac72f4 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/zone_loader_test.py b/src/lib/python/isc/datasrc/tests/zone_loader_test.py
new file mode 100644
index 0000000..cb239ed
--- /dev/null
+++ b/src/lib/python/isc/datasrc/tests/zone_loader_test.py
@@ -0,0 +1,223 @@
+# 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.
+
+import isc.datasrc
+import isc.dns
+
+import os
+import unittest
+import shutil
+import sys
+
+# Constants and common data used in tests
+
+TESTDATA_PATH = os.environ['TESTDATA_PATH']
+TESTDATA_WRITE_PATH = os.environ['TESTDATA_WRITE_PATH']
+
+ZONE_FILE = TESTDATA_PATH + '/example.com'
+STATIC_ZONE_FILE = '../../../../datasrc/static.zone'
+SOURCE_DB_FILE = TESTDATA_PATH + '/example.com.source.sqlite3'
+ORIG_DB_FILE = TESTDATA_PATH + '/example.com.sqlite3'
+DB_FILE = TESTDATA_WRITE_PATH + '/zoneloadertest.sqlite3'
+DB_CLIENT_CONFIG = '{ "database_file": "' + DB_FILE + '" }'
+DB_SOURCE_CLIENT_CONFIG = '{ "database_file": "' + SOURCE_DB_FILE + '" }'
+
+ORIG_SOA_TXT = 'example.com. 3600 IN SOA master.example.com. ' +\
+               'admin.example.com. 1234 3600 1800 2419200 7200\n'
+NEW_SOA_TXT = 'example.com. 1000 IN SOA a.dns.example.com. ' +\
+              'mail.example.com. 1 1 1 1 1\n'
+
+
+class ZoneLoaderTests(unittest.TestCase):
+    def setUp(self):
+        self.test_name = isc.dns.Name("example.com")
+        self.test_file = ZONE_FILE
+        self.client = isc.datasrc.DataSourceClient("sqlite3", DB_CLIENT_CONFIG)
+        # Make a fresh copy of the database
+        shutil.copyfile(ORIG_DB_FILE, DB_FILE)
+        # Some tests set source client; if so, check refcount in
+        # tearDown, since most tests don't, set it to None by default.
+        self.source_client = None
+        self.loader = None
+        self.assertEqual(2, sys.getrefcount(self.test_name))
+        self.assertEqual(2, sys.getrefcount(self.client))
+
+    def tearDown(self):
+        # We can only create 1 loader at a time (it locks the db), and it
+        # may not be destroyed immediately if there is an exception in a
+        # test. So the tests that do create one should put it in self, and
+        # we make sure to invalidate it here.
+
+        # We can also use this to check reference counts; if a loader
+        # exists, the client and source client (if any) should have
+        # an increased reference count (but the name should not, this
+        # is only used in the initializer)
+        if self.loader is not None:
+            self.assertEqual(2, sys.getrefcount(self.test_name))
+            self.assertEqual(3, sys.getrefcount(self.client))
+            if self.source_client is not None:
+                self.assertEqual(3, sys.getrefcount(self.source_client))
+        self.loader = None
+
+        # Now that the loader has been destroyed, the refcounts
+        # of its arguments should be back to their originals
+        self.assertEqual(2, sys.getrefcount(self.test_name))
+        self.assertEqual(2, sys.getrefcount(self.client))
+        if self.source_client is not None:
+            self.assertEqual(2, sys.getrefcount(self.source_client))
+
+    def test_bad_constructor(self):
+        self.assertRaises(TypeError, isc.datasrc.ZoneLoader)
+        self.assertRaises(TypeError, isc.datasrc.ZoneLoader, 1)
+        self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
+                          None, self.test_name, self.test_file)
+        self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
+                          self.client, None, self.test_file)
+        self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
+                          self.client, self.test_name, None)
+        self.assertRaises(TypeError, isc.datasrc.ZoneLoader,
+                          self.client, self.test_name, self.test_file, 1)
+
+    def check_zone_soa(self, soa_txt):
+        """
+        Check that the given SOA RR exists and matches the expected string
+        """
+        result, finder = self.client.find_zone(self.test_name)
+        self.assertEqual(self.client.SUCCESS, result)
+        result, rrset, _ = finder.find(self.test_name, isc.dns.RRType.SOA())
+        self.assertEqual(finder.SUCCESS, result)
+        self.assertEqual(soa_txt, rrset.to_text())
+
+    def check_load(self):
+        self.check_zone_soa(ORIG_SOA_TXT)
+        self.loader.load()
+        self.check_zone_soa(NEW_SOA_TXT)
+
+        # And after that, it should throw
+        self.assertRaises(isc.dns.InvalidOperation, self.loader.load)
+
+    def test_load_from_file(self):
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             self.test_file)
+        self.check_load()
+
+    def test_load_from_client(self):
+        self.source_client = isc.datasrc.DataSourceClient('sqlite3',
+                                    DB_SOURCE_CLIENT_CONFIG)
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             self.source_client)
+        self.check_load()
+
+    def check_load_incremental(self):
+        # New zone has 8 RRs
+        # After 5, it should return False
+        self.assertFalse(self.loader.load_incremental(5))
+        # New zone should not have been loaded yet
+        self.check_zone_soa(ORIG_SOA_TXT)
+
+        # After 5 more, it should return True (only having read 3)
+        self.assertTrue(self.loader.load_incremental(5))
+        # New zone should now be loaded
+        self.check_zone_soa(NEW_SOA_TXT)
+
+        # And after that, it should throw
+        self.assertRaises(isc.dns.InvalidOperation,
+                          self.loader.load_incremental, 5)
+
+    def test_load_from_file_incremental(self):
+        # Create loader and load the zone
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             self.test_file)
+        self.check_load_incremental()
+
+    def test_load_from_client_incremental(self):
+        self.source_client = isc.datasrc.DataSourceClient('sqlite3',
+                                            DB_SOURCE_CLIENT_CONFIG)
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             self.source_client)
+        self.check_load_incremental()
+
+    def test_bad_file(self):
+        self.check_zone_soa(ORIG_SOA_TXT)
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             'no such file')
+        self.assertRaises(isc.datasrc.MasterFileError, self.loader.load)
+        self.check_zone_soa(ORIG_SOA_TXT)
+
+    def test_bad_file_incremental(self):
+        self.check_zone_soa(ORIG_SOA_TXT)
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             'no such file')
+        self.assertRaises(isc.datasrc.MasterFileError,
+                          self.loader.load_incremental, 1)
+        self.check_zone_soa(ORIG_SOA_TXT)
+
+    def test_no_such_zone_in_target(self):
+        self.assertRaises(isc.datasrc.Error, isc.datasrc.ZoneLoader,
+                          self.client, isc.dns.Name("unknownzone"),
+                          self.test_file)
+
+    def test_no_such_zone_in_source(self):
+        # Reuse a zone that exists in target but not in source
+        zone_name = isc.dns.Name("sql1.example.com")
+        self.source_client = isc.datasrc.DataSourceClient('sqlite3',
+                                            DB_SOURCE_CLIENT_CONFIG)
+
+        # make sure the zone exists in the target
+        found, _ = self.client.find_zone(zone_name)
+        self.assertEqual(self.client.SUCCESS, found)
+        # And that it does not in the source
+        found, _ = self.source_client.find_zone(zone_name)
+        self.assertNotEqual(self.source_client.SUCCESS, found)
+
+        self.assertRaises(isc.datasrc.Error, isc.datasrc.ZoneLoader,
+                          self.client, zone_name, self.source_client)
+
+    def test_no_ds_load_support(self):
+        # This may change in the future, but atm, the in-mem ds does
+        # not support the API the zone loader uses (it has direct load calls)
+        inmem_client = isc.datasrc.DataSourceClient('memory',
+                                                    '{ "type": "memory" }');
+        self.assertRaises(isc.datasrc.NotImplemented,
+                          isc.datasrc.ZoneLoader,
+                          inmem_client, self.test_name, self.test_file)
+
+    def test_wrong_class_from_file(self):
+        # If the file has wrong class, it is not detected until load time
+        self.loader = isc.datasrc.ZoneLoader(self.client, self.test_name,
+                                             self.test_file + '.ch')
+        self.assertRaises(isc.datasrc.MasterFileError, self.loader.load)
+
+    def test_wrong_class_from_client(self):
+        # For ds->ds loading, wrong class is detected upon construction
+        # Need a bit of the extended setup for CH source client
+        clientlist = isc.datasrc.ConfigurableClientList(isc.dns.RRClass.CH())
+        clientlist.configure('[ { "type": "static", "params": "' +
+                             STATIC_ZONE_FILE +'" } ]', False)
+        self.source_client, _, _ = clientlist.find(isc.dns.Name("bind."),
+                                                   False, False)
+        self.assertRaises(isc.dns.InvalidParameter, isc.datasrc.ZoneLoader,
+                          self.client, isc.dns.Name("bind."),
+                          self.source_client)
+
+    def test_exception(self):
+        # Just check if masterfileerror is subclass of datasrc.Error
+        self.assertTrue(issubclass(isc.datasrc.MasterFileError,
+                                   isc.datasrc.Error))
+
+if __name__ == "__main__":
+    isc.log.init("bind10")
+    isc.log.resetUnitTestRootLogger()
+    unittest.main()
diff --git a/src/lib/python/isc/datasrc/zone_loader_inc.cc b/src/lib/python/isc/datasrc/zone_loader_inc.cc
new file mode 100644
index 0000000..405ad1a
--- /dev/null
+++ b/src/lib/python/isc/datasrc/zone_loader_inc.cc
@@ -0,0 +1,116 @@
+namespace {
+const char* const ZoneLoader_doc = "\
+Class to load data into a data source client.\n\
+\n\
+This is a small wrapper class that is able to load data into a data\n\
+source. It can load either from another data source or from a master\n\
+file. The purpose of the class is only to hold the state for\n\
+incremental loading.\n\
+\n\
+The old content of zone is discarded and no journal is stored.\n\
+\n\
+ZoneLoader(destination, zone_name, master_file)\n\
+\n\
+    Constructor from master file.\n\
+\n\
+    This initializes the zone loader to load from a master file.\n\
+\n\
+    Exceptions:\n\
+      DataSourceError in case the zone does not exist in destination.\n\
+                 This class does not support creating brand new zones,\n\
+                 only loading data into them. In case a new zone is\n\
+                 needed, it must be created beforehand.\n\
+      DataSourceError in case of other (possibly low-level) errors,\n\
+                 such as read-only data source or database error.\n\
+\n\
+    Parameters:\n\
+      destination (isc.datasrc.DataSourceClient) The data source into\n\
+                  which the loaded data should go.\n\
+      zone_name   (isc.dns.Name) The origin of the zone. The class is\n\
+                  implicit in the destination.\n\
+      master_file (string) Path to the master file to read data from.\n\
+\n\
+ZoneLoader(destination, zone_name, source)\n\
+\n\
+    Constructor from another data source.\n\
+\n\
+    Parameters:\n\
+      destination (isc.datasrc.DataSourceClient) The data source into\n\
+                  which the loaded data should go.\n\
+      zone_name   (isc.dns.Name) The origin of the zone. The class is\n\
+                  implicit in the destination.\n\
+      source      (isc.datasrc.DataSourceClient) The data source from\n\
+                  which the data would be read.\n\
+\n\
+    Exceptions:\n\
+      InvalidParameter in case the class of destination and source\n\
+                 differs.\n\
+      NotImplemented in case the source data source client doesn't\n\
+                 provide an iterator.\n\
+      DataSourceError in case the zone does not exist in destination.\n\
+                 This class does not support creating brand new zones,\n\
+                 only loading data into them. In case a new zone is\n\
+                 needed, it must be created beforehand.\n\
+      DataSourceError in case the zone does not exist in the source.\n\
+      DataSourceError in case of other (possibly low-level) errors,\n\
+                 such as read-only data source or database error.\n\
+\n\
+    Parameters:\n\
+      destination The data source into which the loaded data should\n\
+                 go.\n\
+      zone_name  The origin of the zone.\n\
+      source     The data source from which the data would be read.\n\
+\n\
+";
+
+const char* const ZoneLoader_load_doc = "\
+load() -> None\n\
+\n\
+Perform the whole load.\n\
+\n\
+This performs the whole loading operation. It may take a long time.\n\
+\n\
+Exceptions:\n\
+  InvalidOperation in case the loading was already completed before\n\
+             this call.\n\
+  DataSourceError in case some error (possibly low-level) happens.\n\
+  MasterFileError when the master_file is badly formatted or some\n\
+             similar problem is found when loading the master file.\n\
+\n\
+";
+
+const char* const ZoneLoader_loadIncremental_doc = "\
+load_incremental(limit) -> bool\n\
+\n\
+Load up to limit RRs.\n\
+\n\
+This performs a part of the loading. In case there's enough data in\n\
+the source, it copies limit RRs. It can copy less RRs during the final\n\
+call (when there's less than limit left).\n\
+\n\
+This can be called repeatedly until the whole zone is loaded, having\n\
+pauses in the loading for some purposes (for example reporting\n\
+progress).\n\
+\n\
+Exceptions:\n\
+  InvalidOperation in case the loading was already completed before\n\
+             this call (by load() or by a load_incremental that\n\
+             returned true).\n\
+  DataSourceError in case some error (possibly low-level) happens.\n\
+  MasterFileError when the master_file is badly formatted or some\n\
+             similar problem is found when loading the master file.\n\
+\n\
+Parameters:\n\
+  limit      (integer) The maximum allowed number of RRs to be\n\
+             loaded during this call.\n\
+\n\
+Return Value(s): True in case the loading is completed, false if\n\
+there's more to load.\n\
+\n\
+Note that if the limit is exactly the number of RRs available to be\n\
+loaded, the method will still return False, and True will be returned\n\
+on the next call (which will load 0 RRs). This is because the end of\n\
+iterator or master file is detected when reading past the end, not\n\
+when the last one is read.\n\
+";
+} // unnamed namespace
diff --git a/src/lib/python/isc/datasrc/zone_loader_python.cc b/src/lib/python/isc/datasrc/zone_loader_python.cc
new file mode 100644
index 0000000..98264b3
--- /dev/null
+++ b/src/lib/python/isc/datasrc/zone_loader_python.cc
@@ -0,0 +1,262 @@
+// 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.
+
+// Enable this if you use s# variants with PyArg_ParseTuple(), see
+// http://docs.python.org/py3k/c-api/arg.html#strings-and-buffers
+//#define PY_SSIZE_T_CLEAN
+
+// Python.h needs to be placed at the head of the program file, see:
+// http://docs.python.org/py3k/extending/extending.html#a-simple-example
+#include <Python.h>
+
+#include <util/python/pycppwrapper_util.h>
+
+#include <datasrc/zone_loader.h>
+#include <dns/python/name_python.h>
+#include <dns/python/pydnspp_common.h>
+#include <exceptions/exceptions.h>
+
+#include "client_python.h"
+#include "datasrc.h"
+#include "zone_loader_inc.cc"
+
+using namespace std;
+using namespace isc::dns::python;
+using namespace isc::datasrc;
+using namespace isc::datasrc::python;
+using namespace isc::util::python;
+
+namespace {
+// The s_* Class simply covers one instantiation of the object
+class s_ZoneLoader : public PyObject {
+public:
+    s_ZoneLoader() : cppobj(NULL), target_client(NULL), source_client(NULL)
+        {};
+    ZoneLoader* cppobj;
+    // a zoneloader should not survive its associated client(s),
+    // so add a ref to it at init
+    PyObject* target_client;
+    PyObject* source_client;
+};
+
+// General creation and destruction
+int
+ZoneLoader_init(PyObject* po_self, PyObject* args, PyObject*) {
+    s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+    PyObject *po_target_client = NULL;
+    PyObject *po_source_client = NULL;
+    PyObject *po_name = NULL;
+    char* master_file;
+    if (!PyArg_ParseTuple(args, "O!O!s", &datasourceclient_type,
+                          &po_target_client, &name_type, &po_name,
+                          &master_file) &&
+        !PyArg_ParseTuple(args, "O!O!O!", &datasourceclient_type,
+                          &po_target_client, &name_type, &po_name,
+                          &datasourceclient_type, &po_source_client)
+       ) {
+        PyErr_SetString(PyExc_TypeError,
+                        "Invalid arguments to ZoneLoader constructor, "
+                        "expects isc.datasrc.DataSourceClient, isc.dns.Name, "
+                        "and either a string or another DataSourceClient");
+        return (-1);
+    }
+    PyErr_Clear();
+    try {
+        // The associated objects must be alive during the lifetime
+        // of this instance, so incref them (through a container in case
+        // of exceptions in this method)
+        Py_INCREF(po_target_client);
+        PyObjectContainer target_client(po_target_client);
+        if (po_source_client != NULL) {
+            // See above
+            Py_INCREF(po_source_client);
+            PyObjectContainer source_client(po_source_client);
+            self->cppobj = new ZoneLoader(
+                PyDataSourceClient_ToDataSourceClient(po_target_client),
+                PyName_ToName(po_name),
+                PyDataSourceClient_ToDataSourceClient(po_source_client));
+            self->source_client = source_client.release();
+        } else {
+            self->cppobj = new ZoneLoader(
+                PyDataSourceClient_ToDataSourceClient(po_target_client),
+                PyName_ToName(po_name),
+                master_file);
+        }
+        self->target_client = target_client.release();
+        return (0);
+    } catch (const isc::InvalidParameter& ivp) {
+        PyErr_SetString(po_InvalidParameter, ivp.what());
+    } catch (const isc::datasrc::DataSourceError& dse) {
+        PyErr_SetString(getDataSourceException("Error"), dse.what());
+    } catch (const isc::NotImplemented& ni) {
+        PyErr_SetString(getDataSourceException("NotImplemented"), ni.what());
+    } catch (const std::exception& stde) {
+        PyErr_SetString(getDataSourceException("Error"), stde.what());
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+    }
+    return (-1);
+}
+
+void
+ZoneLoader_destroy(PyObject* po_self) {
+    s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+    delete self->cppobj;
+    self->cppobj = NULL;
+    if (self->target_client != NULL) {
+        Py_DECREF(self->target_client);
+    }
+    if (self->source_client != NULL) {
+        Py_DECREF(self->source_client);
+    }
+    Py_TYPE(self)->tp_free(self);
+}
+
+PyObject*
+ZoneLoader_load(PyObject* po_self, PyObject*) {
+    s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+    try {
+        self->cppobj->load();
+        Py_RETURN_NONE;
+    } catch (const isc::InvalidOperation& ivo) {
+        PyErr_SetString(po_InvalidOperation, ivo.what());
+        return (NULL);
+    } catch (const isc::datasrc::MasterFileError& mfe) {
+        PyErr_SetString(getDataSourceException("MasterFileError"), mfe.what());
+        return (NULL);
+    } catch (const isc::datasrc::DataSourceError& dse) {
+        PyErr_SetString(getDataSourceException("Error"), dse.what());
+        return (NULL);
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+ZoneLoader_loadIncremental(PyObject* po_self, PyObject* args) {
+    s_ZoneLoader* self = static_cast<s_ZoneLoader*>(po_self);
+
+    int limit;
+    if (!PyArg_ParseTuple(args, "i", &limit)) {
+        return (NULL);
+    }
+    if (limit < 0) {
+        PyErr_SetString(PyExc_ValueError,
+                        "load_incremental argument must be positive");
+        return (NULL);
+    }
+    try {
+        if (self->cppobj->loadIncremental(limit)) {
+            Py_RETURN_TRUE;
+        } else {
+            Py_RETURN_FALSE;
+        }
+    } catch (const isc::InvalidOperation& ivo) {
+        PyErr_SetString(po_InvalidOperation, ivo.what());
+        return (NULL);
+    } catch (const isc::datasrc::MasterFileError& mfe) {
+        PyErr_SetString(getDataSourceException("MasterFileError"), mfe.what());
+        return (NULL);
+    } catch (const isc::datasrc::DataSourceError& dse) {
+        PyErr_SetString(getDataSourceException("Error"), dse.what());
+        return (NULL);
+    } catch (const std::exception& exc) {
+        PyErr_SetString(getDataSourceException("Error"), exc.what());
+        return (NULL);
+    } catch (...) {
+        PyErr_SetString(getDataSourceException("Error"),
+                        "Unexpected exception");
+        return (NULL);
+    }
+}
+
+// This list contains the actual set of functions we have in
+// python. Each entry has
+// 1. Python method name
+// 2. Our static function here
+// 3. Argument type
+// 4. Documentation
+PyMethodDef ZoneLoader_methods[] = {
+    { "load", ZoneLoader_load, METH_NOARGS, ZoneLoader_load_doc },
+    { "load_incremental", ZoneLoader_loadIncremental, METH_VARARGS,
+      ZoneLoader_loadIncremental_doc },
+    { NULL, NULL, 0, NULL }
+};
+
+} // end of unnamed namespace
+
+namespace isc {
+namespace datasrc {
+namespace python {
+
+PyTypeObject zone_loader_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "datasrc.ZoneLoader",
+    sizeof(s_ZoneLoader),               // tp_basicsize
+    0,                                  // tp_itemsize
+    ZoneLoader_destroy,                 // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    ZoneLoader_doc,
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    ZoneLoader_methods,                 // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    ZoneLoader_init,                    // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+
diff --git a/src/lib/python/isc/datasrc/zone_loader_python.h b/src/lib/python/isc/datasrc/zone_loader_python.h
new file mode 100644
index 0000000..f148efa
--- /dev/null
+++ b/src/lib/python/isc/datasrc/zone_loader_python.h
@@ -0,0 +1,34 @@
+// 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 PYTHON_DATASRC_ZONE_LOADER_H
+#define PYTHON_DATASRC_ZONE_LOADER_H 1
+
+#include <Python.h>
+
+namespace isc {
+namespace datasrc {
+
+namespace python {
+
+extern PyTypeObject zone_loader_type;
+
+} // namespace python
+} // namespace datasrc
+} // namespace isc
+#endif // PYTHON_DATASRC_ZONE_LOADER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/python/isc/ddns/libddns_messages.mes b/src/lib/python/isc/ddns/libddns_messages.mes
index 7e34e70..406151c 100644
--- a/src/lib/python/isc/ddns/libddns_messages.mes
+++ b/src/lib/python/isc/ddns/libddns_messages.mes
@@ -122,7 +122,7 @@ as the class in the Zone Section, ANY, or NONE.
 A FORMERR response is sent back to the client.
 
 % LIBDDNS_UPDATE_DATASRC_ERROR error in datasource during DDNS update: %1
-An error occured while committing the DDNS update changes to the
+An error occurred while committing the DDNS update changes to the
 datasource. The specific error is printed. A SERVFAIL response is sent
 back to the client.
 
diff --git a/src/lib/python/isc/log_messages/Makefile.am b/src/lib/python/isc/log_messages/Makefile.am
index 6d23df3..97ff6e6 100644
--- a/src/lib/python/isc/log_messages/Makefile.am
+++ b/src/lib/python/isc/log_messages/Makefile.am
@@ -14,8 +14,10 @@ EXTRA_DIST += config_messages.py
 EXTRA_DIST += notify_out_messages.py
 EXTRA_DIST += libddns_messages.py
 EXTRA_DIST += libxfrin_messages.py
+EXTRA_DIST += loadzone_messages.py
 EXTRA_DIST += server_common_messages.py
 EXTRA_DIST += dbutil_messages.py
+EXTRA_DIST += msgq_messages.py
 
 CLEANFILES = __init__.pyc
 CLEANFILES += bind10_messages.pyc
@@ -31,8 +33,10 @@ CLEANFILES += config_messages.pyc
 CLEANFILES += notify_out_messages.pyc
 CLEANFILES += libddns_messages.pyc
 CLEANFILES += libxfrin_messages.pyc
+CLEANFILES += loadzone_messages.pyc
 CLEANFILES += server_common_messages.pyc
 CLEANFILES += dbutil_messages.pyc
+CLEANFILES += msgq_messages.pyc
 
 CLEANDIRS = __pycache__
 
diff --git a/src/lib/python/isc/log_messages/loadzone_messages.py b/src/lib/python/isc/log_messages/loadzone_messages.py
new file mode 100644
index 0000000..2374900
--- /dev/null
+++ b/src/lib/python/isc/log_messages/loadzone_messages.py
@@ -0,0 +1 @@
+from work.loadzone_messages import *
diff --git a/src/lib/python/isc/log_messages/msgq_messages.py b/src/lib/python/isc/log_messages/msgq_messages.py
new file mode 100644
index 0000000..81efa41
--- /dev/null
+++ b/src/lib/python/isc/log_messages/msgq_messages.py
@@ -0,0 +1 @@
+from work.msgq_messages import *
diff --git a/src/lib/resolve/resolve_messages.mes b/src/lib/resolve/resolve_messages.mes
index 1df544a..6447082 100644
--- a/src/lib/resolve/resolve_messages.mes
+++ b/src/lib/resolve/resolve_messages.mes
@@ -86,7 +86,7 @@ SERVFAIL will be returned.
 
 % RESLIB_NOTSINGLE_RESPONSE response to query for <%1> was not a response
 A debug message, the response to the specified query from an upstream
-nameserver was a CNAME that had mutiple RRs in the RRset.  This is
+nameserver was a CNAME that had multiple RRs in the RRset.  This is
 an invalid response according to the standards so a SERVFAIL will be
 returned to the system making the original query.
 
@@ -138,7 +138,7 @@ A debug message, the response to the specified query indicated an error
 that is not covered by a specific code path.  A SERVFAIL will be returned.
 
 % RESLIB_RECQ_CACHE_FIND found <%1> in the cache (resolve() instance %2)
-This is a debug message and indicates that a RecursiveQuery object found the
+This is a debug message and indicates that a RecursiveQuery object found
 the specified <name, class, type> tuple in the cache.  The instance number
 at the end of the message indicates which of the two resolve() methods has
 been called.
@@ -178,7 +178,7 @@ A debug message giving the round-trip time of the last query and response.
 This is a debug message and indicates that a RunningQuery object found
 the specified <name, class, type> tuple in the cache.
 
-% RESLIB_RUNQ_CACHE_LOOKUP looking up up <%1> in the cache
+% RESLIB_RUNQ_CACHE_LOOKUP looking up <%1> in the cache
 This is a debug message and indicates that a RunningQuery object has made
 a call to its doLookup() method to look up the specified <name, class, type>
 tuple, the first action of which will be to examine the cache.
diff --git a/src/lib/server_common/server_common_messages.mes b/src/lib/server_common/server_common_messages.mes
index 0e4efa5..22ce0f3 100644
--- a/src/lib/server_common/server_common_messages.mes
+++ b/src/lib/server_common/server_common_messages.mes
@@ -17,11 +17,11 @@ $NAMESPACE isc::server_common
 # \brief Messages for the server_common library
 
 % SOCKETREQUESTOR_CREATED Socket requestor created for application %1
-Debug message.  A socket requesor (client of the socket creator) is created
+Debug message.  A socket requestor (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.
 
-% SOCKETREQUESTOR_DESTROYED Socket requestor destoryed
+% SOCKETREQUESTOR_DESTROYED Socket requestor destroyed
 Debug message.  The socket requestor created at SOCKETREQUESTOR_CREATED
 has been destroyed.  This event is generally unexpected other than in
 test cases.
diff --git a/src/lib/testutils/dnsmessage_test.h b/src/lib/testutils/dnsmessage_test.h
index 262d177..6de01ec 100644
--- a/src/lib/testutils/dnsmessage_test.h
+++ b/src/lib/testutils/dnsmessage_test.h
@@ -208,10 +208,9 @@ pullSigs(std::vector<isc::dns::ConstRRsetPtr>& rrsets,
 {
     for (ITERATOR it = begin; it != end; ++it) {
         rrsets.push_back(*it);
-        text += (*it)->toText();
+        text += (*it)->toText(); // this will include RRSIG, if attached.
         if ((*it)->getRRsig()) {
             rrsets.push_back((*it)->getRRsig());
-            text += (*it)->getRRsig()->toText();
         }
     }
 }
diff --git a/src/lib/util/encode/base_n.cc b/src/lib/util/encode/base_n.cc
index 0026a0b..c38f901 100644
--- a/src/lib/util/encode/base_n.cc
+++ b/src/lib/util/encode/base_n.cc
@@ -291,7 +291,8 @@ BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
                 isc_throw(BadValue, "Too many " << algorithm
                           << " padding characters: " << input);
             }
-        } else if (ch < 0 || !isspace(ch)) {
+        } else if (!(ch > 0 && isspace(ch))) {
+            // see the note for DecodeNormalizer::skipSpaces() above for ch > 0
             break;
         }
         ++srit;
diff --git a/tests/system/bindctl/setup.sh b/tests/system/bindctl/setup.sh
index 55afcc1..7a9a323 100755
--- a/tests/system/bindctl/setup.sh
+++ b/tests/system/bindctl/setup.sh
@@ -22,5 +22,5 @@ SUBTEST_TOP=${TEST_TOP}/bindctl
 cp ${SUBTEST_TOP}/nsx1/b10-config.db.template ${SUBTEST_TOP}/nsx1/b10-config.db
 
 rm -f ${SUBTEST_TOP}/*/zone.sqlite3
-${B10_LOADZONE} -o . -d ${SUBTEST_TOP}/nsx1/zone.sqlite3 \
-	${SUBTEST_TOP}//nsx1/root.db
+${B10_LOADZONE} -i 1 -c "{\"database_file\": \"${SUBTEST_TOP}/nsx1/zone.sqlite3\"}" \
+	. ${SUBTEST_TOP}//nsx1/root.db
diff --git a/tests/system/bindctl/tests.sh b/tests/system/bindctl/tests.sh
index 9611c90..75c91de 100755
--- a/tests/system/bindctl/tests.sh
+++ b/tests/system/bindctl/tests.sh
@@ -85,6 +85,7 @@ config commit
 quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
+sleep 2
 $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
@@ -118,6 +119,7 @@ config commit
 quit
 " | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR > bindctl.out.$n || status=1
+sleep 2
 $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
@@ -148,6 +150,7 @@ quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 done
+sleep 2
 $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
@@ -183,6 +186,7 @@ quit
 ' | $RUN_BINDCTL \
 	--csv-file-dir=$BINDCTL_CSV_DIR 2>&1 > /dev/null || status=1
 done
+sleep 2
 $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
diff --git a/tests/system/conf.sh.in b/tests/system/conf.sh.in
index 92f72fa..4948d63 100755
--- a/tests/system/conf.sh.in
+++ b/tests/system/conf.sh.in
@@ -51,7 +51,10 @@ export RNDC=$BIND9_TOP/bin/rndc/rndc
 export TESTSOCK=$BIND9_TOP/bin/tests/system/testsock.pl
 export DIGCOMP=$BIND9_TOP/bin/tests/system/digcomp.pl
 
-export SUBDIRS="bindctl glue ixfr/in-2"
+# bindctl test doesn't work right now and is disabled (see #2568)
+#export SUBDIRS="bindctl glue ixfr/in-2"
+export SUBDIRS="glue ixfr/in-2"
+
 # Add appropriate subdirectories to the above statement as the tests become
 # available.
 #SUBDIRS="dnssec masterfile ixfr/in-1 ixfr/in-2 ixfr/in-4"
diff --git a/tests/system/glue/setup.sh.in b/tests/system/glue/setup.sh.in
index dc5b28a..2f9f886 100755
--- a/tests/system/glue/setup.sh.in
+++ b/tests/system/glue/setup.sh.in
@@ -18,8 +18,8 @@ SYSTEMTESTTOP=..
 . $SYSTEMTESTTOP/conf.sh
 
 rm -f */zone.sqlite3
-${B10_LOADZONE} -o . -d @builddir@/nsx1/zone.sqlite3 @builddir@/nsx1/root.db
-${B10_LOADZONE} -o root-servers.nil -d @builddir@/nsx1/zone.sqlite3 \
+${B10_LOADZONE} -i 1 -c '{"database_file": "@builddir@/nsx1/zone.sqlite3"}' . @builddir@/nsx1/root.db
+${B10_LOADZONE} -i 1 -c '{"database_file": "@builddir@/nsx1/zone.sqlite3"}' root-servers.nil \
 	@builddir@/nsx1/root-servers.nil.db
-${B10_LOADZONE} -o com -d @builddir@/nsx1/zone.sqlite3 @builddir@/nsx1/com.db
-${B10_LOADZONE} -o net -d @builddir@/nsx1/zone.sqlite3 @builddir@/nsx1/net.db
+${B10_LOADZONE} -i 1 -c '{"database_file": "@builddir@/nsx1/zone.sqlite3"}' com @builddir@/nsx1/com.db
+${B10_LOADZONE} -i 1 -c '{"database_file": "@builddir@/nsx1/zone.sqlite3"}' net @builddir@/nsx1/net.db
diff --git a/tests/system/ixfr/in-1/setup.sh.in b/tests/system/ixfr/in-1/setup.sh.in
index d4c3978..4332930 100644
--- a/tests/system/ixfr/in-1/setup.sh.in
+++ b/tests/system/ixfr/in-1/setup.sh.in
@@ -27,4 +27,4 @@ cp -f $IXFR_TOP/db.example.n4 ns1/db.example
 
 # Set up the IXFR client - load the same version of the zone.
 cp -f $IXFR_TOP/b10-config.db nsx2/b10-config.db
-${B10_LOADZONE} -o . -d $IXFR_TOP/zone.sqlite3 $IXFR_TOP/db.example.n4
+${B10_LOADZONE} -c "{\"database_file\": \"$IXFR_TOP/zone.sqlite3\"}" example. $IXFR_TOP/db.example.n4
diff --git a/tests/system/ixfr/in-2/setup.sh.in b/tests/system/ixfr/in-2/setup.sh.in
index a5f64e5..a210636 100644
--- a/tests/system/ixfr/in-2/setup.sh.in
+++ b/tests/system/ixfr/in-2/setup.sh.in
@@ -26,4 +26,4 @@ cp -f $IXFR_TOP/db.example.n6 ns1/db.example
 
 # Set up the IXFR client - load an earlier version of the zone
 cp -f $IXFR_TOP/b10-config.db nsx2/b10-config.db
-${B10_LOADZONE} -o . -d $IXFR_TOP/zone.sqlite3 $IXFR_TOP/db.example.n6
+${B10_LOADZONE} -c "{\"database_file\": \"$IXFR_TOP/zone.sqlite3\"}" example. $IXFR_TOP/db.example.n6
diff --git a/tests/system/ixfr/in-3/setup.sh.in b/tests/system/ixfr/in-3/setup.sh.in
index 867e06e..2a08c58 100644
--- a/tests/system/ixfr/in-3/setup.sh.in
+++ b/tests/system/ixfr/in-3/setup.sh.in
@@ -26,4 +26,4 @@ cp -f $IXFR_TOP/db.example.n0 ns1/db.example
 
 # Set up the IXFR client - load a previous version of the zone.
 cp -f $IXFR_TOP/b10-config.db nsx2/b10-config.db
-${B10_LOADZONE} -o . -d $IXFR_TOP/zone.sqlite3 $IXFR_TOP/db.example.n2
+${B10_LOADZONE} -c "{\"database_file\": \"$IXFR_TOP/zone.sqlite3\"}" example. $IXFR_TOP/db.example.n2
diff --git a/tests/system/ixfr/in-4/setup.sh.in b/tests/system/ixfr/in-4/setup.sh.in
index 7419e27..1c2e9c8 100644
--- a/tests/system/ixfr/in-4/setup.sh.in
+++ b/tests/system/ixfr/in-4/setup.sh.in
@@ -27,4 +27,4 @@ cp -f $IXFR_TOP/db.example.n2.refresh ns1/db.example
 # Set up the IXFR client - load a previous version of the zone with a short
 # refresh time.
 cp -f $IXFR_TOP/b10-config.db nsx2/b10-config.db
-${B10_LOADZONE} -o . -d $IXFR_TOP/zone.sqlite3 $IXFR_TOP/db.example.n2.refresh
+${B10_LOADZONE} -c "{\"database_file\": \"$IXFR_TOP/zone.sqlite3\"}" example. $IXFR_TOP/db.example.n2.refresh
diff --git a/tests/tools/perfdhcp/pkt_transform.cc b/tests/tools/perfdhcp/pkt_transform.cc
index b1c64e2..15f15f1 100644
--- a/tests/tools/perfdhcp/pkt_transform.cc
+++ b/tests/tools/perfdhcp/pkt_transform.cc
@@ -196,12 +196,10 @@ PktTransform::unpackOptions(const OptionBuffer& in_buffer,
 
         // Get option length which is supposed to be after option type.
         offset += offset_step;
-        uint16_t opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
-        if (option->getUniverse() == Option::V6) {
-            opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
-        } else {
-            opt_len = in_buffer[offset];
-        }
+        const uint16_t opt_len =
+            (option->getUniverse() == Option::V6) ?
+            in_buffer[offset] * 256 + in_buffer[offset + 1] :
+            in_buffer[offset];
 
         // Check if packet is not truncated.
         if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
diff --git a/tests/tools/perfdhcp/tests/.gitignore b/tests/tools/perfdhcp/tests/.gitignore
index d6d1ec8..5697f95 100644
--- a/tests/tools/perfdhcp/tests/.gitignore
+++ b/tests/tools/perfdhcp/tests/.gitignore
@@ -1 +1,6 @@
 /run_unittests
+/test1.hex
+/test2.hex
+/test3.hex
+/test4.hex
+/test5.hex



More information about the bind10-changes mailing list